addslashes() 曾是早期 PHP 防 SQL 注入的「萬靈丹」,但隨著資料庫驅動演進,**現代最佳實務已轉向參數化查詢(Prepared Statements)**。本文詳細說明 addslashes() 機制、侷限性、歷史誤區,並給出當前專案應採用的防注入標準流程。
在**單引號 '、雙引號 \"、反斜線 \、NULL 字元**前加上反斜線跳脫。
$str = "Is your name O'reilly?";
echo addslashes($str); // Is your name O\'reilly?
反函數:stripslashes() 移除跳脫反斜線。
| 問題 | 說明 |
|---|---|
| 字元集敏感 | GBK/Big5 等多位元組編碼下,0xbf27 等位元組序列可能繞過單引號跳脫(著名「寬字節注入」) |
| 驅動差異 | MySQL/MariaDB/PostgreSQL/SQLite 對跳脫規則不同,addslashes 非通用 |
| 非 SQL 場景 | 僅處理引號/反斜線,無法防禦 LIKE 萬用字元 %、_ 注入 |
| 雙重跳脫 | 若資料已含跳脫字元,再次 addslashes 會導致資料污染 |
PHP < 5.4 曾有 magic_quotes_gpc=On 自動對 $_GET/POST/COOKIE 執行 addslashes,**已於 PHP 5.4 移除**。舊專案升級時務必檢查是否殘留 stripslashes() 邏輯導致資料遺失。
<?php
// PDO 範例(推薦)
$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();
// MySQLi 範例
$mysqli = new mysqli('127.0.0.1', 'user', 'pass', 'test');
$stmt = $mysqli->prepare('SELECT * FROM users WHERE email = ? AND status = ?');
$stmt->bind_param('si', $email, 1);
$stmt->execute();
$result = $stmt->get_result()->fetch_assoc();
filter_var($email, FILTER_VALIDATE_EMAIL))。SELECT/INSERT/UPDATE,拒絕 DROP/ALTER。display_errors,記錄至日誌。ModSecurity 規則集。echo "var msg = '" . addslashes($msg) . "';"(更推薦 json_encode())。mysqli_real_escape_string()(針對 MySQL 連線字元集)作為權宜之計。| 函數 | 用途 | 跳脫字元 |
|---|---|---|
| addslashes | SQL/字串常量跳脫 | ' " \ NULL |
| htmlspecialchars | HTML 輸出防 XSS | < > & " ' (含 ENT_QUOTES) |
| json_encode | JS/JSON 安全輸出 | 完整 Unicode 跳脫 |
**總結**:新專案**全面採用參數化查詢**,addslashes() 退役為歷史教材;維護舊代碼時,識別其侷限並逐步重構為預處理語句。