SQL 注入是 Web 應用頭號風險。許多開發者仍誤信 `addslashes()`、`mysql_escape_string()` 能全方位防護。本文從字元編碼層面揭示 `addslashes` 失效根因,對比三大轉義函數差異,確立現代防注入黃金標準。
以 GBK/Big5 等多位元組編碼為例:
**結論**:`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 = 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}%"]);
addslashes 不是防注入工具,mysql_real_escape_string 是有條件的補丁,PDO 真實預處理才是架構級解法。 新專案零理由不直接上 PDO + 參數綁定。