在线工具集

XSS / CSRF 完全防御指南

XSS 与 CSRF 是 Web 安全里出现频率最高、修复难度最低、却最容易被忽视的两类漏洞。它们都源于浏览器与服务端之间古老的信任假设——浏览器默认信任服务端返回的 HTML 与 JavaScript,服务端默认信任带着 Cookie 来的请求一定来自合法用户。一旦这层信任被攻击者钻空子,账号被盗、资金被转、数据被改写都会悄无声息地发生。本文从三大类 XSS(存储型、反射型、DOM 型)讲到 CSRF 的不同变体,再给出完整的防御组合:内容安全策略、输出转义、富文本净化、CSRF token、SameSite cookie、双提交模式。文末附上 OWASP Top 10 2025 的最新分类,帮你建立体系化的 Web 安全意识,让代码上线后能扛得住自动化扫描器与人工渗透测试。

一、XSS 三种形态:存储型、反射型、DOM 型

XSS 的全称是 Cross-Site Scripting,跨站脚本攻击,本质是把攻击者控制的脚本插入到受害者浏览器中执行。按照注入路径不同分三类。存储型 XSS 是最危险的一种:攻击者把恶意脚本写入数据库(评论、昵称、个人简介等字段),所有访问该页面的用户都会触发执行,杀伤面极大。2014 年 TweetDeck 那次蠕虫式 XSS 让数百万用户在毫无操作的情况下转发了攻击者的推文,就是典型的存储型。

反射型 XSS 通常出现在搜索结果页、错误提示页这种把 URL 参数原样回显的位置。攻击者构造一个带恶意脚本的链接,诱导受害者点击,脚本立刻执行。它需要受害者主动访问,所以传播性弱于存储型,但配合钓鱼邮件、QQ 群链接也能造成大规模感染。DOM 型 XSS 不经过服务端,恶意 payload 通过 location.hash、document.referrer 等客户端 API 进入页面,再被 innerHTML、document.write、eval 等接收器(sink)执行。这种 XSS 服务端日志看不到,扫描器也常常漏掉,单页应用尤其要警惕。

二、输入校验与输出转义:防御的两道闸门

XSS 防御的第一原则是区分数据与代码。所有用户提交的内容都应被视为字符串数据,绝不能让浏览器以代码身份执行。最便宜的防御就是输出转义。把字符串插入 HTML 文本节点时,至少要转义 &、<、>、"、' 五个字符;插入 HTML 属性时还要考虑无引号属性场景;插入 JavaScript 上下文要做 JS 转义;插入 URL 时要做 URL 编码。每个上下文有不同的转义规则,混用会出问题。

现代框架已经把转义做成默认行为。React 的 JSX、Vue 的 mustache、Svelte 的模板都默认对插值做 HTML 转义,只有显式调用 dangerouslySetInnerHTML、v-html、@html 才会绕过。这意味着项目里出现这些 API 的地方就是审计重点。如果必须输出富文本,使用 DOMPurify 走白名单净化,配置只允许安全的标签与属性,禁止 onerror、onload、javascript: 协议、style 中的 expression() 等危险语法。本站的 HTML 实体编码工具 可以帮助开发者快速验证转义结果。

三、CSP 内容安全策略:深度防御的最后一道墙

Content Security Policy(CSP)是浏览器层面的白名单机制,通过 HTTP 响应头告诉浏览器哪些来源的脚本、样式、图片可以加载,哪些必须拒绝。即便代码里漏掉了一处转义,攻击者注入的 <script> 也无法执行,因为它的来源不在白名单中。一个最小可用的严格 CSP 类似于:default-src 'self'; script-src 'nonce-RANDOM' 'strict-dynamic'; object-src 'none'; base-uri 'none'。

