innerHTML赋值前必须转义,否则执行用户代码;优先用textContent;渲染HTML需DOMPurify净化;属性值拼接要按上下文转义;contenteditable属高危入口;CSP不能替代编码,须配合上下文敏感转义。

innerHTML 赋值前不转义等于直接执行用户代码
只要把未处理的用户输入塞进 element.innerHTML,就等同于把控制权交到攻击者手上。哪怕只含一个 或 <code>",都可能触发 <img src="x" onerror="alert(1)" alt="HTML安全防护:防范XSS攻击的结构安全规范" > 这类 payload。
常见错误现象:document.getElementById('comment').innerHTML = userInput 直接写入;服务端返回的 HTML 片段被前端无脑插入。
- 优先改用
element.textContent = userInput—— 纯文本渲染,浏览器完全不解析标签,零风险 - 必须渲染 HTML?先调用
DOMPurify.sanitize(dirtyHtml),再赋给innerHTML;别用正则删<script></script>,漏点就全崩 - 服务端若已对字段做了 HTML 实体编码(如
<div>hello</div>),前端不能再二次转义,否则显示为字面量
HTML 属性值拼接时引号与事件属性必须分开防
把用户数据插进 title、data-id、href 等属性,看似安全,实则极易被绕过。关键不是“有没有标签”,而是“是否破坏了属性结构”。
常见错误现象:<div title="{{ userInput }}"> 中输入 <code>" onclick="alert(1),最终变成 <div title="" onclick="alert(1)">,事件直接生效。<p><span>立即学习</span>“<a href="https://pan.quark.cn/s/cb6835dc7db1" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">前端免费学习笔记(深入)</a>”;</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill10828" title="财经HTML高端海报生成"><img
src="https://img.php.cn/upload/skill/001/246/273/6adaf20dbae7c507.jpg" alt="财经HTML高端海报生成" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill10828" title="财经HTML高端海报生成">财经HTML高端海报生成</a>
<p>复杂得财经如何变成清晰,适合手机看的海报?如何加上你的IP</p>
</div>
<a href="/xiazai/skill10828" title="财经HTML高端海报生成" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div>
<ul>
<li>双引号包围的属性(<code>title="xxx")必须转义 " → ";单引号包围(title='xxx')则转义 ' → '
href、src 类属性不能只做 HTML 转义,得先 encodeURIComponent() 再拼入,否则 javascript:alert(1) 仍可执行<button onclick="doSomething('{{ userInput }}')"></button> 这种写法——事件处理器内嵌用户数据,无解富文本场景下 contenteditable 是高危入口
contenteditable 让元素变成 HTML 编辑区,用户输入内容会被浏览器直接解析执行。它不像 <input> 那样天然隔离标签,是 XSS 的“绿色通道”。
常见错误现象:评论框、后台编辑器启用 contenteditable 后,用户输入 <img src="x" onerror="fetch('/api/steal'," body:document.cookie alt="HTML安全防护:防范XSS攻击的结构安全规范" >,Cookie 就被发走了。
- 禁用
innerHTML直接读取:用element.innerText或element.textContent获取纯文本内容 - 若需保留格式,读取后必须经
DOMPurify.sanitize()处理,且配置严格白名单(如禁用script、onerror、javascript:协议) - 服务端保存前仍需再次净化——前端净化可被绕过,后端才是最后一道防线
CSP 不是防护终点,而是兜底策略
加了 Content-Security-Policy: script-src 'self' 就以为万事大吉?错。CSP 挡不住 <img src="x" onerror="eval(atob('YWxlcnQoMSk='))" alt="HTML安全防护:防范XSS攻击的结构安全规范" > 这类利用现有脚本环境的攻击。
常见错误现象:只设 CSP、不处理输出编码;或把 'unsafe-inline' 当成兼容性妥协,放行所有内联事件和样式。
- CSP 必须配合上下文敏感的输出编码才有意义——它不替代转义,只限制执行范围
- 禁止
unsafe-inline和unsafe-eval;base-uri 'self'防止 base 标签劫持 - 启用
report-uri或report-to收集违规日志,实际观察哪些策略被绕过


















