` 被視為標籤,`&` 用於實體引用。若用戶輸入 `` 直接輸出,瀏覽器會執行腳本 (XSS)。
屬性上下文:`` 中的 `"` 會截斷屬性值。
JavaScript 上下文:字符串中 `\n`、`\`、`'`、`"` 需轉義。
URL 參數:`&`、`=`、`#`、`%`、`空格` 需百分號編碼。
二、HTML 字符實體分類大全
1. 必須轉義的核心字符 (XML/HTML 5 核心)
| 字符 | 實體名稱 | 實體編號 (十進制) | 實體編號 (十六進制) | 說明 |
| < | < | < | < | 小於號,標籤開始 |
| > | > | > | > | 大於號,標籤結束 |
| & | & | & | & | 和號,實體開始符 |
| " | " | " | " | 雙引號,屬性值界定 |
| ' | ' (XML/HTML5) | ' | ' | 單引號,屬性值界定 |
原則:在 HTML 文本節點與屬性值中,**務必**將以上 5 字符替換為實體。現代框架 (React, Vue, Thymeleaf) 默認自動轉義。
2. 空白與排版實體
| 顯示 | 實體名稱 | 編號 | 用途 |
| | |   | 不換行空格 (最常用) |
| |   |   | 半個中文字符寬度 (en space) |
| |   |   | 一個中文字符寬度 (em space) |
| |   |   | 窄空白 (thin space) |
| | ‌ | ‌ | 零寬非連接器 |
| | ‍ | ‍ | 零寬連接器 (Emoji 合成用) |
| / 
 | | 換行符 (HTML 中白間折疊,需 `` 或 CSS `white-space: pre`) |
