必须过滤script标签和on事件属性,否则用户输入直接进dom会引发xss;前端用dompurify.sanitize()默认移除script、iframe及on*属性,后端须二次净化并显式配置协议白名单,富文本渲染需隔离于sandbox iframe且配合csp nonce策略。

HTML输入内容必须过滤script和on事件属性
不做过滤的用户输入直接进DOM,等于给XSS开绿灯。哪怕只渲染一条评论、一个昵称或搜索关键词,只要没处理,<script></script>或onclick就能触发执行。
- 前端用
DOMPurify.sanitize()是最轻量且可靠的方案,它默认移除<script></script>、<iframe></iframe>、所有on*属性(如onerror)、javascript:伪协议 - 后端不能只靠前端过滤——必须二次净化,推荐
jsoup(Java)或bleach(Python),它们按白名单控制标签/属性,比正则更防绕过 - 别信“我只允许p、span、a”,如果
<a href="javascript:alert(1)"></a>能过,说明白名单没禁掉危险协议,必须显式配置allowed_protocols=['https', 'http']
富文本渲染必须隔离在sandbox iframe中
允许用户发带格式的内容(比如编辑器输出),就绝不能用innerHTML直接插入主页面。即使做了净化,仍可能被新浏览器特性或0day绕过。
- 把渲染结果放进
<iframe sandbox="allow-scripts allow-same-origin"></iframe>,但务必去掉allow-top-navigation和allow-popups - iframe src设为
blob:URL或独立子域(如preview.example.com),避免共享主站cookie和localStorage - 父子通信必须用
postMessage,且在接收端验证event.origin和event.source,否则沙箱形同虚设
CSP策略不能只写default-src 'self'
Content-Security-Policy是最后一道防线,但很多团队只配个default-src 'self'就以为万事大吉——这根本挡不住内联脚本或动态eval。
- 禁用
'unsafe-inline'和'unsafe-eval',改用nonce:服务端生成随机值,写入<script nonce="abc123"></script>,并在CSP头中声明script-src 'nonce-abc123' - 图片、字体等资源要单独放开,比如
img-src https: data:,避免因CSP拦截导致页面残缺 - 开发期开启
report-uri或report-to,收集违规行为,而不是等被黑了才看到日志
语义结构错误会直接破坏无障碍合规
WCAG 2.1 AA级要求不是“建议”,而是法律风险点。屏幕阅读器依赖<main></main>、<header></header>等标签建立导航树,写错等于主动拒斥残障用户。
-
<main></main>必须且只能出现一次,且不能被<div>或<code><section></section>包裹——否则隐式role="main"丢失,键盘用户无法Ctrl+Alt+O跳转 - 标题层级断裂(如
<h1></h1>后直接<h4></h4>)比没写标签更糟,会导致阅读器把整页当平铺文本读,丧失结构感知 <table>必须有<code><caption></caption>,<th>必须带<code>scope或headers绑定,否则数据行列关系对辅助技术完全不可见 真实项目里,安全与合规的交界处最易被忽略:一个没加scope的<th>不会报错,但会让视障用户在表格里彻底迷失;一段漏掉<code>nonce的内联脚本可能几个月都运行正常,直到某次浏览器更新启用新解析规则才暴露。防御性实践不是堆砌工具,而是把每个HTML标签当作契约来履行。











