萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL SQL 注入防禦:特殊字符轉義與參數化查詢最佳實踐

SQL 注入仍是 Web 應用頭號安全風險。雖然 PHP 提供 mysql_real_escape_string 等轉義函數,但單純依賴轉義存在繞過風險。本文深入分析轉義機制局限,並推薦參數化查詢作為根本解決方案。

為何單純轉義不可靠?

測試資料含特殊字符:

INSERT INTO `user` VALUES ('1', ''\\'''\\\'');

直接查詢會語法錯誤:

SELECT * FROM user WHERE name = "'\'''\'"; -- 錯誤

需手動轉義後才能查詢:

SELECT * FROM user WHERE name = "'\\'''\\\'"; -- 正確

常見轉義函數局限性

  • mysql_escape_string:不考慮連線字符集,已棄用
  • mysql_real_escape_string:依賴連線字符集,但無法防禦所有注入向量(如數字型注入、編碼繞過)
  • 自定義 str_replace 黑名單:維護成本高、易漏掉特殊字符、無法處理編碼變體

錯誤的防禦模式:關鍵字過濾

嘗試用正則攔截 select|insert|update|delete 等關鍵字:

function inject_check($sql_str) {
    return eregi('select|insert|update|delete|\'|', $sql_str);
}

致命缺陷:

  • 誤攔截正常業務字段(如 user_select、update_time)
  • 繞過手段多樣:大小寫變體、編碼、註釋符、十六進位、字串拼接等
  • 無法防禦二階注入、盲注等進階攻擊

根本解決方案:參數化查詢

使用預處理語句,資料與代碼徹底分離:

PDO 範例(推薦)

$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->prepare('SELECT * FROM user WHERE name = ? AND status = ?');
$stmt->execute([$name, $status]);
$rows = $stmt->fetchAll();

MySQLi 範例

$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$stmt = $mysqli->prepare('SELECT * FROM user WHERE name = ?');
$stmt->bind_param('s', $name);
$stmt->execute();
$result = $stmt->get_result();

輔助防禦層(纏深度防禦)

  1. 輸入驗證:白名單驗證格式、長度、範圍(如 ID 必須為正整數)
  2. 最小權限:應用布號僅授予必要表的 SELECT/INSERT/UPDATE/DELETE,禁用 DROP/ALTER
  3. 錯誤資訊隱藏:生產環境關閉詳細 SQL 錯誤輸出,避免洩露表結構
  4. WAF/IDS:部署 Web 應用防火牆攔截已知攻擊特徵
  5. 定期審計:代碼審查、滲透測試、依賴掃描

歷史代碼遷移建議

  • 逐步重構:優先改造高風險入口(登入、搜尋、ID 參數)
  • 封裝資料存取層:統一使用參數化查詢,禁止拼接 SQL
  • 啟用 sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION 提高嚴格度
  • 考慮 ORM 框架(Laravel Eloquent、Doctrine 等)內建參數化保護

總結

字符轉義只是輔助手段,參數化查詢才是防禦 SQL 注入的銀彈。現代 PHP 開發應完全棄用 mysql_* 系列函數,全面採用 PDO/MySQLi 預處理語句,配合輸入驗證與最小權限原則,構建多層防禦體系。

copyright © 萬盛學電腦網 all rights reserved