PHP 早期引入 magic_quotes_gpc 自動轉義 GET/POST/COOKIE 資料,試圖防止 SQL 注入,卻因「雙重轉義」、「非資料庫場景污染」、「版本不一致」造成無數災難。PHP 5.4 徹底移除此特性。本文系統梳理轉義演進史,詳解 addslashes/stripslashes 正確用法,並確立現代 PHP 防禦鐵律:棄用轉義,擁抱預處理語句。
| 設定 | 影響範圍 | 可執行期修改 | 預設值 | 備註 |
|---|---|---|---|---|
magic_quotes_gpc | GET/POST/COOKIE | 否 (PHP_INI_PERDIR) | On (≤5.3) | 自動為 ', ", \, NUL 加反斜線 |
magic_quotes_runtime | 外部資料來源 (DB、檔案、fopen 等) | 是 | Off | 已移除 |
magic_quotes_sybase | 覆蓋 gpc,單引號轉為 '' 而非 \' | 否 | Off | 已移除 |
addslashes($str):在 ' " \ NUL 前加反斜線。不感知字元編碼,多位元組字元 (如 GBK 中文) 可能破壞字元結構 (經典 0x5c 尾字節問題)。stripslashes($str):移除 addslashes() 加入的反斜線。get_magic_quotes_gpc():PHP 5.4+ 已移除,呼叫直接報錯。function wp_slash($value) {
if (is_array($value)) {
foreach ($value as $k => $v) {
// 遞歸處理多維陣列
$value[$k] = is_array($v) ? wp_slash($v) : addslashes($v);
}
} else {
$value = addslashes($value);
}
return $value;
}
設計意圖:為 WordPress 核心 API (期望已轉義資料) 準備資料,**非用於直接進入 SQL 查詢**。
if (!function_exists('get_magic_quotes_gpc') || !get_magic_quotes_gpc()) {
// magic_quotes 關閉或函數不存在 (PHP 5.4+):手動轉義 (僅用於老舊 mysql_* API)
foreach (['_COOKIE', '_POST', '_GET'] as $v) {
foreach ($$v as $kk => $vv) {
// 跳過以 _ 開頭的鍵 (如 _token, _method)
if ($kk[0] !== '_') {
$$v[$kk] = addslashes($vv);
}
}
}
} else {
// magic_quotes 開啟:資料已被自動轉義,若再手動 addslashes 會雙重轉義
// 輸出顯示時需 stripslashes() 還原
}
| 維度 | addslashes / mysql_real_escape_string | PDO / MySQLi 預處理語句 |
|---|---|---|
| 原理 | 字串轉義拼接 SQL | SQL 解析與資料分離,參數綁定 |
| 編碼安全 | 依賴連線字元集設定,易出錯 | 驅動層處理編碼,徹底免疫 |
| 效能 | 每次查詢皆解析 SQL | 可重複執行,優化器複用執行計畫 |
| 防注入 | 邊緣案例可繞過 (多位元組、編碼) | 理論上 100% 防注入 |
| 適用場景 | 極舊專案維護、無法用預處理的 DDL | 所有現代專案強制標準 |
<?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("INSERT INTO users (username, email) VALUES (?, ?)");
$stmt->execute([$_POST['username'], $_POST['email']]); // 參數自動綁定,零轉義
// 查詢
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ? AND status = ?");
$stmt->execute([$id, 1]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
資料入庫不轉義 (存原始資料),輸出到 HTML 時才轉義:
htmlspecialchars($str, ENT_QUOTES, 'UTF-8'):防 XSS 黃金標準。{{ $var }}, Twig {{ var|e }}) 預設自動轉義。若歷史資料已被雙重轉義儲存 (如 <img alt=\\"\\" ...>):
stripslashes() 還原一次。addslashes、mysql_real_escape_string、mysqli_escape_string。htmlspecialchars,JS 用 json_encode,URL 用 urlencode,Shell 用 escapeshellarg。magic_quotes 狀態,統一資料流向,重構舊代碼移除所有手動轉義邏輯。轉義是上世紀的產物,預處理才是當代 PHP 開發者的專業徽章。