SQL 注入攻擊 (SQL Injection)
SQL Injection (SQL 注入) 是一種常見的網站安全漏洞,攻擊者透過在輸入欄位中插入惡意的 SQL 程式碼,來操控資料庫執行非預期的查詢,可能導致資料外洩、資料被竄改甚至整個資料庫被刪除。
SQL Injection 攻擊原理
假設有一個登入功能,後端程式這樣組合 SQL:
-- 程式碼 (有漏洞)
query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
正常情況下,使用者輸入帳號 admin 和密碼 1234,會產生:
SELECT * FROM users WHERE username = 'admin' AND password = '1234';
但如果攻擊者在密碼欄位輸入 ' OR '1'='1,SQL 會變成:
SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1';
由於 '1'='1' 永遠為真,這個查詢會返回所有使用者資料,攻擊者就能繞過驗證登入系統。
常見的 SQL Injection 攻擊手法
繞過登入驗證
帳號: admin' --
密碼: (任意)
產生的 SQL:
SELECT * FROM users WHERE username = 'admin' --' AND password = '';
-- 是 SQL 註解,後面的密碼驗證被忽略了。
取得額外資料 (UNION 注入)
輸入: ' UNION SELECT username, password FROM users --
攻擊者可以利用 UNION 將其他資料表的內容一起查出來。
刪除資料
輸入: '; DROP TABLE users; --
這會導致整個 users 資料表被刪除。
如何防範 SQL Injection
1. 使用參數化查詢 (Parameterized Query)
這是最有效的防範方式。讓資料庫區分「指令」和「資料」:
PHP (PDO):
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = ? AND password = ?');
$stmt->execute([$username, $password]);
Python:
cursor.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password))
Java:
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE username = ? AND password = ?");
stmt.setString(1, username);
stmt.setString(2, password);
Node.js:
connection.query('SELECT * FROM users WHERE username = ? AND password = ?', [username, password]);
2. 使用預存程序
將 SQL 邏輯封裝在預存程序中,應用程式只傳遞參數:
CREATE PROCEDURE ValidateUser
@username VARCHAR(50),
@password VARCHAR(50)
AS
BEGIN
SELECT * FROM users WHERE username = @username AND password = @password;
END;
3. 輸入驗證
對使用者輸入進行檢查和過濾:
- 檢查資料型別 (數字、日期等)
- 限制輸入長度
- 過濾特殊字元 (但不能只依賴這個方法)
4. 最小權限原則
資料庫帳號只給予必要的權限,例如:
- 網站使用的帳號不應有 DROP、ALTER 權限
- 只開放需要存取的資料表
5. 錯誤訊息處理
不要將詳細的資料庫錯誤訊息顯示給使用者,這會洩漏資料庫結構資訊。
重點整理
| 防範方式 | 有效性 |
|---|---|
| 參數化查詢 | ★★★★★ 最有效 |
| 預存程序 | ★★★★☆ |
| 輸入驗證 | ★★★☆☆ 輔助用 |
| 權限控制 | ★★★☆☆ 降低損害 |
絕對不要用字串串接的方式組合 SQL 語句,這是 SQL Injection 漏洞的主要成因。