首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 防 SQL 注入深度解析:addslashes 為何不安全、mysql_real_escape_string 與 PDO 預處理正確姿勢

     編輯:PHP 相關知識

PHP 防 SQL 注入深度解析:addslashes 為何不安全、mysql_real_escape_string 與 PDO 預處理正確姿勢

SQL 注入是 Web 應用頭號風險。許多開發者仍誤信 `addslashes()`、`mysql_escape_string()` 能全方位防護。本文從字元編碼層面揭示 `addslashes` 失效根因,對比三大轉義函數差異,確立現代防注入黃金標準。

addslashes 失效的核心原因:多位元組字元編碼繞過

以 GBK/Big5 等多位元組編碼為例:

  1. 攻擊者提交位元組序列 `0xBF 0x27` (對應某個雙位元組漢字的後半部 + 單引號)。
  2. `addslashes()` 僅對單引號 `0x27` 轉義為 `0x5C 0x27` (反斜線 + 單引號),結果變為 `0xBF 0x5C 0x27`。
  3. 資料庫按 GBK 解碼:`0xBF 0x5C` 合法組成一個漢字,`0x27` 恢復為**單引號** → **注入成功**。
**結論**:`addslashes()` 不感知字元集,僅做位元組級替換,**在多位元組編碼下根本無法防注入**。

三大轉義函數對比表

函數機制字元集感知PHP 版本要求推薦等級
`addslashes()`強制在單引號、雙引號、反斜線、NULL 前加反斜線❌ 無所有版本❌ **禁用於 SQL**
`mysql_escape_string()`MySQL 客戶端庫轉義,**不需連線**❌ 無 (使用預設字元集)PHP 4.3.0+⚠️ 已棄用/移除
`mysql_real_escape_string()`MySQL 客戶端庫轉義,**需有效連線**,讀取連線當前字元集✅ 有 (依賴 `mysql_set_charset`)PHP 4.3.0+⚠️ 已移除 (PHP 7)
`mysqli_real_escape_string()`同上,mysqli 版✅ 有PHP 5+✅ 可用但非最佳
PDO 預處理 (參數綁定)**協議層分離代碼與資料**,無需轉義✅ 原生支援PHP 5.1+🏆 **黃金標準**

為何 PDO 預處理是終極方案?

  1. 協議級分離:SQL 範本與參數分開發送至 MySQL,參數永遠不會被解析為 SQL 片段,根絕注入可能。
  2. 無字元集陷阱:驅動層處理編碼轉換,開發者無需關心連線字元集。
  3. 效能優勢:同一 SQL 範本重複執行時,MySQL 僅解析優化一次 (Prepared Statement)。
  4. 統一 API:支援 MySQL、PostgreSQL、SQLite、SQL Server 等,遷移零成本。

正確使用範例 (PDO)

$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8mb4', 'user', 'pass', [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES   => false, // 關鍵:使用真實預處理,非模擬
]);

// 查詢
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? AND status = ?');
$stmt->execute([$email, 1]);
$user = $stmt->fetch();

// 寫入
$stmt = $pdo->prepare('INSERT INTO logs (msg, level) VALUES (?, ?)');
$stmt->execute([$message, 'info']);

// LIKE 模糊查詢:通配符放在參數值,而非 SQL 範本
$stmt = $pdo->prepare('SELECT * FROM articles WHERE title LIKE ?');
$stmt->execute(["%{$keyword}%"]);

歷史遺留代碼修補策略

  1. **立即停用** `addslashes()` 用於 SQL 參數處理。
  2. 若仍在用 `mysql_*`:升級至 `mysqli` 或 PDO,**務必**先 `mysqli_set_charset($link, 'utf8mb4')` 確保字元集正確,再用 `mysqli_real_escape_string()`。
  3. 啟用 `magic_quotes_gpc` (PHP 5.4 已移除) 的舊專案:入口檔案首行 `if (get_magic_quotes_gpc()) { $_GET = stripslashes_deep($_GET); ... }` 還原原始輸入,再走 PDO 預處理。
  4. 資料庫層面:最小權限帳號、禁用多語句 (`mysqli::multi_query`)、WAF 防禦、定期審計慢查詢日誌。

總結

addslashes 不是防注入工具,mysql_real_escape_string 是有條件的補丁,PDO 真實預處理才是架構級解法。 新專案零理由不直接上 PDO + 參數綁定。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved