本文旨在協助開發者理解 SQL 注入攻擊向量,以建構更安全的應用程式。**嚴禁將此技術用於未經授權的系統測試或攻擊。** 請遵守當地法律法規與《電腦犯罪條例》等相關規定。
應用程式將使用者輸入**直接拼接**進 SQL 語句,未經嚴格驗證或參數化,導致惡意輸入改變 SQL 語義。
| 技巧 | 原理 | 防禦對策 |
|---|---|---|
ORDER BY 探測欄位數 |
遞增數字直到報錯,推斷欄位數 | 參數化查詢;錯誤訊息不回顯前端 |
GROUP BY 替代 ORDER BY |
繞過針對 ORDER BY 的 WAF 規則 |
WAF 規則需涵蓋同義語句;根本靠參數化 |
(SELECT * FROM t) = (SELECT 1) 強制報錯洩露欄位數 |
利用子查詢欄位數不匹配報錯回顯 | 統一錯誤頁面,不回顯資料庫錯誤細節 |
UNION SELECT 聯合查詢竊取資料 |
將惡意查詢結果併入正常結果集 | 嚴格輸入驗證、最小權限帳號、參數化 |
information_schema 列舉結構 |
查詢系統表取得資料庫/表/欄位名 | 資料庫帳號僅授予業務所需最小權限 |
INSERT/UPDATE/DROP 破壞資料 |
多語句執行(若驅動支援)執行寫入操作 | 帳號無 INSERT/UPDATE/DROP 權限;禁用多語句 |
唯一可靠防線——永遠使用預備語句,將資料與代碼徹底分離。
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id AND status = :status');
$stmt->execute([':id' => $input_id, ':status' => 1]);
$rows = $stmt->fetchAll();
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM users WHERE id = ? AND status = ?");
ps.setInt(1, inputId);
ps.setInt(2, 1);
ResultSet rs = ps.executeQuery();
cursor.execute("SELECT * FROM users WHERE id = %s AND status = %s", (input_id, 1))
SELECT/INSERT/UPDATE,嚴禁 DROP/ALTER/GRANT。intval(),列舉值用 in_array() 驗證。display_errors,統一友善錯誤頁,詳細錯誤寫入日誌供開發排查。general_log 或审计插件,異常 SQL 即時告警。