直接用 innerhtml 插入未处理的用户数据会导致 dom-based xss,应优先使用 textcontent;若必须插入 html,需严格 html 编码或使用 dompurify、trusted types 等防护机制。

直接用 innerHTML 插入用户数据就是高危操作
只要把未处理的用户输入(比如 URL 参数、表单值、localStorage 读取内容)直接赋给 element.innerHTML,就等于给攻击者开了执行任意脚本的后门。浏览器会立刻解析其中的标签和事件属性,onclick、onerror、<script></script> 全部生效。
常见错误场景包括:
- 从
location.search解析出name后直接写进document.getElementById('welcome').innerHTML = name - 用
fetch拿到服务端返回的富文本,不做清洗就塞进div.innerHTML - 把用户昵称存进
localStorage,再读出来渲染时跳过编码
这些操作看似“只是显示”,但一旦输入含 <img src="1" onerror="alert(1)">,弹窗就出来了——而且不经过服务器,日志里完全没痕迹。
textContent 是最简单可靠的替代方案
如果只需要展示纯文本(比如用户名、搜索关键词、错误提示),textContent 能彻底避免 HTML 解析,浏览器把它当字符串原样插入,连尖括号都不解释。
实操建议:
- 替换所有非必要
innerHTML场景:用element.textContent = userInput替代element.innerHTML = userInput - 注意兼容性:
textContent在 IE9+ 完全支持,老版本可用innerText回退(但需注意它会触发重排) - 不要为了“保留换行”而妥协:用 CSS 的
white-space: pre-wrap配合textContent,比拼接 HTML 安全得多
真需要插入 HTML?必须走双重编码链
仅靠 JavaScript 编码(如 JSON.stringify() 或 encodeURI())无法防住 DOM-based XSS,因为 innerHTML 会先解码再解析。正确路径是:先 HTML 编码,再嵌入 JS 字符串上下文。
例如,要安全地将变量 untrusted 插入 HTML 片段:
const safeHtml = `<div class="msg">${escapeHtml(untrusted)}</div>`;
element.innerHTML = safeHtml;
其中 escapeHtml 必须实现为:
function escapeHtml(str) {
return str
.replace(/&/g, '&')
.replace(/, '/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
关键点:
- 不能只转义
和 <code>>:引号漏掉会导致title="xxx" onclick=...绕过 - 不要用正则全局替换
&最后——否则&会被二次转义成& - 第三方库如
DOMPurify.sanitize()可用于复杂富文本,但它不是银弹:默认配置可能放行onload,必须显式禁用危险属性
Trusted Types 是现代浏览器的硬性防护层
Chrome 83+、Edge 83+、Firefox 105+ 已支持 Trusted Types,它能从根本上拦截不安全的 DOM 写入调用。启用后,innerHTML、document.write 等 API 只接受经 TrustedHTML 类型包装的值,普通字符串直接抛错。
最小启用方式:
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'; trusted-types default">
配合 JS 使用:
const policy = trustedTypes.createPolicy('default', {
createHTML: (input) => escapeHtml(input)
});
element.innerHTML = policy.createHTML(untrusted);
注意:
- 它不解决旧代码问题,只对新调用生效;已有
innerHTML = ...会直接崩溃,必须逐个修复 - Node.js 环境或 SSR 场景下不生效,它纯属浏览器运行时机制
- 开发阶段开控制台就能看到
Failed to set an unsafe value报错,这是你该改代码的明确信号
真正棘手的从来不是“怎么写安全代码”,而是“怎么确认所有动态 DOM 更新路径都被覆盖”。URL 参数、hash 变更、WebSocket 消息、IndexedDB 读取——任何能进 JS 执行流的入口,都得按同一套规则过滤。漏掉一个,整个防线就形同虚设。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











