PHP 提供多種轉義函數,針對不同輸出場景 (SQL、HTML、Shell、JSON) 有專用函數。混用或誤用會導致安全漏洞或資料損壞。本文詳細對比核心函數差異、適用場景與最佳實踐。
| 函數 | 用途 | 轉義字元 | 反轉義 | 適用場景 |
|---|---|---|---|---|
addslashes() | 通用字串轉義 | ' " \ NUL | stripslashes() | 無專用 DB 驅動時的 SQL (不推薦) |
htmlspecialchars() | HTML 輸出 | & " ' < > | htmlspecialchars_decode() | 所有輸出到 HTML 的變數 (防 XSS) |
htmlentities() | HTML 完整實體化 | 所有可轉實體字元 | html_entity_decode() | 需完整實體編碼的 XML/HTML 匯出 |
mysql_real_escape_string() | MySQL 專用 SQL | \x00 \n \r \ ' " \x1a | 自動 (資料庫層) | 舊版 mysql_* (已移除),改用 PDO/MySQLi |
strip_tags() | 移除 HTML/PHP 標籤 | 移除而非轉義 | 無 | 簡單內容過濾 (不安全,易繞過) |
$str = "O'Reilly";
echo addslashes($str); // O\'Reilly
// ⚠️ 問題:
// 1. 不支援多位元組字元集 (GBK 等) 導致注入繞過
// 2. PHP 5.3 前 magic_quotes_gpc 自動開啟會導致雙重轉義
// 3. 已被 PDO/MySQLi 參數化查詢取代
<?php
$input = '';
$safe = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
// 輸出:<script>alert("XSS")</script>
$input2 = "Tom & Jerry";
$safe2 = htmlspecialchars($input2); // 預設 ENT_COMPAT 不轉單引號
// 輸出:Tom & Jerry
// 旗標說明:
// ENT_COMPAT 預設,只轉雙引號
// ENT_QUOTES 轉單雙引號 (推薦)
// ENT_NOQUOTES 不轉引號
// ENT_HTML5 HTML5 模式 (PHP 5.4+)
$str = '中文 © 2024';
echo htmlentities($str, ENT_QUOTES, 'UTF-8');
// 輸出:中文 © 2024 (中文通常不轉,視 charset 而定)
// 差異:htmlentities 會轉換所有有實體對應的字元
// htmlspecialchars 只轉 & " ' < > 五個字元
<?php
// PDO (推薦)
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');
$stmt->execute([':email' => $email, ':status' => 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();
$result = $stmt->get_result();
// 參數化查詢優勢:
// 1. 徹底分離代碼與資料,免疫 SQL 注入
// 2. 自動處理編碼、轉義、類型轉換
// 3. 可重複執行提升效能
// 4. 程式碼更清晰
$html = '正常段落
';
echo strip_tags($html);
// 輸出:正常段落 alert(1) alert(2) (屬性事件處理器未被移除!)
// 允許特定標籤
echo strip_tags($html, ''); // 保留 p, b, i
| 場景 | 正確處理 | 錯誤示範 |
|---|---|---|
| 輸出到 HTML 內容 | htmlspecialchars($var, ENT_QUOTES, 'UTF-8') | 直接 echo $var |
| 輸出到 HTML 屬性值 | htmlspecialchars($var, ENT_QUOTES, 'UTF-8') | 無引號包裹屬性值 |
| 輸出到 JavaScript 字串 | json_encode($var) (自動處理引號、換行、Unicode) | 手動 addslashes |
| 輸出到 URL 參數 | urlencode($var) / rawurlencode() | 直接拼接 |
| 輸出到 Shell 命令 | escapeshellarg($var) / escapeshellcmd() | 直接拼接命令 |
| SQL 查詢 | PDO/MySQLi 參數化查詢 | addslashes / 字串拼接 |
| JSON 輸出 | json_encode($var, JSON_UNESCAPED_UNICODE) | 手動拼接 JSON |
1. SQL 唯一正解:參數化查詢 (PDO/MySQLi),絕不手動轉義
2. HTML 輸出預設:htmlspecialchars($var, ENT_QUOTES, 'UTF-8')
3. JavaScript 輸出:json_encode() 自動處理所有邊界情況
4. 不要信任 strip_tags() 防 XSS,屬性事件處理器、CSS expression 等可繞過
5. 統一編碼 UTF-8:資料庫、連線、PHP 檔案、HTML meta、HTTP Header 全鏈路 UTF-8
6. 推薦使用 HTML Purifier 處理富文本內容 (允許部分 HTML 標籤的場景)