掃描 GraphQL API 以查找漏洞
自2020年6月以來,Acunetix支持日益流行的API查詢語言– GraphQL。在本文中,想逐步向您展示如何掃描使用GraphQL定義的API。為此,您將首先創建一個故意易受攻擊的API及其GraphQL定義,然后使用Acunetix對其進行掃描,消除使用Acunetix發現的嚴重漏洞,并確認已消除了這些漏洞。
階段1:設置測試環境
為了能夠進行此練習,在測試之前必須準備一個測試環境。在本練習中,我們將Windows操作系統與開源軟件一起使用。
- 使用PHP和MySQL將Wamp64安裝為本地Web服務器
- 將Windows路徑環境變量設置為指向您的PHP和MySQL可執行文件:
- 運行Windows可執行文件systempropertiesadvanced.exe
- 轉到高級選項卡
- 單擊環境變量
- 將您的php.exe文件夾和mysql.exe文件夾添加到路徑列表;要添加的典型值是:
- c:\ wamp64 \ bin \ php \ php7.3.21
- c:\ wamp64 \ bin \ mysql \ mysql5.7.31 \ bin
- 安裝適用于PHP的Composer依賴性管理器
階段2:建立簡單的GraphQL API
要使用Acunetix掃描GraphQL API,您將構建一個簡單的,易受攻擊的API。要構建API,您需要執行以下步驟:
- 在Web服務器上創建數據庫以存儲數據
- 創建一個Web服務根文件夾并安裝一些依賴庫
- 創建一個GraphQL模式文件
- 創建一個GraphQL模式加載器文件
- 創建索引文件以處理GraphQL請求
- 創建一個解析器文件
步驟1:在Web服務器上創建數據庫
要在Wamp64 Web服務器上設置數據庫,請執行以下操作
打開命令提示符
運行mysql -u root
在MySQL根目錄提示符下運行以下命令:
mysql> create user 'graphuser'@'localhost' identified by 'graphuserpass'; mysql> create database graphusersdb; mysql> GRANT ALL PRIVILEGES ON graphusersdb.* TO 'graphuser'@'localhost'; mysql> use graphusersdb; mysql> CREATE TABLE `users` (`id` int(11) NOT NULL AUTO_INCREMENT,`fname` varchar(255) NOT NULL, `lname` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, `notes` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `email` (`email`)); mysql> INSERT INTO users (fname, lname, email) VALUES ('John', 'Smith', 'john@example.com'); mysql> INSERT INTO users (fname, lname, email) VALUES ('Jane', 'Doe', 'jane@example.com');
步驟2:創建Web服務根文件夾
要創建Web服務根文件夾,請執行以下操作:
打開命令提示符
運行以下命令為您的項目創建文件夾,包括PHP庫依賴項:
C:\>mkdir C:\wamp64\www\graphusers C:\>cd C:\wamp64\www\graphusers C:\wamp64\www\graphusers>composer require leocavalcante/siler C:\wamp64\www\graphusers>composer require overblog/dataloader-php C:\wamp64\www\graphusers>composer require webonyx/graphql-php
步驟3:創建一個GraphQL模式文件
創建具有以下內容的C:\ wamp64 \ www \ graphusers \ schema.graphql文件:
type Query {
getUser(email: String): [User]
}
type Mutation {
addUser(fname: String, lname: String, email: String, notes: String): [User]
deluser(email: String): [User]
}
type User {
id: Int
fname: String
lname: String
email: String
notes: String
}
步驟4:創建GraphQL模式加載器文件
創建具有以下內容的C:\ wamp64 \ www \ graphusers \ schema.php文件:
<?php
use Siler\Graphql;
$typeDefs = file_get_contents(__DIR__.'/schema.graphql');
$resolvers = include __DIR__.'/resolvers.php';
return Graphql\schema($typeDefs, $resolvers);
步驟5:創建索引文件以處理GraphQL請求
創建具有以下內容的C:\ wamp64 \ www \ graphusers \ index.php文件:
<?php
require_once 'vendor/autoload.php';
use Siler\Graphql;
use GraphQL\GraphQL as WGraphql;
use Siler\Http\Request;
use Siler\Http\Response;
use Overblog\DataLoader\DataLoader;
use Overblog\DataLoader\Promise\Adapter\Webonyx\GraphQL\SyncPromiseAdapter;
use Overblog\PromiseAdapter\Adapter\WebonyxGraphQLSyncPromiseAdapter;
try {
$MyDB = new PDO("mysql:host=localhost;dbname=graphusersdb;charset=utf8mb4", "graphuser", "graphuserpass");
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
?function select_sql($query, $sql_args) {
global $MyDB;
$stmt = $MyDB->prepare($query);
$stmt->execute($sql_args);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $rows;
}
?function insert_sql($query, $sql_args) {
global $MyDB;
$stmt = $MyDB->prepare($query);
return $stmt->execute($sql_args); // true if successful; false if not successful
}
?function delete_sql($query, $sql_args) {
global $MyDB;
$stmt = $MyDB->prepare($query);
return $stmt->execute($sql_args); // true if successful; false if not successful
}
$graphQLSyncPromiseAdapter = new SyncPromiseAdapter();
$promiseAdapter = new WebonyxGraphQLSyncPromiseAdapter($graphQLSyncPromiseAdapter);
WGraphQL::setPromiseAdapter($graphQLSyncPromiseAdapter);
$context = [
'select_sql' => function ($query, $sql_args) { return select_sql($query, $sql_args); },
'insert_sql' => function ($query, $sql_args) { return insert_sql($query, $sql_args); },
'delete_sql' => function ($query, $sql_args) { return delete_sql($query, $sql_args); },
];
if (Request\method_is('post')) {
$schema = include __DIR__.'/schema.php';
Graphql\init($schema, null, $context);
}
步驟6:創建解析器文件
創建具有以下內容的C:\ wamp64 \ www \ graphusers \ resolvers.php文件:
<?php
return [
'Query' => [
getUser' => function($root, $args, $context) {
return $context['select_sql']("SELECT id, fname, lname, email, notes FROM users WHERE email=:email", ['email'=>$args['email']]);
}
],
'Mutation' => [
'addUser' => function($root, $args, $context) {
$dummy = $context['insert_sql']("INSERT INTO users (fname, lname, email, notes) VALUES ('" . $args['fname'] . "', '" . $args['lname'] . "', '" . $args['email'] . "', '" . $args['notes'] . "')", []);
if ($dummy) {
return $context['select_sql']("SELECT id, fname, lname, email, notes FROM users WHERE email=:email", ['email'=>$args['email']]);
}
return null;
},
'delUser' => function($root, $args, $context) {
$delrecord = $context['select_sql']("SELECT id, fname, lname, email, notes FROM users WHERE email=:email", ['email'=>$args['email']]);
$dummy = $context['delete_sql']("DELETE FROM users WHERE email=:email", ['email'=>$args['email']]);
if ($dummy) {
return $delrecord;
}
return null;
}
]
];
階段3:掃描您的GraphQL API
在此示例中,Web服務定義位于以下URL:http://localhost/graphusers/schema.graphql。要使用Acunetix掃描Web服務:
使用以下URL創建一個新目標:http:// localhost / graphusers /
將修改后的模式文件導入到目標:
從以下URL下載模式文件:http://localhost/graphusers/schema.graphql
在文件頂部插入一行以指定GraphQL端點;該文件應如下所示:
graphql_endpoint="/graphusers/"; type Query { getUser(email: String): [User] } type Mutation { addUser(fname: String, lname: String, email: String, notes: String): [User] deluser(email: String): [User] } type User { id: Int fname: String lname: String email: String notes: String }找到目標配置的“導入文件”部分,然后導入修改后的架構文件