2026 年的最佳实践是 nonce-based + strict-dynamic 模式。每次请求生成 16 字节随机 nonce,模板渲染时把 nonce 注入到合法 <script> 标签上,CSP 头同时带上该 nonce。strict-dynamic 让被 nonce 信任的脚本动态加载的脚本也被信任,避免大量手动维护白名单。配套上线 Content-Security-Policy-Report-Only 一段时间收集真实违规数据,再逐步迁移到强制模式。CSP 不是银弹,但能让 XSS 漏洞从严重事故降级到日志告警。

四、CSRF 攻击原理与典型场景

CSRF 全称 Cross-Site Request Forgery,跨站请求伪造。它利用浏览器对 Cookie 的自动携带机制:受害者已经在 bank.com 登录,浏览器留有会话 Cookie;受害者访问攻击者站点 evil.com,evil.com 上的隐藏表单或图片标签向 bank.com/transfer 发起请求,浏览器自动带上 bank.com 的 Cookie,转账成功。受害者全程没有任何感知。

CSRF 的可怕之处在于不需要拿到 Cookie 内容,只要浏览器愿意带上。早期常见的攻击向量包括 <img src="bank.com/transfer?to=attacker&amount=1000">、隐藏表单 + auto-submit 脚本、伪装的二维码扫码登录链接。现代框架通常默认开启 CSRF 防护,但 SPA + 自定义 fetch 调用、移动端 WebView 嵌入第三方页面、跨域子域共享 Cookie 等场景仍频繁踩坑。CSRF 攻击的核心前提是受害者已登录目标站点,所以单点登录 SSO 体系下尤需警惕。

五、CSRF Token 与 SameSite Cookie:两种防御范式

经典 CSRF 防御方案是同步 token 模式:服务端为每个会话生成一个 CSRF token,渲染表单时嵌入隐藏字段,提交时校验 token 与会话绑定。攻击者位于不同源,无法读取页面也就无法获取 token。token 必须做到不可预测、与会话绑定、对每个用户唯一。框架如 Django、Spring Security、Rails 都内置实现,开发者只需开启中间件并在表单里输出 token 字段。

2026 年的新范式是 SameSite Cookie 属性。SameSite=Lax 让 Cookie 在跨站请求中除少数 top-level 导航外不被发送;SameSite=Strict 完全禁用跨站发送;SameSite=None 配合 Secure 才能跨站使用。Chrome 自 80 版起把未声明 SameSite 的 Cookie 默认按 Lax 处理,这一变化让大量历史 CSRF 漏洞自然失效。但浏览器兼容性、子域 same-site 判定、第三方支付回调的特殊路径仍可能让 SameSite 保护失效,所以高危操作建议同时启用 SameSite + token + Origin/Referer 校验,做到三层防御。

六、双提交 Cookie 模式与无状态服务

对于完全无状态的微服务,维护服务端 token 缓存成本高,于是出现了双提交 Cookie 模式(Double Submit Cookie)。具体流程:登录后服务端下发一个加密签名的 csrf_cookie;前端在每次请求时把 cookie 值复制到自定义请求头 X-CSRF-Token;服务端比对 cookie 与请求头是否一致并校验签名。攻击者位于不同源无法读取 cookie,自然也无法构造一致的请求头。

双提交模式的关键是 csrf_cookie 必须带签名或加密,否则攻击者可以通过子域脚本写入伪造 cookie 后再发起请求。另一个变体是 Origin/Referer 校验:服务端拿到请求后检查 Origin 头是否在白名单内,对绝大多数浏览器场景已足够。无状态架构推荐组合:SameSite=Lax + 双提交 token + Origin 校验,三者中任一失效都仍有兜底。本站的 JWT 最佳实践 一文讨论了无状态认证下的更多细节。

七、富文本场景的高阶防御

评论区、富文本编辑器、Markdown 渲染等场景必须允许用户输出 HTML,单纯转义会破坏体验。这种情况下白名单净化是唯一安全选项。DOMPurify 是 JavaScript 生态最成熟的 HTML 净化库,配置一次就能在前后端共用。最小可用配置只允许 p、br、strong、em、a、img、ul、ol、li、code、pre 等常见标签,attribute 严格限制 href、src、alt、title,并拒绝 javascript:、data: 等危险协议。

