歷史遺產:Magic Quotes (PHP 5.4 移除)
| 指令 | 作用範圍 | 觸發時機 | 狀態 |
magic_quotes_gpc | GET/POST/COOKIE | 請求開始 (RINIT) | 已移除 |
magic_quotes_runtime | file_get_contents、exec、SQL 查詢結果 | 執行時 | 已移除 |
magic_quotes_sybase | 用 '' 代替 \' 轉義 | 同 gpc | 已移除 |
核心問題:全域自動轉義導致雙重轉義、資料污染、可移植性差。現代 PHP 禁止使用,需在程式碼層面顯式處理。
核心轉義函數對照
| 函數 | 用途 | 轉義字符 | 逆向函數 | 適用場景 |
addslashes() | 字串通用轉義 | ' " \ NUL | stripslashes() | 非資料庫字串處理 |
htmlspecialchars() | HTML 實體編碼 | & < > " ' (ENT_QUOTES) | htmlspecialchars_decode() | 輸出到 HTML 頁面 |
htmlentities() | 所有可轉實體字符 | 含國際字符 | html_entity_decode() | 完整 HTML 實體化 |
mysqli_real_escape_string() | MySQL 字串轉義 | 依連線字符集 | 無 | 舊式 SQL 拼接 (不推薦) |
PDO::quote() | PDO 通用轉義 | 依驅動 | 無 | PDO 拼接 (不推薦) |
現代最佳實踐:預備語句 (Prepared Statements)
// PDO - 推薦標準
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', $user, $pass);
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? AND status = ?');
$stmt->execute([$email, $status]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 參數綁定類型控制
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
輸出端防 XSS:語境感知編碼
$input = '';
// HTML 內文
echo htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
// 輸出:<script>alert(1)</script>
// HTML 屬性值
echo '';
// JavaScript 字串
echo '';
// json_encode 自動處理 JS 字串轉義
// URL 參數
echo 'link';
// CSS 值 (需專用庫)
echo '';
資料流向轉義策略
- 輸入驗證:型別、長度、格式、白名單 (非轉義)
- 儲存層:預備語句參數化,完全隔離代碼與數據
- 業務邏輯:保持原始數據,不做轉義
- 輸出層:根據輸出語境 (HTML/JS/URL/CSS) 做語境編碼
常見錯誤模式
- ❌ 入庫前
addslashes,輸出時 htmlspecialchars → 雙重轉義
- ❌ 信任
magic_quotes_gpc 自動處理
- ❌ 直接拼接 SQL:
"WHERE name = '$name'"
- ❌ 輸出 JS 時用
htmlspecialchars 而非 json_encode
- ✅ 統一使用 PDO 預備語句 + 輸出時語境編碼