本文目录导读:

针对存储型XSS(Stored XSS)的拦截,核心思路是在数据进入数据库时进行消毒,并在输出到浏览器时进行转义,同时配合内容安全策略(CSP)作为最后一道防线,以下是具体的拦截方案:
输入过滤与消毒(数据入库前)
这是最关键的环节,目标是在用户提交数据时,移除或转义潜在的恶意脚本。
- 白名单策略:只允许特定的、安全的标签和属性(如
<b>、<i>、<a>且href只允许http/https协议),这是最推荐的方式。 - 黑名单过滤:过滤常见的XSS攻击载荷,如
<script>、onerror、javascript:等。但黑名单很容易被绕过(例如使用大小写、编码、<img src=x onerror=...>),仅作为辅助手段。 - 使用成熟的库:不要自己写正则过滤,使用经过验证的库:
- Java:使用
OWASP Java Encoder或Jsoup(Jsoup.clean()方法可以非常方便地只保留安全标签)。 - Python:使用
bleach库,它基于HTML5lib,能有效解析并清理恶意代码。 - PHP:使用
HTML Purifier。 - Node.js:使用
DOMPurify(可在服务端使用)。
- Java:使用
输出转义与编码(数据出库前)
即使输入过滤不完美,只要在输出时正确转义,攻击代码就不会被浏览器执行。
- 上下文敏感的编码:根据数据插入的位置选择不同的编码方式:
- HTML元素内容(如:
<div>用户输入</div>):对< > & " '进行HTML实体编码,大多数模板引擎(如Thymeleaf、Jinja2、Vue的)默认会做这件事。 - HTML属性(如:
<input value="用户输入">):对 、&、<、>进行编码。 - JavaScript环境(如:
<script>var x = "用户输入"</script>):必须对 、、、换行符等进行JavaScript字符串转义。 - URL参数(如:
<a href="用户输入">):使用URL编码(如encodeURIComponent())并只允许http/https协议(防止javascript:伪协议)。
- HTML元素内容(如:
内容安全策略(CSP)(兜底方案)
即使1和2都失效,CSP是阻止XSS执行的最强技术手段,通过在HTTP响应头中设置策略,浏览器会拒绝执行非白名单的脚本。
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline';
- 关键点:绝对不要允许
'unsafe-inline'和eval()(除非绝对必要),如果使用了内联脚本,应使用nonce或hash机制。 - 存储型XSS特别建议:对用户生成的内容(如评论区、帖子),考虑使用
sandbox属性或将其放在隔离的iframe中,限制其执行JavaScript的能力。
其他辅助措施
- HttpOnly Cookie:虽然不能阻止XSS注入,但可以防止攻击者通过
document.cookie窃取会话令牌,对所有会话Cookie必须设置HttpOnly。 - 设置
X-XSS-Protection: 0:现代浏览器已经废弃了老的XSS过滤器(XSS Auditor),该特性在Chrome中已被默认禁用,关闭它可以避免误阻断和新的绕过问题,防御应依赖CSP。 - 验证富文本内容:如果允许用户使用富文本编辑器(如TinyMCE、Quill),输出前需使用服务端的HTML清理库(如Java的
Jsoup、Python的bleach)再次清洗,因为编辑器本身的过滤很容易被通过请求篡改绕过。
四层防御体系
- 第0层(预防):输入校验,严格限制数据类型(如邮箱、数字长度),并拒绝不符合格式的数据。
- 第1层(核心):输入消毒,使用白名单库(如
Jsoup.clean())清理富文本内容。 - 第2层(必备):输出转义,根据上下文(HTML、属性、JS、URL)使用对应的编码函数。
- 第3层(兜底):CSP,仅允许执行受信任的脚本源,从根本上阻止恶意代码的执行。
最终建议:不要完全依赖输入过滤(因为总有未知的绕过方式),必须做好输出编码,并一定要加上CSP策略作为最后一道防线。