CSRF (跨站请求伪造)
约 3 分钟阅读
最后更新: 2026-09-01
什么是 CSRF (跨站请求伪造)
CSRF (Cross-Site Request Forgery) 是一种攻击手法,使用户的浏览器向已认证的 Web 站点发送非用户本意的请求。攻击者准备一个陷阱网页或邮件,受害者一旦查看,其浏览器就会自动向目标站点发送恶意请求。
浏览器会自动附加与请求目标域名绑定的 Cookie,而不论该请求由哪个页面发起,因此如果受害者处于登录状态,攻击者的请求也会被作为已认证请求处理。密码修改、转账、邮箱地址变更、商品购买等改变状态的操作都会成为攻击目标。
攻击原理与具体示例
CSRF 攻击按以下流程执行。
- 受害者登录目标站点 (例如网上银行),会话 Cookie 保存在浏览器中
- 受害者查看攻击者准备的陷阱页面 (通过邮件链接、论坛帖子等)
- 陷阱页面中嵌入的 HTML 或 JavaScript 从受害者的浏览器向目标站点发送请求
- 浏览器自动附加会话 Cookie,目标站点将其作为合法请求处理
例如,如果转账功能的设计是由 POST /transfer 从请求体中接收收款方和金额,攻击者只需在陷阱页面中嵌入自动提交的表单,就能从受害者账户执行转账。若把表单放在屏幕外的 iframe 中并用 JavaScript 提交,受害者甚至不会察觉操作已经发生。
虽然容易与 XSS 混淆,但 CSRF 的根本区别在于它是从受害者的浏览器伪造合法请求,而非在受害者的浏览器中执行攻击者的脚本。
防御措施的实现
CSRF 防御的核心是引入验证请求是否基于合法用户意图的机制。
CSRF 令牌
服务器在显示表单时生成随机令牌并嵌入隐藏字段。表单提交时验证令牌是否匹配,从而拒绝来自外部站点的伪造请求。这种方式称为同步令牌模式 (Synchronizer Token Pattern),令牌对每个会话是唯一的,并且由于同源策略,外部站点的 JavaScript 无法读取它。但如果站点存在 XSS,令牌本身也会被窃取,因此 CSRF 令牌只有与 XSS 对策结合才能发挥作用。如果不希望在服务器端保存令牌,可以采用双重提交 Cookie 模式,把带签名的值同时放入 Cookie 和请求中进行比对。
SameSite Cookie 属性
通过设置 Cookie 的 SameSite 属性,可以控制跨站请求是否附加 Cookie。
SameSite=Strict:不为来自外部站点的任何请求附加 Cookie。最安全,但通过外部链接访问时也无法保持登录状态SameSite=Lax:仅为顶级导航 (链接点击) 的 GET 请求附加 Cookie。由于不为 POST 请求附加 Cookie,可以防止大多数 CSRF 攻击。但在未指定该属性、由浏览器按默认值应用 Lax 的情况下,规则更为宽松,Cookie 设置后 2 分钟内的 POST 请求仍会附加 CookieSameSite=None:跨站请求也附加 Cookie。必须与Secure属性配合使用
SameSite 属性作为多层防御有效,但单靠它并不构成 CSRF 对策。同站判定以可注册域名为单位,因此 app.example.com 发行的 Cookie 在来自 other.example.com 的请求中也被视为同站,子域名被劫持或共享域名上的第三方内容都会成为绕过途径。此外,与外部服务集成时不得不指定 SameSite=None 的 Cookie,以及不应用该默认值的旧版浏览器和嵌入式浏览器依然存在,因此请务必与令牌验证配合使用。
其他防御措施
- Origin / Referer 头验证:确认请求来源是否为本站。但由于隐私设置或代理,Referer 可能不会被发送
- 要求自定义头:由于 HTML 表单无法附加任意请求头,要求
X-Requested-With这类自定义头即可拦截通过表单发出的伪造请求。跨站的 JavaScript 若要附加此类头部,需要 CORS 预检,但如果在启用Access-Control-Allow-Credentials的同时放宽允许的来源,这一防御就不再成立,因此允许的来源应控制在最小范围 - 与安全头的职责划分:CSP 和
X-Frame-Options并不是阻止 CSRF 本身的机制。CSRF 应通过 Cookie 的附加条件和请求来源验证来防御,而安全头则用于封堵 XSS 和点击劫持这些不同的攻击路径
框架内置的 CSRF 防护
许多服务器端 Web 框架都内置提供 CSRF 防护功能。
- Django:
CsrfViewMiddleware默认启用,并验证{% csrf_token %}模板标签所嵌入的令牌。在 HTTPS 下还会将Origin头与CSRF_TRUSTED_ORIGINS进行比对,因此也能应对经由子域名的攻击 - Ruby on Rails:新创建的应用中
config.action_controller.default_protect_from_forgery处于启用状态,因此会自动生成和验证 CSRF 令牌。如需显式指定,可写protect_from_forgery with: :exception - Spring Security:对 POST 等非安全 HTTP 方法默认启用 CSRF 保护。使用与 Thymeleaf 或 JSP 的集成时,
CsrfToken会被自动嵌入表单 - Next.js / SPA:在使用 Cookie 认证的 API 路由中,应结合 SameSite Cookie 与
Origin头验证。也可考虑通过 WAF 进行额外保护
如果禁用框架的 CSRF 保护,请确认替代措施已可靠实现。在 API 端点排除 CSRF 保护时,前提是切换到 Bearer Token 认证,不使用基于 Cookie 的认证。
常见误解
- GET 请求不会发生 CSRF
- 如果通过 GET 请求实现了状态变更操作 (删除、设置变更等),就会成为 CSRF 的目标。仅通过在 img 标签的 src 属性中设置 URL 就能发送 GET 请求,因此状态变更必须使用 POST/PUT/DELETE 实现。即使 <code>SameSite=Lax</code> 成为默认值,Lax 仍将 GET 视为安全方法而放行,因此通过 GET 变更状态的实现无法依靠浏览器的默认设置得到保护。
- 使用 HTTPS 就能防止 CSRF
- HTTPS 提供通信加密和防篡改功能,但与 CSRF 无关。CSRF 是从合法用户的浏览器发送合法请求的攻击,即使通信被加密,攻击仍然成立。