<?php $str = "O'Reilly"; echo addslashes($str); // O\'Reilly // 轉義字符: ' (單引號) " (雙引號) \ (反斜槿) NULL 字節
它僅做字符級轉義,不理解 SQL 語法上下文,不處理編碼、佔位符、多字節字符繞過 (GBK 编码注入) 等問題。
0xbf27 組合可繞過單引號轉義 (經典 %bf%27 攻擊)。% _)、表名/列名、ORDER BY 子句等無法用引號包裹,addslashes 無效。<?php
// PDO (推薦)
$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, 1]);
$user = $stmt->fetch();
// MySQLi
$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$stmt = $mysqli->prepare('SELECT * FROM users WHERE email = ?');
$stmt->bind_param('s', $email);
$stmt->execute();
原理:SQL 模板與數據分離發送給數據庫服務器,服務器解析模板生成執行計劃,再綁定參數執行,參數永遠不會被解析為 SQL 代碼。
<?php // HTML 上下文 echo htmlspecialchars($str, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); // JavaScript 上下文 echo json_encode($str); // 自動處理引號、換行、Unicode // Shell 命令上下文 $escaped = escapeshellarg($userInput);
若必須動態,嚴格白名單驗證:
$allowedTables = ['users', 'orders', 'logs']; $table = in_array($inputTable, $allowedTables) ? $inputTable : 'users'; $sql = "SELECT * FROM `$table` WHERE ..."; // 反引號包裹標識符
mysql_real_escape_string() 並設置連接字符集)。永遠使用參數化查詢 (PDO/MySQLi Prepared Statements) 防禦 SQL 注入。
addslashes()、mysql_real_escape_string() 皆為不推薦的拼接 SQL 時代產物,無法全面防禦現代注入向量。