3. 國際化與符號實體 (常用精選)
| 字符 | 實體名稱 | 編號 | 字符 | 實體名稱 | 編號 |
| © | © | © | ® | ® | ® |
| ™ | ™ | ™ | € | € | € |
| × | × | × | ÷ | ÷ | ÷ |
| § | § | § | ¶ | ¶ | ¶ |
| • | • | • | … | … | … |
| ← | ← | ← | → | → | → |
| ↑ | ↑ | ↑ | ↓ | ↓ | ↓ |
| ″ | ″ | ″ | ′ | ′ | ′ |
| ″ | ″ | ″ | ′ | ′ | ′ |
4. 數學與希臘字母 (精選)
| 字符 | 實體名稱 | 編號 | 字符 | 實體名稱 | 編號 |
| ∀ | ∀ | ∀ | ∃ | ∃ | ∃ |
| ∈ | ∈ | ∈ | ∉ | ∉ | ∉ |
| ∑ | ∑ | ∑ | ∏ | ∏ | ∏ |
| √ | √ | √ | ∞ | ∞ | ∞ |
| ≈ | ≈ | ≈ | ≠ | ≠ | ≠ |
| ≤ | ≤ | ≤ | ≥ | ≥ | ≥ |
| ⊂ | ⊂ | ⊂ | ⊃ | ⊃ | ⊃ |
| α | α | α | β | β | β |
| γ | γ | γ | δ | δ | δ |
| π | π | π | σ | σ | σ |
| Ω | Ω | Ω | ω | ω | ω |
三、Java 實現:HTML 轉義與反轉義
1. 手動實現 (教學用)
public static String escapeHtml(String input) {
if (input == null) return "";
StringBuilder sb = new StringBuilder(input.length() + 16);
for (char c : input.toCharArray()) {
switch (c) {
case '<': sb.append("<"); break;
case '>': sb.append(">"); break;
case '&': sb.append("&"); break;
case '"': sb.append("""); break;
case '\'': sb.append("'"); break; // HTML5 支援 '
default:
if (c > 0x7E || c < 0x20) { // 非可打印 ASCII,可選轉數字實體
sb.append("").append((int) c).append(";");
} else {
sb.append(c);
}
}
}
return sb.toString();
}
2. 生產級庫 (推薦)
- Apache Commons Text:`StringEscapeUtils.escapeHtml4(input)` / `unescapeHtml4(escaped)`
- OWASP Java Encoder:`Encode.forHtml(input)` (專為 XSS 防護設計,效能極高)
- Spring Framework:`HtmlUtils.htmlEscape(input)` / `HtmlUtils.htmlUnescape(input)`
- Thymeleaf:模板中默認 `${var}` 自動轉義,`${#strings.escapeHtml(var)}` 手動調用。
3. Maven 依賴
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.11.0</version>
</dependency>
<!-- OWASP Encoder -->
<dependency>
<groupId>org.owasp.encoder</groupId>
<artifactId>encoder</artifactId>
<version>1.2.3</version>
</dependency>
四、JavaScript 實現
1. 瀏覽器原生 DOM 方法 (最安全、最簡單)
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text; // 瀏覽器自動轉義
return div.innerHTML;
}
function unescapeHtml(html) {
const div = document.createElement('div');
div.innerHTML = html;
return div.textContent || div.innerText;
}
2. 正則替換 (Node.js / 無 DOM 環境)
const escapeMap = {
'&': '&', '<': '<', '>': '>',
'"': '"', "'": ''', '/': '/' // / 針對 攻擊
};
function escapeHtml(str) {
return String(str).replace(/[&<>"'\/]/g, c => escapeMap[c]);
}
3. JavaScript 字符串轉義序列 (字面量中)
| 序列 | 字符 |
| \b | 退格 |
| \f | 換頁 |
| \n | 換行 |
| \r | 回車 |
| \t | Tab |
| \' | 單引號 |
| \" | 雙引號 |
| \\ | 反斜槓 |
| \xHH | Latin-1 字符 (2位十六進制) |
| \uHHHH | Unicode 字符 (4位十六進制) |
| \u{HHHHH} | Unicode 碼點 (ES2015+) |
五、PHP 實現
// HTML 轉義 (默認雙引號,ENT_QUOTES 含單引號)
$escaped = htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
// 反轉義
$original = htmlspecialchars_decode($escaped, ENT_QUOTES);
// 所有字符轉實體 (含多字節)
$allEntities = mb_convert_encoding($str, 'HTML-ENTITIES', 'UTF-8');
// 反向
$original = mb_convert_encoding($allEntities, 'UTF-8', 'HTML-ENTITIES');
// 全局輸出轉義 (配置 php.ini 或入口文件)
ini_set('default_charset', 'UTF-8');
// 針對特定輸出流自動轉義 (慎用,影響性能)
// ob_start('mb_output_handler'); // 需 mbstring
六、Python 實現
import html
escaped = html.escape(text, quote=True) # 含雙引號
original = html.unescape(escaped)
七、不同上下文的轉義策略總表
| 上下文 | 必須轉義字符 | 推薦方法 | 錯誤示範 |
| HTML 文本節點 | < > & | 框架自動 / OWASP Encoder | 手動拼接未轉義變量 |
| HTML 屬性值 | < > & " ' | 同上 + 確保屬性用雙引號包裹 | 屬性無引號或單引號未轉義 |
| JavaScript 字符串 | \ ' " < > / \n \r | JSON 序列化 (`JSON.stringify`) / JS 模板字面量 | 直接拼接變量到腳本 |
| URL 查詢參數 | & = # % 空格 非 ASCII | `encodeURIComponent()` / `URLSearchParams` | 手動拼接字符串 |
| SQL 語句 | ' " \ NUL | **參數化查詢 (PreparedStatement)** | 字符串拼接 SQL |
| Shell 命令 | 空格 $ ` \ " ' ; | & | 避免 `Runtime.exec`,用 `ProcessBuilder` 列表參數 | 字符串拼接命令 |
八、編碼轉換:字符串 ↔ Unicode 轉義 (\uXXXX)
Java 實現
public static String toUnicode(String str) {
StringBuilder sb = new StringBuilder();
for (char c : str.toCharArray()) {
if (c <= 0x7F) sb.append(c); // ASCII 保留
else sb.append(String.format("\\u%04x", (int) c));
}
return sb.toString();
}
public static String fromUnicode(String unicodeStr) {
// 簡單實現,實際需處理 \uXXXX 模式
return unicodeStr; // 建議用 Apache Commons Text: StringEscapeUtils.unescapeJava()
}
JavaScript 實現
function toUnicode(str) {
return [...str].map(c => c.charCodeAt(0) > 0x7F
? '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0')
: c).join('');
}
// 反向: 瀏覽器自動解析 \uXXXX 字面量,或用 JSON.parse('"' + str.replace(/"/g, '\\"') + '"')
九、總結:安全輸出黃金法則
- 永遠不要信任輸入:用戶輸入、數據庫讀取、第三方 API 返回、日志文件均視為非受信。
- 輸出時轉義,而非輸入時:存儲原始數據,根據輸出上下文 (HTML/JS/URL/SQL) 選擇對應編碼器。
- 使用成熟庫/框架內建機制:手寫轉義極易疏漏 (如遺漏 `/` 導致 `` 繞過)。
- 設置正確 Content-Type 與 Charset:`Content-Type: text/html; charset=UTF-8` 避免瀏覽器編碼猜測導致繞過。
- 啟用 CSP (Content Security Policy):作為纵深防禦,限制腳本來源、禁止內聯腳本。
字符轉義看似瑣碎,實則是 Web 安全的第一道防線。建立「上下文感知輸出編碼」的思維模型,比記住具體實體表格更重要。