目錄
一、源碼分析
1、index.php
2、impossible.php
二、SQL注入防范分析
1、Anti-CSRF 令牌
2、參數化查詢
3、輸入驗證
本系列為通過《DVWA靶場通關筆記》的SQL Injection?關卡(low,medium,high,impossible共4關)滲透集合,通過對相應關卡源碼的代碼審計找到講解滲透原理并進行滲透實踐。本文為SQL Injection??impossible關卡的原理分析部分,講解相對于low、medium和high級別,為何對其進行滲透測試是Impossible的。
一、源碼分析
1、index.php
進入DVWA靶場SQL Injection源目錄,找到index.php源碼。
這段代碼實現了
這段 PHP 代碼是 Damn Vulnerable Web Application (DVWA) 中 SQL 注入攻擊演示頁面的主控制器,主要功能包括:
- 環境初始化:設置頁面路徑、驗證用戶身份、連接數據庫。
- 安全級別控制:根據用戶 Cookie 中的安全級別設置(低、中、高、不可能),加載不同級別的實現文件。這些文件包含不同防護級別的 SQL 查詢代碼,用于演示不同難度的 SQL 注入場景。
- 表單生成:根據安全級別動態生成不同的用戶輸入表單(低級、中級、高級、不可能共4個級別)
- 環境檢測:檢查 PHP 配置中的魔術引號和安全模式,提供環境安全提示。
- 結果展示:將 SQL 查詢結果和安全參考資料鏈接整合到頁面中。
經過注釋后的詳細代碼如下所示。
<?php// 定義網站根目錄路徑常量,并引入頁面處理工具
define( 'DVWA_WEB_PAGE_TO_ROOT', '../../' );
require_once DVWA_WEB_PAGE_TO_ROOT . 'dvwa/includes/dvwaPage.inc.php';// 初始化頁面,驗證用戶認證狀態并啟動PHPIDS防護模塊
dvwaPageStartup( array( 'authenticated', 'phpids' ) );// 創建新頁面實例并設置頁面元信息
$page = dvwaPageNewGrab();
$page[ 'title' ] = 'Vulnerability: SQL Injection' . $page[ 'title_separator' ].$page[ 'title' ];
$page[ 'page_id' ] = 'sqli';
$page[ 'help_button' ] = 'sqli';
$page[ 'source_button' ] = 'sqli';// 連接數據庫
dvwaDatabaseConnect();// 設置表單提交方式和級別文件
$method = 'GET';
$vulnerabilityFile = '';
// 根據安全級別Cookie選擇不同的級別實現文件
switch( $_COOKIE[ 'security' ] ) {case 'low':$vulnerabilityFile = 'low.php';break;case 'medium':$vulnerabilityFile = 'medium.php';$method = 'POST'; // 中等級別使用POST方法break;case 'high':$vulnerabilityFile = 'high.php';break;default:$vulnerabilityFile = 'impossible.php'; // 默認使用安全實現break;
}// 引入對應安全級別的SQL注入攻擊實現文件
require_once DVWA_WEB_PAGE_TO_ROOT . "vulnerabilities/sqli/source/{$vulnerabilityFile}";// 檢查PHP環境配置并生成警告信息
$WarningHtml = '';
// 檢測魔術引號是否開啟(已棄用的安全機制)
if( ini_get( 'magic_quotes_gpc' ) == true ) {$WarningHtml .= "<div class=\"warning\">The PHP function \"<em>Magic Quotes</em>\" is enabled.</div>";
}
// 檢測安全模式是否開啟(已棄用的安全機制)
if( ini_get( 'safe_mode' ) == true ) {$WarningHtml .= "<div class=\"warning\">The PHP function \"<em>Safe mode</em>\" is enabled.</div>";
}// 構建頁面主體內容
$page[ 'body' ] .= "
<div class=\"body_padded\"><h1>Vulnerability: SQL Injection</h1>{$WarningHtml}<div class=\"vulnerable_code_area\">";// 高級安全級別使用JavaScript彈窗獲取用戶ID
if( $vulnerabilityFile == 'high.php' ) {$page[ 'body' ] .= "Click <a href=\"#\" onclick=\"javascript:popUp('session-input.php');return false;\">here to change your ID</a>.";
}
// 其他安全級別使用表單獲取用戶ID
else {$page[ 'body' ] .= "<form action=\"#\" method=\"{$method}\"><p>User ID:";// 中等級別使用下拉菜單限制輸入范圍if( $vulnerabilityFile == 'medium.php' ) {$page[ 'body' ] .= "\n <select name=\"id\">";// 動態生成下拉選項(基于數據庫行數)for( $i = 1; $i < $number_of_rows + 1 ; $i++ ) { $page[ 'body' ] .= "<option value=\"{$i}\">{$i}</option>"; }$page[ 'body' ] .= "</select>";}// 低級別和不可能級別使用文本框直接輸入else$page[ 'body' ] .= "\n <input type=\"text\" size=\"15\" name=\"id\">";$page[ 'body' ] .= "\n <input type=\"submit\" name=\"Submit\" value=\"Submit\"></p>\n";// 不可能級別添加CSRF令牌保護if( $vulnerabilityFile == 'impossible.php' )$page[ 'body' ] .= " " . tokenField();$page[ 'body' ] .= "</form>";
}// 添加查詢結果區域和安全參考資料鏈接
$page[ 'body' ] .= "{$html} // 存儲SQL查詢結果的變量</div><h2>More Information</h2><ul><li>" . dvwaExternalLinkUrlGet( 'http://www.securiteam.com/securityreviews/5DP0N1P76E.html' ) . "</li><li>" . dvwaExternalLinkUrlGet( 'https://en.wikipedia.org/wiki/SQL_injection' ) . "</li><li>" . dvwaExternalLinkUrlGet( 'http://ferruh.mavituna.com/sql-injection-cheatsheet-oku/' ) . "</li><li>" . dvwaExternalLinkUrlGet( 'http://pentestmonkey.net/cheat-sheet/sql-injection/mysql-sql-injection-cheat-sheet' ) . "</li><li>" . dvwaExternalLinkUrlGet( 'https://www.owasp.org/index.php/SQL_Injection' ) . "</li><li>" . dvwaExternalLinkUrlGet( 'http://bobby-tables.com/' ) . "</li></ul>
</div>\n";// 輸出HTML頁面
dvwaHtmlEcho( $page );?>
2、impossible.php
進入DVWA靶場SQL Injection的source源碼目錄,找到impossible.php源碼,分析其為何能讓這一關卡名為不可能實現SQL注入滲透。
打開源碼impossible.php,分析可知這段代碼實現了用戶信息查詢功能,如下所示。
- 驗證用戶登錄狀態:通過檢查$_SESSION['id']判斷用戶是否已登錄。
- 查詢用戶信息:從數據庫中查詢當前登錄用戶的姓和名,并將結果以 HTML 形式展示。
- 數據庫操作流程:獲取會話 ID → 構建 SQL 查詢 → 執行查詢 → 解析結果 → 關閉連接。
詳細注釋后的impossible.php源碼如下所示。
<?php// 檢查是否通過GET方法提交了表單
if( isset( $_GET[ 'Submit' ] ) ) {// 驗證CSRF令牌,防止跨站請求偽造攻擊// 比較用戶請求中的令牌與會話存儲的令牌是否一致checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );// 從GET參數中獲取用戶ID$id = $_GET[ 'id' ];// 驗證輸入是否為數字(防止非數字類型的SQL注入)if(is_numeric( $id )) {// 準備SQL查詢:從users表中查詢指定ID的用戶的名字和姓氏// 使用預處理語句防止SQL注入$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );// 將用戶輸入的ID綁定為整數類型參數// 這一步確保即使輸入包含非數字字符也會被轉換為整數$data->bindParam( ':id', $id, PDO::PARAM_INT );// 執行SQL查詢$data->execute();// 獲取查詢結果的第一行$row = $data->fetch();// 確保查詢只返回一行結果(防止越權訪問)if( $data->rowCount() == 1 ) {// 提取查詢結果中的名字和姓氏$first = $row[ 'first_name' ];$last = $row[ 'last_name' ];// 輸出用戶信息(存在XSS風險,未對輸出進行HTML轉義)$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";}}
}// 生成新的CSRF令牌并存儲到會話中
generateSessionToken();?>
二、SQL注入防范分析
impossible.php 主要功能是處理用戶提交的 ID 查詢請求,通過驗證 Anti-CSRF 令牌確保請求合法性,檢查輸入 ID 是否為數字,再使用 PDO 參數化查詢從數據庫中獲取對應用戶的姓名并反饋給用戶,通過多重安全措施有效防范 SQL 注入等攻擊。impossible.php 主要功能是處理用戶提交的 ID 查詢請求,通過驗證 Anti-CSRF 令牌確保請求合法性,檢查輸入 ID 是否為數字,再使用 PDO 參數化查詢從數據庫中獲取對應用戶的姓名并反饋給用戶,通過多重安全措施有效防范 SQL 注入等攻擊。
- 參數化查詢(Prepared Statement):使用 PDO 預編譯語句,分離 SQL 代碼與用戶輸入數據。
- 輸入驗證:限制輸入為數字類型(
is_numeric
?檢查)。 - Anti-CSRF 令牌:防止跨站請求偽造,間接提升安全性。
1、Anti-CSRF 令牌
通過generateSessionToken
生成隨機令牌并存儲在會話中,表單提交時包含該令牌字段,服務器端checkToken
驗證請求中的令牌與會話中存儲的是否一致,不一致則拒絕請求。
// 驗證令牌
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 生成令牌
generateSessionToken();
// 表單中包含令牌字段
$page[ 'body' ] .= " " . tokenField();
CSRF 攻擊依賴盜用用戶身份發起惡意請求,而 Anti-CSRF 令牌為每個請求附加隨機且時效性的驗證信息。攻擊者攻擊者難以獲取合法令牌,即使構造惡意請求,因令牌不匹配也會被攔截,間接保障 SQL 查詢的輸入來源合法性,降低惡意注入請求的成功率。
2、參數化查詢
通過 PDO 預編譯語句將 SQL 結構與用戶輸入分離,先用:id
作為占位符定義查詢模板,再通過bindParam
綁定參數并指定類型為整數,最后執行查詢。即使輸入包含惡意 SQL 片段,也只會被當作數據處理,無法改變查詢邏輯
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
預編譯階段 SQL 語句結構已固定,數據庫會將占位符視為數據而非可執行代碼。綁定參數時強制類型為整數(PDO::PARAM_INT
),進一步確保輸入僅作為數值處理,從根本上避免用戶輸入被解析為 SQL 指令,徹底阻斷斷裂取數據庫控制權的注入路徑。
3、輸入驗證
獲取用戶輸入的id
后,通過is_numeric
函數驗證其是否為數字。只有當輸入為數字時,才執行后續數據庫操作;非數字輸入會被直接攔截,不進入查詢流程。
$id = $_GET[ 'id' ];
if(is_numeric( $id )) {// 執行數據庫查詢
}
SQL 注入常依賴字符串拼接注入惡意指令,而數字類型輸入無法包含引號、關鍵字等注入要素。is_numeric
從輸入源頭限制數據類型,過濾掉包含字母、符號的潛在惡意輸入,減少注入攻擊的可能性。即使參數化查詢存在疏漏,該驗證也能作為第二道防線攔截風險輸入。