在 OWASP Top 10 的歷史中,注入攻擊(Injection) 長期榜上有名。其中最具代表性的,就是SQL 注入(SQL Injection, SQLi)。他破壞力極強,攻擊者只需在輸入框或API參數中注入惡意SQL語法,就能繞過身分驗證,甚至將整個資料庫導出(脫庫)。
當後端將未經過濾或處理的使用者輸入直接拼接到 SQL 查詢語句中時,資料庫直譯器會將使用者輸入的字串當作是「SQL 指令」的一部分來執行,而改變原本程式邏輯。
1. 繞過身分驗證
假設後端登入驗證的 SQL 查詢如下:
-- 後端不安全的 SQL 拼接範例
SELECT * FROM users WHERE username = '$user_input' AND password = '$pass_input';
如果攻擊者在帳號欄位輸入:admin' -- 或 admin' OR '1'='1
拼接後的 SQL 會變成:SELECT * FROM users WHERE username = 'admin' --' AND password = '...';-- 在 SQL 中代表單行註解,後續的密碼比對邏輯會直接被資料庫忽略,攻擊者不需要密碼即可登入帳號。
2. 資料庫脫庫
-- 原始查詢
SELECT id, title, content FROM articles WHERE category_id = '1'
當攻擊者發現頁面會顯示 SQL 查詢結果時,可利用 UNION SELECT 語法附加自訂的查詢,將敏感資料一次性抓出:
-- 攻擊者注入 UNION 語法
SELECT id, title, content FROM articles WHERE category_id = '1' UNION SELECT 1, username, password_hash FROM users;
3. 盲注
當網站不會在畫面上顯示 SQL 查詢結果或錯誤訊息時,攻擊者可以發動盲注:
布林盲注(Boolean-Based): 透過輸入會觸發 True / False 的 SQL 條件,觀察網頁頁面結構的微小變化,逐一推敲出資料庫字元。
時間盲注(Time-Based): 利用 SLEEP(5) 或 WAITFOR DELAY 語法,觀察伺服器回應延遲的時間差,判斷 SQL 條件是否成立。
不安全的字串拼接與動態組裝
# Python / Flask 不安全範例
@app.route("/search")
def search():
keyword = request.args.get("keyword")
# 直接拼接字串,極度危險!
query = f"SELECT id, name, price FROM products WHERE name LIKE '%{keyword}%'"
cursor.execute(query)
return cursor.fetchall()
採用參數化查詢
# Python / Flask 安全範例(使用 Parameterized Query)
@app.route("/search")
def search():
keyword = request.args.get("keyword")
# 將 SQL 結構與參數分開處理,使用預編譯語法
query = "SELECT id, name, price FROM products WHERE name LIKE %s"
# 強制將輸入視為純字串值,資料庫絕不解析為語法指令
cursor.execute(query, (f"%{keyword}%",))
return cursor.fetchall()
1. 參數化查詢
這是防禦 SQLi 最有效、最根本的方法。當使用 Prepared Statements 時,資料庫會採取「先編譯 SQL 結構,再填入資料」兩階段處理:
2. 最小權限原則
Web 應用程式存取資料庫的帳號,切勿使用最高權限帳號。
依據業務需求區分權限(例如:一般使用者帳號不應具備 DROP TABLE、ALTER TABLE 或讀取系統表的權限)。
3. 深度防禦:WAF阻斷
在邊界部署 WAF(網站應用程式防火牆),透過常見的 SQLi 語法特徵過濾第一線攻擊 Payload,為未開發完善的舊系統爭取虛擬修補(Virtual Patching)時間。