將PHP AcuSensor部署到您的Web服務
啟動您的Web服務的完整掃描,然后等待其完成
階段4:識別GraphQL API中的漏洞
檢查要掃描的漏洞列表。在本練習中,我們將重點介紹SQL注入漏洞,因為它們都有相同的根本原因。

Acunetix顯示了攻擊詳細信息— GraphQL API調用已向變量注入了延遲命令,并且Acunetix能夠確認響應確實延遲了指定的秒數。這證實了該API調用容易受到SQL注入的攻擊,從而使惡意黑客可以制作其他請求以可能檢索大量數據。

階段5:解決漏洞
快速瀏覽**resolvers.php類文件中的addUser變異函數可以揭示根本原因。該查詢是使用字符串連接構建的:
$dummy = $context 'insert_sql' VALUES ('" . $args['fname'] . "', '" . $args['lname'] . "', '" . $args['email'] . "', '" . $args['notes'] . "')", []);
]
該的$ args [“FNAME”]變量和其他變量被簡單地連接起來無需任何驗證查詢字符串。我們可以使用參數化查詢來調整代碼。resolvers.php文件中新調整的行如下所示:
$dummy = $context'insert_sql' VALUES (:fname, :lname, :email, :notes)", ['fname'=>$args['fname'], 'lname'=>$args['lname'], 'email'=>$args['email'], 'notes'=>$args['notes']]);
階段6:重新掃描以確認分辨率

轉到掃描漏洞列表,然后選擇剛剛解決的SQL注入漏洞。
單擊“重新測試”按鈕-這將創建一個新掃描,以再次測試所選漏洞。結果將表明您已經成功解決了這些漏洞。