動態生成 SQL 時未驗證用戶輸入,導致惡意輸入改變查詢邏輯。例:
-- 原查詢
SELECT * FROM admin WHERE username='&user' AND password='&pwd'
-- 攻擊輸入 username: 1' or '1'='1
-- 變為
SELECT * FROM admin WHERE username='1' or '1'='1' AND password='&pwd'
-- 條件永真,繞過驗證直接登入
<script language="javascript">
var url = location.search;
var re = /^\?(.*)(select%20|insert%20|delete%20from%20|count\(|drop%20table|update%20truncate%20|asc\(|mid\(|char\(|xp_cmdshell|exec%20master|net%20localgroup%20administrators|"|:|net%20user|'|%20or%20)(.*)$/gi;
if (re.test(url)) {
alert("地址中含有非法字符~");
location.href = "error.asp";
}
</script>
局限: 僅檢查 URL 參數 (GET),無法防禦 POST、Cookie、Header;可被編碼繞過;僅作第一層輕量阻擋。
<%
On Error Resume Next
Dim strTemp
If LCase(Request.ServerVariables("HTTPS")) = "off" Then strTemp = "https://" Else strTemp = "https://" End If
strTemp = strTemp & Request.ServerVariables("SERVER_NAME")
If Request.ServerVariables("SERVER_PORT") <> 80 Then strTemp = strTemp & ":" & Request.ServerVariables("SERVER_PORT")
strTemp = strTemp & Request.ServerVariables("URL")
If Trim(Request.QueryString) <> "" Then strTemp = strTemp & "?" & Trim(Request.QueryString)
strTemp = LCase(strTemp)
If InStr(strTemp, "select%") Or InStr(strTemp, "insert%") Or InStr(strTemp, "delete%20from") Or InStr(strTemp, "count(") Or InStr(strTemp, "drop%20table") Or InStr(strTemp, "update%") Or InStr(strTemp, "truncate%") Or InStr(strTemp, "asc(") Or InStr(strTemp, "mid(") Or InStr(strTemp, "char(") Or InStr(strTemp, "xp_cmdshell") Or InStr(strTemp, "exec%20master") Or InStr(strTemp, "net%20localgroup%20administrators") Or InStr(strTemp, ":") Or InStr(strTemp, "net%20user") Or InStr(strTemp, "'") Or InStr(strTemp, "%20or%20") Then
Response.Write "<script language='javascript'>alert('非法地址!!');location.href='error.asp';</script>"
Response.End
End If
%>
部署: 放入全站 include 頭部。警告: 會誤攔正常含關鍵字內容,**不可替代參數化查詢**。
bool CheckParams(params object[] args) {
string[] Lawlesses = { "=", "'" }; // 自定義非法字元
if (Lawlesses == null || Lawlesses.Length <= 0) return true;
string str_Regex = ".*[";
for (int i = 0; i < Lawlesses.Length - 1; i++)
str_Regex += Lawlesses[i] + "|";
str_Regex += Lawlesses[Lawlesses.Length - 1] + "].*";
foreach (object arg in args) {
if (arg is string) {
if (Regex.Matches(arg.ToString(), str_Regex).Count > 0) return false;
}
else if (arg is ICollection) {
foreach (object obj in (ICollection)arg) {
if (obj is string && Regex.Matches(obj.ToString(), str_Regex).Count > 0) return false;
}
}
}
return true;
}
最佳實踐: 改用白名單正則 (如 `^[a-zA-Z0-9_@.-]{1,50}$`) 驗證每個參數格式,拒絕不符合預期格式的輸入。
| 語言/平台 | 參數化寫法範例 |
|---|---|
| ASP (ADO) | `cmd.CommandText = "SELECT * FROM users WHERE id=?"` → `cmd.Parameters.Append cmd.CreateParameter("@id", 3, 1, , id)` (adInteger) |
| PHP (PDO) | `$stmt = $pdo->prepare("SELECT * FROM users WHERE id=?"); $stmt->execute([$id]);` |
| Java (JDBC) | `PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id=?"); ps.setInt(1, id);` |
| C# (ADO.NET) | `cmd.CommandText = "SELECT * FROM users WHERE id=@id"; cmd.Parameters.AddWithValue("@id", id);` |
| Python (DB-API) | `cursor.execute("SELECT * FROM users WHERE id=%s", (id,))` |
| Node.js (mysql2) | `await connection.execute('SELECT * FROM users WHERE id=?', [id])` |
核心原則: **代碼與資料永久分離**,資料絕不被解析為 SQL 指令,徹底杜絕注入。
SQL 注入防禦的鐵律是:**參數化查詢為唯一根治,關鍵字過濾僅為輔助,系統級 WAF/最小權限/安全標頭為纵深防線**。開發階段強制參數化、代碼審計攔截拼接 SQL;上線前滲透測試驗證;運行期 WAF 實時防護。三位一體,方能讓 SQL 注入成為歷史。