一、SQL注入基礎理論
?
1.1 什么是SQL注入
?
SQL注入是一種常見的Web安全問題,攻擊者通過在Web應用程序的輸入字段中插入惡意的SQL語句,改變原本SQL查詢的邏輯,實現非法獲取數據、篡改數據、執行系統命令等操作。這種情況產生的根本原因在于應用程序對用戶輸入數據的合法性沒有進行嚴格的驗證和過濾,導致用戶輸入的數據被當作SQL語句的一部分執行。
?
1.2 SQL注入的危害
?
數據泄露:攻擊者可以通過SQL注入獲取數據庫中的敏感信息,如用戶賬號、密碼、身份證號等。
數據篡改:修改數據庫中的數據,如篡改用戶的交易記錄、修改網站內容等。
權限提升:在某些情況下,攻擊者可以利用SQL注入獲取數據庫管理員權限,進一步控制整個服務器。
服務器被控制:通過SQL注入執行系統命令,控制服務器,如上傳惡意文件、開啟后門等。
?
1.3 SQL注入的類型
?
數字型注入:當輸入的參數為數字類型時,SQL語句中沒有使用引號包裹參數,如 ?SELECT * FROM users WHERE id = 1?。
字符型注入:輸入的參數為字符類型,SQL語句中使用引號包裹參數,如 ?SELECT * FROM users WHERE username = 'admin'?。
寬字節注入:利用GBK等寬字節編碼的特性,繞過對單引號等特殊字符的過濾。
盲注:在無法直接獲取數據庫返回結果的情況下,通過構造條件語句,根據頁面返回的狀態(如頁面正常、頁面錯誤、響應時間等)來推斷數據庫中的信息,包括布爾盲注和時間盲注。
二次注入:攻擊者將惡意數據存儲在數據庫中,當應用程序再次調用該數據進行SQL操作時,觸發SQL注入。
堆疊注入:在一條SQL語句中執行多條SQL語句,通過分號(;)分隔不同的SQL命令 。
?
二、SQL注入實戰案例
?
2.1 搭建測試環境
?
我們可以使用PHP + MySQL搭建一個簡單的Web應用來模擬相關場景。
?
數據庫表結構:
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(50),password VARCHAR(50)
);
INSERT INTO users (username, password) VALUES ('admin', 'admin123'), ('user1', 'user123');
PHP代碼(存在非安全設計):
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "testdb";// 創建連接
$conn = new mysqli($servername, $username, $password, $dbname);// 檢查連接
if ($conn->connect_error) {die("連接失敗: ". $conn->connect_error);
}$user_input = $_GET["username"];
$sql = "SELECT * FROM users WHERE username = '$user_input'";
$result = $conn->query($sql);if ($result->num_rows > 0) {while($row = $result->fetch_assoc()) {echo "id: ". $row["id"]. " Name: ". $row["username"]. "<br>";}
} else {echo "沒有找到匹配的用戶";
}$conn->close();
?>
訪問鏈接 ?http://localhost/test.php?username=admin? 時,會正常查詢并顯示用戶信息。
2.2 數字型SQL注入實戰
假設SQL查詢語句為 ?SELECT * FROM products WHERE id = 1?,我們可以嘗試以下操作:
輸入 ?1 or 1=1?,此時SQL語句變為 ?SELECT * FROM products WHERE id = 1 or 1=1?,由于 ?1=1? 恒成立,會返回所有產品信息。
2.3 字符型SQL注入實戰
對于 ?SELECT * FROM users WHERE username = 'admin'? 這樣的查詢語句:
輸入 ?' or '1'='1?,SQL語句變為 ?SELECT * FROM users WHERE username = '' or '1'='1'?,同樣因為 ?'1'='1'? 恒成立,會返回所有用戶信息。
輸入 ?' and 1=2 union select 1,2 -- -?,?' and 1=2? 使前面的查詢條件不成立,?union select 1,2? 會將 ?1? 和 ?2? 作為查詢結果返回,?-- -? 注釋掉后面的單引號,防止語法錯誤。
2.4 盲注實戰
布爾盲注
假設頁面只有兩種返回狀態:有數據返回時顯示 “查詢成功”,無數據返回時顯示 “查詢失敗”。我們可以通過構造條件語句來推斷數據庫中的信息。例如,判斷數據庫中是否存在 ?admin? 用戶:
輸入 ?' and exists(select * from users where username='admin') -- -?,如果頁面顯示 “查詢成功”,則說明存在 ?admin? 用戶。
時間盲注
當頁面沒有明顯的回顯信息時,可以利用時間盲注。例如,判斷數據庫中 ?users? 表的記錄數是否大于10:
' and if((select count(*) from users)>10, sleep(5), 1) -- -
如果頁面響應時間超過5秒,說明 ?users? 表的記錄數大于10。
三、SQL注入遇到的問題及解決方案
3.1 特殊字符過濾
很多應用程序會對單引號、分號等特殊字符進行過濾,導致相關操作失敗。
解決方案:
使用編碼繞過,如將單引號 ?'? 編碼為 ?%27?(URL編碼)。
利用寬字節注入,在單引號前添加 ?%df?(GBK編碼下,?%df'? 會組成一個合法的漢字,從而繞過單引號過濾)。
3.2 預編譯語句的使用
預編譯語句可以有效防范相關風險,因為它將用戶輸入的數據和SQL語句進行了分離。
示例代碼(PHP PDO預編譯):
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "testdb";try {$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);// 設置 PDO 錯誤模式為異常$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);$user_input = $_GET["username"];$sql = "SELECT * FROM users WHERE username = :username";$stmt = $conn->prepare($sql);$stmt->bindParam(':username', $user_input);$stmt->execute();$result = $stmt->fetchAll(PDO::FETCH_ASSOC);if (count($result) > 0) {foreach ($result as $row) {echo "id: ". $row["id"]. " Name: ". $row["username"]. "<br>";}} else {echo "沒有找到匹配的用戶";}
} catch(PDOException $e) {echo "Error: ". $e->getMessage();
}
$conn = null;
?>
在上述代碼中,?bindParam? 方法會自動對用戶輸入進行轉義,確保數據的安全性。
四、繞過WAF(Web應用防火墻)
4.1 WAF的工作原理
WAF通過檢測HTTP請求中的數據,與預設的規則庫進行匹配,一旦發現請求中包含可疑的特征,就會攔截該請求。
4.2 繞過WAF的方法
大小寫混合:將SQL關鍵字進行大小寫混合,如 ?SeLeCt?,可能繞過部分基于規則的WAF。
內聯注釋:使用MySQL的內聯注釋,如 ?/*!SELECT*/?,部分WAF可能無法識別這種形式的SQL關鍵字。
雙寫繞過:將關鍵字拆分后雙寫,例如 ?selseselectct?,當WAF過濾掉第一個 ?select? 后,仍能組成完整的 ?select? 關鍵字。
編碼繞過:對相關語句進行URL編碼、Unicode編碼等,如將 ?select? 編碼為 ?%73%65%6C%65%63%74?,繞過基于明文匹配的WAF。
利用WAF的邏輯漏洞:某些WAF在處理長請求、多參數請求時可能存在邏輯問題,可以構造特殊的請求格式來繞過檢測。
五、總結
SQL注入是Web應用中非常危險的安全問題,掌握SQL注入的原理、實戰技巧以及繞過方法,對于滲透測試人員來說至關重要。同時,開發人員也應該重視輸入驗證和使用安全的數據庫操作方式(如預編譯語句),從根源上避免出現相關安全隱患。在實際的滲透測試和安全防護過程中,需要不斷學習和實踐,以應對不斷變化的攻擊和防御技術。