HTML注入风险防范核心是禁用innerHTML拼接用户输入、严格校验a[href]等属性协议、正确配置DOMPurify白名单;必须用textContent替代innerHTML显示纯文本,渲染富文本时须经DOMPurify.sanitize()处理并显式声明ALLOWED_TAGS与ALLOWED_ATTRIBUTES。

直接用 innerHTML 拼接用户输入、未校验 a[href] 协议、漏配 DOMPurify 的 ALLOWED_ATTRIBUTES —— 这三处是当前最常被忽略、也最容易被绕过的漏洞入口。
检查所有 innerHTML 赋值点是否混入了用户可控数据
只要右侧值来自 URL 参数、表单字段、API 响应(如 res.data.content)、localStorage 或任何非硬编码字符串,就构成风险。浏览器会立即解析其中的 <script></script>、onerror、javascript: 等内容。
替代方案不是“加个正则替换”,而是改用 textContent 或 innerText;若必须渲染 HTML,须经 DOMPurify.sanitize() 处理,并确认配置了 ALLOWED_TAGS 和 ALLOWED_ATTRIBUTES 白名单。
常见错误现象:
立即学习“前端免费学习笔记(深入)”;
-
el.innerHTML = '<div>' + userInput + '</div>'—— 即使只含字母数字,攻击者仍可注入" onmouseover=alert(1)类属性 -
document.write(userInput)—— 已被禁用多年,但老项目里仍有残留,它会触发二次 HTML 解析,放大污染风险 - 使用
vue或react时误用v-html/dangerouslySetInnerHTML,且未对源数据做净化
验证所有带用户输入的属性是否限制协议与格式
a[href]、img[src]、iframe[src] 是高危属性,DOMPurify 默认不校验其值内容,只看是否在白名单中。哪怕你开了 ALLOWED_TAGS: ['a'],若没配 ADD_URI_SAFE_ATTR 并显式声明允许的协议前缀,href="javascript:fetch('/api/leak')" 依然能执行。
实操建议:
-
a[href]必须限定为https://、/、#开头,禁用javascript:、data:、vbscript: -
img[src]同样要禁用data:,且不能省略alt属性配置 —— 漏掉会导致 img 标签被当成空标签绕过校验 -
iframe即使不在ALLOWED_TAGS中,若sandbox被单独放进ALLOWED_ATTRIBUTES,反而会启用沙箱逃逸能力
排查 id / name 属性是否动态拼接用户输入
这不是 XSS,但会造成全局变量污染:比如 <input id="fetch"> 加载后,window.fetch 就变成该 DOM 节点,后续所有 fetch() 调用都会失败或静默异常。原生 JS 页面中极易中招,React/Vue 组件因作用域隔离不易暴露,但一旦用 el.setAttribute('id', userInput) 就等同于开放挂载点。
正确做法是始终对 id / name 做白名单校验(如仅允许字母数字下划线)或哈希化处理,绝不能直接拼接。
容易踩的坑:
- 评论系统里把用户昵称直接设为
id,昵称填console就覆盖window.console - 表单字段名用 URL 参数生成,参数传
length导致document.forms[0].length返回节点数而非数组长度 - 使用
eval()或Function()动态执行字符串,哪怕字符串来自内部配置,也可能被原型链污染间接控制
确认第三方库版本与 CSP 配置是否形成闭环防护
只更新 DOMPurify 版本不够,还要检查它是否被正确调用 —— 很多项目引入了但实际没用,或者用了旧版(如 v2.x),而新版(v3+)才默认开启更严格的属性过滤。
CSP(Content-Security-Policy)不能只写 script-src 'self',必须明确禁止内联脚本与 eval,并配合 base-uri 'none' 阻断 base 标签劫持。
关键点:
- CDN 引入的库需带完整版本号(如
https://cdn.jsdelivr.net/npm/dompurify@3.1.5/dist/purify.min.js),避免被中间人替换为恶意版本 - 启用 SRI(Subresource Integrity)校验,给
<script></script>标签加integrity属性,防止 CDN 投毒 - CSP 的
frame-ancestors必须设为'self'或明确域名,否则点击劫持(Clickjacking)无法防御
真正难防的不是复杂漏洞,而是那些看起来“只是显示一段文字”的地方 —— 比如一个搜索结果页的标题、一条评论的作者名、甚至页面底部的统计代码注入点。这些位置一旦放松校验,整个净化逻辑就形同虚设。



