Markdown 场景要特别小心:marked、markdown-it 等解析器默认不会执行脚本,但允许嵌入原始 HTML,攻击者写一段 <script> 就能逃逸。正确做法是关闭 raw HTML 选项,或者解析后再过一遍 DOMPurify。SVG 同样危险,<svg><script> 是经典的 XSS 向量,上传 SVG 头像必须先做服务端净化或转换为光栅图。所有这些场景都该写入安全 review checklist,新功能上线前过一遍。

八、OWASP Top 10 2025 关键定位与团队落地

OWASP Top 10 是行业最权威的 Web 风险清单。2025 版做了几项调整:A01 仍是 Broken Access Control,A02 是加密失效,A03 把 XSS 与所有注入合并,CSRF 隐含在 A07 Identification and Authentication Failures 中。这意味着 XSS 与 CSRF 不再被视为孤立漏洞,而是与权限、认证、加密体系一起评估。开发团队应当把这些类别落到代码评审 checklist、自动化扫描规则、上线前安全测试用例三层。

团队落地建议:(1)框架默认开启转义、SameSite、CSP,禁止裸用 dangerouslySetInnerHTML;(2)CI 集成 ESLint 安全规则与 SAST 扫描,发现 innerHTML、eval、document.write 立即报错;(3)部署 WAF 作为最后一道挡板,对常见 XSS / CSRF payload 做模式匹配;(4)每季度做一次内部红蓝对抗,专门针对历史功能做回归式渗透;(5)建立 vulnerability disclosure policy,欢迎白帽提交,按风险等级评估奖励。安全不是一次性工程,而是持续运营。

常见问题

XSS 与 CSRF 的本质区别是什么?

XSS 是把恶意脚本注入到受害者浏览器中执行,攻击者借受害者的身份调用接口、读取数据;CSRF 是诱导受害者在已登录状态下访问攻击者构造的链接,浏览器自动携带 Cookie 触发危险操作。XSS 是代码注入问题,CSRF 是身份滥用问题。XSS 一旦得手通常能直接绕过 CSRF 防御,所以业界共识是先治 XSS,再补 CSRF。

CSP 头能完全防住 XSS 吗?

不能,但能大幅降低危害。严格模式 CSP(script-src 使用 nonce 或 strict-dynamic、不带 unsafe-inline)能挡住绝大多数注入脚本执行的尝试。剩余风险包括:旧浏览器不支持、CSP 报告漏报、攻击者利用 JSONP 端点或同源静态资源绕过。CSP 应作为深度防御的最后一层,前面仍要做好输入校验与输出转义。

SameSite=Lax 已经够了吗,还需要 CSRF token 吗?

对纯浏览器场景的 GET/POST 表单,SameSite=Lax 已经能挡住绝大多数跨站请求。但是支付、转账、删除等高危操作仍建议同时使用 CSRF token,原因有三:旧浏览器不支持 SameSite、第三方库可能用 method=GET 触发副作用、内部子域之间的请求可能仍被认定为 same-site。Lax + token 是 2026 年的稳妥做法。

DOMPurify 与手写转义哪个更靠谱?

处理富文本必须用 DOMPurify 这类成熟的 HTML 净化库。手写正则替换尖括号在面对嵌套引号、HTML 实体、SVG、MathML 时几乎必然漏。如果只是把字符串当纯文本插入,使用 textContent 或框架的默认转义即可,不需要任何库。原则是:纯文本走转义,富文本走白名单净化,绝不直接拼接 innerHTML。

OWASP Top 10 2025 还把 XSS 排第几?

在 OWASP Top 10 2025 中,XSS 已经被归并到 A03 Injection 大类下,与 SQL 注入、命令注入并列。CSRF 不再单独列项,但相关的会话与认证问题归入 A01 Broken Access Control 与 A07 Identification and Authentication Failures。这并不表示这些问题不重要,而是说明它们已被视为基础风险,所有应用都必须默认防御。

相关工具