应优先用textcontent而非innerhtml渲染用户输入,因innerhtml会解析执行html/js导致xss;必须用innerhtml时须严格转义5个字符或采用dom构造法、dompurify等库净化,正则逃逸易失效。

直接结论:不用 innerHTML 渲染用户输入,优先用 textContent;必须用 innerHTML 时,只对 、<code>>、&、"、' 这5个字符做转义,且不能靠正则“修补”——要走 DOM 构造法或成熟库。
为什么不能用正则写 escapeHtml 函数
正则替换看似简单,但容易漏掉边界情况:比如 (十六进制编码)、<code>(十进制)、<code>(命名实体)都会被浏览器解析为 <code>;还有注释中藏标签、属性里嵌 JS(<code>onerror="alert(1)")、CSS 表达式等。正则根本覆盖不全,反而给人“已防护”的错觉。
实操建议:
- 永远别手写
escapeHtml正则版本 - 若需轻量转义,用 DOM 构造法(兼容性好、无依赖):
function htmlEscape(str) {<br> const el = document.createElement('div');<br> el.textContent = str;<br> return el.innerHTML;<br>} - 该方法天然处理所有 Unicode 编码变体,且不依赖字符串解析逻辑
什么时候必须用 innerHTML?怎么安全地用
常见场景包括:富文本编辑器输出、Markdown 渲染结果、服务端已净化的 HTML 片段。此时 textContent 会破坏格式,必须用 innerHTML,但绝不能跳过净化。
实操建议:
- 前端渲染前,必须调用
DOMPurify.sanitize(dirty),而不是自己“过滤 script 标签” - 配置白名单,例如只允许
p、br、strong、a,并限制a[href]协议为https?或/ - 禁止启用
FORBID_TAGS: ['script']这类黑名单模式——DOMPurify 默认就禁用高危标签,重点是显式声明ALLOWED_TAGS - 不要把
DOMPurify.sanitize结果再塞进eval、setTimeout或location.href,那等于绕过全部防护
服务端转义和前端转义不是二选一
前端转义(如 textContent)防的是“当前页面渲染环节”的 XSS;服务端转义(如 PHP 的 htmlspecialchars、Python 的 html.escape)防的是“模板直出时”被注入。两者职责不同,缺一不可。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
典型错误现象:
- 后端没转义,前端用
textContent看似安全,但若某天改用innerHTML就立刻中招 - 后端已转义,前端又用
DOMPurify多余处理,可能误杀合法实体(如©) - API 返回 JSON 中的字段(如
user.bio)未经过滤,前端直接拼进模板字符串:`<div>${bio}</div>`—— 模板字符串不会自动转义,等同于innerHTML
关键点:服务端对所有用户输入字段做 HTML 实体编码;前端对所有动态插入 DOM 的操作,按上下文选择 textContent / setAttribute / DOMPurify,不混用。
CSP 不是补丁,而是最后一道闸门
Content-Security-Policy 头无法阻止恶意 HTML 被插入,但它能阻止插入后的脚本执行、外部资源加载、内联事件触发。它不替代转义,但能让漏掉的漏洞无法利用。
实操建议:
- 至少设置:
default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self' - 绝对禁用:
'unsafe-inline'和'unsafe-eval'(除非你真在用new Function) - 如果必须支持内联脚本(如统计代码),用
nonce,且每次响应生成唯一值,不复用 -
report-uri或report-to必须配,否则你根本不知道 CSP 是否被绕过
最常被忽略的一点:CSP 对 javascript: 伪协议、data: URI、eval() 的拦截效果,取决于你是否在策略中显式禁止——默认不禁止。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










