聲明: 本文旨在從安全防禦角度分析 SQL 注入攻擊原理,幫助開發者理解風險並編寫安全代碼。嚴禁用於非法攻擊測試。
假設存在一個未經嚴格過濾的查詢接口:
SELECT username, email, content FROM test_table WHERE user_id = {uid};
攻擊者可控制 uid 參數。正常業務預期 uid 為整數,但若無參數化處理,攻擊者可構造:
uid = -1 UNION SELECT username, password, content FROM test_table WHERE user_id = 1;
最終執行 SQL 變為:
SELECT username, email, content FROM test_table WHERE user_id = -1
UNION SELECT username, password, content FROM test_table WHERE user_id = 1;
原本顯示 email 的列位置,現泄露了管理員 password。
若原查詢僅返回 1-2 個列(如 SELECT secret FROM logs WHERE id=?),攻擊者無法直接通過 UNION SELECT 獲取多敏感字段(列數必須一致)。此時 CONCAT() 成為關鍵利用工具:
uid = -1 UNION SELECT CONCAT(password, ':', sex, ':', address, ':', telephone) FROM users WHERE id=1;
原理: CONCAT(str1, str2, ...) 將多字段拼接為單一字符串,繞過「列數必須匹配」的限制,將多敏感信息壓縮進單列輸出。
進階技巧: 使用十六進制分隔符 0x3a(冒號)避免特殊字符過濾:
CONCAT(password, 0x3a, sex, 0x3a, address, 0x3a, telephone)
uid 進行類型檢查(如強制轉 int)、長度限制、特殊字符過濾。FILE 權限)。使用預編譯語句,數據與代碼徹底分離:
-- Java (PreparedStatement)
String sql = "SELECT username, email FROM users WHERE user_id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, uid); // 類型安全,自動處理轉義
ResultSet rs = ps.executeQuery();
-- Python (PyMySQL)
cursor.execute("SELECT username, email FROM users WHERE user_id = %s", (uid,))
SELECT/INSERT/UPDATE,禁止 DROP, UNION 操作敏感系統表。生產環境關閉詳細錯誤回顯,統一返回通用錯誤頁面,避免洩露表結構、SQL 片段。
CONCAT 在注入中是「放大器」,而非「入口」。堵住入口——**堅持參數化查詢、嚴格輸入驗證、最小權限原則**——才是根治 SQL 注入的唯一正道。安全無小事,每一行拼接 SQL 的代碼都是潛在隱患。