addslashes()、stripslashes() 與已移除的 magic_quotes_gpc 是 PHP 早期為防止 SQL 注入而引入的機制,但因設計缺陷導致無數雙重轉義、資料損壞、安全誤區。本文深度解析其運作原理、歷史坑點、現代參數化查詢取代方案,以及維護舊專案時的正確處理策略。
| 函數 | 操作 | 轉換對照 |
|---|---|---|
addslashes($str) | 在特定字元前加反斜線 | '→\', "→\", \→\\, NULL→\0 |
stripslashes($str) | 移除反斜線轉義 | \'→', \\→\, \"→", \0→NULL |
PHP 4/5 預設開啟 magic_quotes_gpc = On → 自動對 $_GET, $_POST, $_COOKIE 執行 addslashes() → 開發者以為「已安全」,直接串接 SQL → 問題: 1. 雙重轉義:開發者又手動 addslashes() → \\' 寫入 DB 2. 輸出時需 stripslashes(),否則頁面顯示 O\'Reilly 3. 不同伺服器設定不同 (On/Off),代碼移植必壞 4. 無法防禦所有注入 (如數值型注入、編碼繞過) 5. 影響 JSON、XML、檔案上傳等非 SQL 場景 PHP 5.4 移除 magic_quotes_gpc,PHP 7 移除相關函數
<?php
// magic_quotes_gpc = On 時
$name = $_POST['name']; // 已自動 addslashes: O\'Reilly
$name = addslashes($name); // 再次轉義: O\\\'Reilly
$sql = "INSERT INTO users (name) VALUES ('$name')"; // 寫入 DB: O\\\'Reilly
// 讀出顯示: O\\'Reilly (需 stripslashes 兩次才還原)
?>
<?php
// 1. 入口檔案 (bootstrap.php) 統一清理 magic_quotes 影響
if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) {
function stripslashes_deep($value) {
return is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
}
$_GET = stripslashes_deep($_GET);
$_POST = stripslashes_deep($_POST);
$_COOKIE = stripslashes_deep($_COOKIE);
$_REQUEST = stripslashes_deep($_REQUEST);
}
// 2. 之後代碼完全不使用 addslashes/stripslashes
// 3. 所有 SQL 使用參數化查詢 (PDO / mysqli prepare)
$stmt = $pdo->prepare("INSERT INTO users (name) VALUES (?)");
$stmt->execute([$_POST['name']]); // 完全安全,無需轉義
?>
<?php
$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("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->execute([$name, $email]);
// 查詢
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ? AND status = ?");
$stmt->execute([$email, 1]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// 批次插入
$pdo->beginTransaction();
$stmt = $pdo->prepare("INSERT INTO logs (msg) VALUES (?)");
foreach ($messages as $msg) {
$stmt->execute([$msg]);
}
$pdo->commit();
?>
<?php
$mysqli = new mysqli('127.0.0.1', 'user', 'pass', 'db');
$stmt = $mysqli->prepare("INSERT INTO users (name) VALUES (?)");
$stmt->bind_param('s', $name);
$stmt->execute();
?>
幾乎不需要。極少數情境:
echo "var msg = '" . addslashes($str) . "';" (更好用 json_encode())。mysqli_real_escape_string() (針對連線字元集) 而非 addslashes()。escapeshellarg())、正則模式 (應用 preg_quote())。php.ini magic_quotes_gpc = Off (PHP 5.4+ 已移除)。stripslashes_deep 清理超全域變數 (如上)。
3. 全域搜尋 addslashes、stripslashes、mysql_escape_string、mysql_real_escape_string。
4. 逐個替換為 PDO::prepare / mysqli::prepare + bind_param / execute。
5. 移除所有 stripslashes() 輸出端呼叫 (資料庫已存原始資料)。
6. 執行完整測試套件,特別檢查含單引號、反斜線、多位元組字元的資料。
7. 部署後監控 SQL 錯誤日誌、資料完整性。
addslashes()/stripslashes() 是 已棄用的歷史產物。現代 PHP 開發完全不需要這兩個函數。參數化查詢 (Prepared Statements) 從根本上分離代碼與資料,徹底解決 SQL 注入,且無編碼、雙重轉義、效能等副作用。維護舊專案時,優先在入口點清理 magic_quotes 殘留影響,再系統性遷移至參數化查詢,即可獲得安全、乾淨、可維護的代碼庫。