innerhtml 直接插入用户输入最危险,因不转义且解析为 dom;需按上下文(文本、属性、script、url)分别转义;富文本须用白名单净化而非正则。

直接在 区域插入用户可控内容时,innerHTML 是最危险的入口点——它不加区分地解析所有字符串为 DOM,哪怕只是一段昵称或搜索关键词,都可能触发 <img src="x" onerror="alert(1)"> 这类 payload。
为什么 innerHTML + 用户输入 = 立即执行
浏览器看到 el.innerHTML = userInput,就认为你明确要渲染 HTML。它不会检查内容是否“可信”,也不会自动转义 —— 这不是 bug,是设计使然。
- 哪怕
userInput只含一个单引号('),拼进属性值里就可能闭合标签、注入事件处理器 -
textContent安全但无格式;insertAdjacentHTML和document.write同样危险,规则一致 - React 的
dangerouslySetInnerHTML、Vue 的v-html都是显式绕过框架默认防护,必须自行兜底
必须按上下文选转义方式,不能一招打天下
同一段用户数据,可能出现在 HTML 文本、属性值、<script></script> 内部、URL 参数四种位置 —— 每种都需要不同处理逻辑,混用等于无效。
- HTML 文本内容:用
he.escape()或html.escape()(Python)转义、<code>>、&、"、' - 作为
data-xxx属性值:仍属 HTML 属性上下文,需额外对双引号/单引号做转义,否则可能提前闭合 - 拼进
<script></script>标签内:必须用JSON.stringify()包裹,否则引号、反斜杠、换行都会破坏 JS 语法 - 构造 URL 查询参数:用
encodeURIComponent(),不是encodeURI();javascript:协议必须被拦截或拒绝
富文本场景下,白名单过滤比转义更可靠
当业务真需要保留 <b></b>、<ul></ul> 等标签(如评论区支持简单排版),仅靠转义会把合法标签也变成纯文本,此时必须用净化库做白名单控制。
- Node.js 推荐
sanitize-html,配置allowedTags: ['p', 'br', 'strong', 'em'],禁用所有事件属性(onerror、onclick) - Java 用
JSoup的Whitelist.relaxed().addTags(...),并强制校验href属性协议为http://、https://或/ - 前端可用
DOMPurify.sanitize(dirtyHTML),它会移除脚本、iframe、危险属性,并重写不安全的 URL - 切忌自己写正则过滤
<script></script>—— 绕过方式太多(大小写混淆、注释干扰、编码变形)
最易被忽略的是多上下文复用:同一个用户昵称,既显示在正文,又作为 data-uid,还传给 JS 函数作参数。这时候,一次转义解决不了问题 —— 必须在每个使用点,按该位置的语义做对应处理。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











