当你的网站用户反馈说,在评论框输入一段代码后,页面突然弹出了奇怪的广告,甚至跳转到钓鱼网站,这时你很可能已经遭遇了XSS(跨站脚本)攻击。XSS攻击的本质是攻击者将恶意脚本注入到网页中,当其他用户访问时,脚本在浏览器中执行,窃取Cookie、会话令牌或篡改页面内容。防止XSS攻击最基础也最有效的两个手段就是输入过滤和输出编码。
输入过滤:第一道防线,但并非万能
输入过滤是指在数据进入应用之前,对用户提交的内容进行校验和清洗,移除或转义可能构成脚本的字符。常见的做法包括:
- 白名单校验:只允许特定格式的输入,例如邮箱、电话号码、纯数字等。对于富文本输入,可使用白名单标签和属性,如只允许
<b>、<i>等,而拒绝<script>、<iframe>等。 - 黑名单过滤:移除或转义
<script>、javascript:、onerror等危险关键词。但黑名单容易被绕过,例如大小写混写、编码变体(如<script>)等,因此不推荐作为唯一手段。
实际操作中,输入过滤通常在服务端进行,因为客户端过滤可以被绕过。例如,在Node.js中,可以使用 validator 库进行白名单校验,或使用 xss 库进行过滤。但要注意,输入过滤并不能完全消除XSS风险,因为某些上下文(如URL、CSS)中,即使过滤了HTML标签,仍可能注入恶意代码。
输出编码:第二道防线,更可靠
输出编码是在数据输出到HTML页面时,根据上下文对特殊字符进行转义,使其被浏览器当作纯文本处理,而不是可执行的代码。与输入过滤不同,输出编码是防御XSS的关键,它确保即使输入包含恶意代码,也无法在浏览器中执行。
不同上下文需要不同的编码方式:
- HTML实体编码:在HTML标签内容或属性值中,将
&、<、>、"、'等转义为实体。例如,<script>变为<script>,浏览器会显示为文本。 - JavaScript编码:在
<script>块或事件属性中,需要对字符串进行JavaScript转义,如将<转义为<。 - URL编码:在URL参数中,需要对特殊字符进行百分号编码。
- CSS编码:在样式属性中,对特殊字符进行转义。
在开发中,应使用各语言提供的安全编码函数,例如PHP的 htmlspecialchars(),Java的 StringEscapeUtils.escapeHtml(),JavaScript的 textContent 属性等。同时,模板引擎如Vue、React默认会进行输出编码,但要注意使用 v-html 或 dangerouslySetInnerHTML 时会绕过编码,需谨慎使用。
结合Cloudflare防护:边缘层的补充
除了在应用层实施输入过滤和输出编码,还可以借助CDN或云防护服务在边缘层拦截XSS攻击。例如,Cloudflare的WAF(Web应用防火墙)可以检测并阻止常见的XSS攻击载荷。Cloudflare的DDoS防护也能在攻击流量到达源站前进行缓解,虽然DDoS主要针对可用性,但部分XSS攻击可能伴随大规模流量。Cloudflare的缓存和Worker服务也能提供额外的安全层:通过缓存静态内容,减少源站压力;通过Worker在边缘执行自定义逻辑,可对请求进行实时过滤或重写响应。
例如,在Cloudflare Workers中,你可以编写一个简单的中间件,对所有响应中的用户输入进行编码,或者拦截包含可疑 <script> 的请求。Worker运行在边缘节点,能够快速响应,并且无需修改源站代码。但要注意,边缘过滤并不能替代应用层的输出编码,因为攻击可能通过其他方式绕过,且Worker逻辑也需要谨慎设计。
常见误区与失败条件
在实施XSS防护时,开发者常犯以下错误:
- 只依赖输入过滤:如上所述,输入过滤可能被绕过,且无法覆盖所有上下文。
- 在客户端进行过滤:客户端代码可被攻击者修改,过滤无效。
- 使用不安全的API:如使用
innerHTML而非textContent,或使用eval()执行用户输入。 - 忽略上下文:对HTML实体编码了,但未对JavaScript上下文编码,导致在
<script>中仍可执行。
失败条件包括:攻击者通过编码变体绕过黑名单;在富文本编辑器中,未正确使用白名单;在URL中注入 javascript: 协议;在CSS中使用 expression() 等。此外,如果应用从数据库读取数据并直接输出,即使输入时过滤了,存储型XSS仍可能发生,因此输出编码必须在每次输出时执行。
总结
防止XSS攻击,输入过滤和输出编码是相辅相成的两道防线。输入过滤应作为辅助,输出编码才是根本。开发者应遵循“编码一切输出”的原则,根据上下文选择正确的编码方式,并避免使用危险的API。同时,借助Cloudflare等云服务,可以在边缘层增加防护,但不应过度依赖。通过多层防护,才能有效降低XSS攻击的风险。
参考资料
延伸阅读
