新代碼請直接使用 PDO 預處理語句 (Prepared Statements),完全避免手動轉義。 本文僅針對存量代碼維護、歷史認知糾偏。
| 函數 | 轉義範圍 | 字符集感知 | PHP 版本 | 狀態 |
|---|---|---|---|---|
addslashes() | 單引號、雙引號、反斜杠、NUL | ❌ 無感知 | 所有 | 不推薦用於 SQL |
mysql_escape_string() | MySQL 特殊字符 | ❌ 無感知 | PHP 4.3.0+ | 已移除 (PHP 7.0) |
mysql_real_escape_string() | MySQL 特殊字符 | ✅ 需活躍連接,讀取 character_set_client | PHP 4.3.0+ | 已移除 (PHP 7.0) |
addslashes 防不住注入?經典 GBK 編碼繞過漏洞:
<?php
// 假設連接字符集為 GBK
$input = chr(0xbf) . chr(0x27) . ' OR 1=1 /*'; // 0xbf27 是有效 GBK 字符 '縗' 的第二字節 + 單引號
// addslashes 將 0x27 轉為 0x5c27
$escaped = addslashes($input); // 0xbf 0x5c 0x27 ...
// MySQL 以 GBK 解析:0xbf5c 是一個合法字符 '鰈',0x27 依然是單引號 → 注入成功
?>
mysql_real_escape_string 知道當前連接是 GBK,會將 0xbf27 整體視為多字節字符,不插入反斜杠,從而避免產生新的合法字符組合。
mysql_real_escape_string vs mysql_escape_stringmysql_real_escape_string($str, $link):**必須傳入連接資源** (或使用最近打開的連接),讀取連接的字符集設置。mysql_escape_string($str):無連接參數,假設默認字符集 (通常 latin1),**不安全**。magic_quotes_gpc 歷史遺毒 (PHP 5.4 已移除)<?php
// 舊代碼常見模式
if (!get_magic_quotes_gpc()) {
$lastname = addslashes($_POST['lastname']);
} else {
$lastname = $_POST['lastname']; // 已被自動 addslashes
}
// 問題:magic_quotes 也是 addslashes,同樣有 GBK 漏洞!
// 正確做法:無論 magic_quotes 狀態,統一用 mysql_real_escape_string 或預處理
?>
<?php
$pdo = new PDO('mysql:host=localhost;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, $status]);
$user = $stmt->fetch();
// 優勢:
// 1. 數據與代碼徹底分離,零注入風險
// 2. 同一 SQL 重複執行只解析一次,性能更好
// 3. 自動處理類型轉換、NULL、字符集
?>
addslashes( / mysql_real_escape_string( / mysql_escape_string(。PDO::prepare + execute([...])。mysqli_real_escape_string($link, $str) (mysqli 版本仍保留)。magic_quotes_gpc 相關判斷代碼。addslashes 是字符串級轉義,**不懂 SQL、不懂字符集**,僅適用於非數據庫場景 (如寫入文件、生成 JS 字符串)。mysql_real_escape_string 雖懂字符集,但屬於 **已棄用的 mysql_* 擴展**。**唯一正解:PDO 預處理 + 關閉模擬預處理**。