核心原则是不信任用户输入,所有动态插入HTML必须经DOMPurify净化;优先用textContent替代innerHTML;必须渲染HTML时强制白名单配置sanitize;URL、API等隐性来源数据同样需按上下文处理;配合CSP构建兜底防线。

核心原则是:不信任任何用户输入,所有动态插入 HTML 的操作必须经过净化,不能靠简单替换或黑名单。
优先用 textContent 替代 innerHTML
只要内容不需要渲染格式,就别用 innerHTML。比如展示用户名、搜索关键词、错误提示等纯文本场景:
- ✅ 正确:
el.textContent = userInput;—— `<script>alert(1)</script>` 会原样显示,不会执行 - ❌ 危险:
el.innerHTML = userInput;—— 恶意脚本立即触发 - 注意:
innerText虽不执行脚本,但会受 CSS 隐藏影响,语义不如textContent稳定,推荐统一用后者
必须渲染 HTML 时,强制使用 DOMPurify
富文本、后台公告、评论区支持样式等场景无法避免 HTML,此时必须调用 DOMPurify.sanitize() 处理原始字符串,而不是“集成到编辑器里”或“监听 DOM 变化”:
- 获取原始 HTML:如
quill.root.innerHTML或tinymce.activeEditor.getContent() - 显式净化:
const cleanHtml = DOMPurify.sanitize(dirtyHtml, { ALLOWED_TAGS: ['p','br','strong','a'], ALLOWED_ATTR: ['href','src'] }); - 再赋值:
el.innerHTML = cleanHtml; - ⚠️ 关键配置:务必设置
ALLOWED_TAGS和ALLOWED_ATTR白名单;默认保留style和class属性,可能被用于 CSS 注入绕过
避开所有隐性执行入口
很多 XSS 不来自表单提交,而是 URL 参数、localStorage、API 响应等“看似安全”的来源:
立即学习“Java免费学习笔记(深入)”;
- URL 参数(
location.search、location.hash)直接拼进innerHTML是高危操作,应先校验格式或转义后改用textContent - JSON 接口返回的字段(如
data.title)不是“可信数据”,前端拿到后仍需按上下文处理:插 HTML 就净化,进 JS 字符串就JSON.stringify(),作 URL 就encodeURIComponent() - 禁用
document.write、eval()、setTimeout(string)、onerror=等事件属性写法;绑定事件一律用addEventListener
配合 CSP 构建兜底防线
DOMPurify 是主动防御,CSP 是被动拦截。即使某处漏掉净化,CSP 也能阻止脚本执行:
- 响应头中设置:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; - 禁止内联脚本:
script-src 'self'会拦住<script></script>和onclick="" - 禁止动态执行:
script-src 'self'同时禁用eval()和new Function() - 启用违规上报:
report-uri /csp-report,便于发现未覆盖的漏洞点



















