HTML转义是防止浏览器将字面量误解析为标签或属性的安全机制,关键在于匹配上下文(HTML/XML/URL/纯文本);innerHTML中写<仍被解析,是因为未正确转义或转义后又被二次解析。

HTML 转义不是“把字符变好看”,而是防止浏览器把你的字面量误当成标签或属性解析——只要涉及动态插入字符串到 DOM,就必须明确当前上下文是 HTML、XML、URL 还是纯文本。
innerHTML 里写 < 为什么还是被当标签解析?
因为你没转义,或者转义了但又用错了方式。浏览器在解析 innerHTML 时,会先做 HTML 解析,再渲染;所以如果你直接赋值 el.innerHTML = 'a ,<code> 这段会被当作一个未闭合的标签开始,后续结构全乱。
- 正确做法:提前把
替换成 <code><,>替换成>,&替换成&,双引号/单引号也按需处理 - 错误示范:
el.innerHTML = 'x < y'.replace(/, '<')—— 只处理了,漏掉 <code>&,而<本身含&,若原始字符串已有实体,可能叠成 - 更稳的方式是用 DOM 方法兜底:
const div = document.createElement('div'); div.textContent = rawStr; return div.innerHTML;,它自动覆盖全部 5 个关键字符
PHP 里用 htmlspecialchars() 为什么单引号没转?
默认行为只转双引号,不碰单引号——这是历史兼容设计,不是 bug。如果你把字符串插进 value='xxx' 这类单引号属性里,没转单引号就等于留了个 XSS 缝隙。
- 必须显式传
ENT_QUOTES标志:htmlspecialchars($str, ENT_QUOTES, 'UTF-8') - 别依赖默认编码,
UTF-8必须写全,否则某些多字节字符可能截断或乱码 -
ENT_HTML5在 PHP 7.4+ 更稳妥,它支持如'这类 HTML5 实体,比老式'更可读
Python 的 html.unescape() 和 xml.sax.saxutils.escape() 能混用吗?
不能。前者是“反转义”,把 < 变回 ;后者是“正向转义”,把 <code> 变成 <code><。名字像,方向相反,用途完全不同。
立即学习“前端免费学习笔记(深入)”;
- 前端 JS 传来的
到后端,要用 <code>html.unescape()恢复原意(比如存数据库前) - 后端生成 XML 响应(如 RSS),必须用
xml.sax.saxutils.escape(),因为 XML 解析器比 HTML 严格得多,<在 XML 里不算合法转义,得用 -
html.escape()是 Python 3.2+ 的标准方案,比手写正则或调 sax 更轻量,且默认处理全部 5 字符:html.escape("a
React/Vue 里 v-html 或 dangerouslySetInnerHTML 怎么安全用?
它们绕过框架默认转义,等于把控制权交还给浏览器 HTML 解析器——输入即执行,没有中间层过滤。你必须确保内容来自白名单来源,或已由服务端做过结构化清洗。
- 绝对不要把用户输入 +
v-html直接拼一起,哪怕你前端做了escapeHTML(),服务端仍要再做一次 - 富文本场景推荐用专用库(如 DOMPurify),它基于 HTML 规范做白名单标签/属性过滤,比单纯字符替换可靠得多
- 如果只是显示代码片段,优先用
textContent或{`<div>`}(React 字符串字面量),而不是硬上 <code>dangerouslySetInnerHTML最易被忽略的一点:同一段字符串,在 HTML 页面里用
textContent是安全的,在发给后端的 XML 请求体里却必须再做一次转义——因为两者的解析上下文完全不同,不能靠“一次转义,到处通用”来省事。



















