防止 XSS(跨站腳本攻擊)的第一道防線是輸出轉義。PHP 提供 htmlspecialchars() 將特殊字元轉為 HTML 實體,與 htmlspecialchars_decode() 反向還原。本文詳細說明參數、編碼規則、常見陷阱與最佳實踐。
string htmlspecialchars(
string $string,
int $flags = ENT_COMPAT | ENT_HTML401,
string $encoding = 'UTF-8',
bool $double_encode = true
)
| 字元 | 實體 | 說明 |
|---|---|---|
| & | & | 和號 |
| " | " | 雙引號 |
| ' | ' | 單引號(需 ENT_QUOTES) |
| < | < | 小於號 |
| > | > | 大於號 |
| 常數 | 行為 |
|---|---|
| ENT_COMPAT | 預設。僅轉換雙引號,單引號不動。 |
| ENT_QUOTES | 雙引號與單引號皆轉換(推薦)。 |
| ENT_NOQUOTES | 引號皆不轉換。 |
| ENT_HTML401 | 預設。HTML 4.01 規則。 |
| ENT_HTML5 | HTML5 規則(支援更多實體)。 |
| ENT_SUBSTITUTE | 無效編碼字元以 U+FFFD 取代(而非空字串)。 |
| ENT_DISALLOWED | 文件類型不允許的字碼以 U+FFFD 取代。 |
常用:UTF-8(預設)、ISO-8859-1、GB2312、BIG5。務必與實際輸出頁面編碼一致,否則會產生亂碼或安全漏洞。
<?php
$str = "John & 'Adams'";
echo htmlspecialchars($str, ENT_COMPAT); // John & 'Adams'
echo htmlspecialchars($str, ENT_QUOTES); // John & 'Adams'
echo htmlspecialchars($str, ENT_NOQUOTES); // John & 'Adams'
// 瀏覽器顯示皆為:John & 'Adams'
// 檢視原始碼才見實體差異
?>
string htmlspecialchars_decode(
string $string,
int $flags = ENT_COMPAT
)
僅處理上表五種實體,flags 行為與 htmlspecialchars() 對應。
<?php
$str = "John & 'Adams'";
echo htmlspecialchars_decode($str); // John & 'Adams'
echo htmlspecialchars_decode($str, ENT_QUOTES); // John & 'Adams'
echo htmlspecialchars_decode($str, ENT_NOQUOTES); // John & 'Adams'
?>
double_encode=true 會將 & 再編碼為 &amp;。若輸入已含實體,設 false 或改用 htmlentities() 配合 ENT_SUBSTITUTE。htmlspecialchars($str, ENT_QUOTES, 'UTF-8') 指定 UTF-8,再由 iconv 轉輸出編碼。<script>、<style>、屬性值、URL 等語境需不同轉義策略(如 json_encode、urlencode)。default_charset 影響預設 encoding,建議顯式傳參避免驚喜。| 場景 | 推薦函數 | 關鍵參數 |
|---|---|---|
| HTML 文字內容輸出 | htmlspecialchars | ENT_QUOTES, 'UTF-8' |
| HTML 屬性值輸出 | htmlspecialchars | ENT_QUOTES, 'UTF-8' |
| 需保留現有實體 | htmlspecialchars | double_encode=false |
| 解碼用戶輸入的實體 | htmlspecialchars_decode | ENT_QUOTES |
| 完整 Unicode 實體轉換 | htmlentities | ENT_QUOTES | ENT_SUBSTITUTE |