HTML实体编码本身不防XSS,仅在特定上下文有效;必须转义<、>、&、"、'这五个字符才能拦住基础XSS,漏一即可能被绕过,且不同上下文(如属性、URL、JS)需匹配对应转义规则。

HTML实体编码本身不防XSS,它只在特定上下文里有效——用错地方、漏掉字符、忽略上下文,等于没做。
哪些字符必须转义才能拦住基础 XSS
只转义 <、>、&、"、' 这五个字符,才能覆盖绝大多数 HTML 文本内容场景。少一个都可能被绕过:
-
<→ 防标签起始(否则<script>直接解析) -
>→ 防标签闭合(否则破坏原有结构) -
&→ 防实体解析干扰(否则可还原为 <code><) -
"→ 双引号属性值中防闭合(title="<img src=x onerror=alert(1)>"会提前截断) -
'→ 单引号属性值中防闭合(data-id='{{ userInput }}'里漏转会导致事件注入)
注意:html.escape()(Python)、htmlspecialchars()(PHP 默认)都不转义单引号,StringEscapeUtils.escapeHtml4()(Java)不处理 Unicode 变体——这些默认行为在属性上下文中直接留缺口。
innerHTML 赋值前不做转义 = 主动执行 XSS
写 element.innerHTML = userInput 是最危险的常见操作。哪怕用户只输入 <img src=x onerror=alert(1)>,浏览器也会立刻执行。
立即学习“前端免费学习笔记(深入)”;
- 纯文本展示一律用
element.textContent = userInput,零风险,不解析任何 HTML - 真要渲染富文本(如评论带链接),必须先过
DOMPurify.sanitize(dirtyHtml),再赋给innerHTML - 绝对不要自己写正则删
<script>或过滤onerror——绕过方式太多,比如<scr<script>ipt>、<img alt="x" onerror=alert(1)>
属性值里插用户数据,光转义 < > 完全不够
把用户输入塞进 title="{{ userInput }}" 或 data-id="{{ userInput }}" 时,攻击者只要输入 " onclick="alert(1),最终生成的就是 title="" onclick="alert(1)",事件直接绑定成功。
- 双引号包裹的属性(
title="xxx")必须转义"→" - 单引号包裹的属性(
title='xxx')必须转义'→'或' - URL 类属性(
href、src)得先用encodeURIComponent()编码,再拼入带引号的属性值;不能只做 HTML 实体转义 - 禁止写
<button onclick="doSomething('{{ userInput }}')">——这是 JS 执行上下文,和 HTML 转义完全不是一回事
he 库 encode 的安全配置要点
he.encode() 看似简单,但默认行为在不同场景下风险各异:
- 默认对
<、>、&、"、'全部编码(依赖escapeMap),但需确认你用的是最新版,旧版可能漏' - 启用
useNamedReferences: true可输出<而非,更易读,但不影响安全性 - 严格模式(
strict: true)会拒绝无效 Unicode(如\uD800),防止畸形实体攻击,建议开启 - 解码时
he.decode()会验证实体完整性,拒绝不完整或畸形引用(如<少分号),这点比手写正则可靠得多
真正容易被忽略的,是上下文切换:同一段用户输入,可能同时出现在文本节点、双引号属性、单引号属性、URL 参数、JS 字符串里——每个位置都要用对应方式处理,混用就等于开门揖盗。



















