【Web安全】一次性搞懂 CSRF 漏洞原理/检测/防御

往期文章:

【Web安全】一次性搞懂 ReDOS 漏洞原理/检测/防御

【Web安全】一次性搞懂 XSS 漏洞原理/检测/防御

1 漏洞本质

CSRF 的本质是:攻击者利用浏览器对已认证用户的信任机制,在用户不知情时伪造合法请求执行非授权操作。

本质解读

  1. 信任关系滥用
    利用浏览器(不限于PC)自动发送凭证(Cookie)的机制

  2. 请求伪造
    通过恶意页面构造合法格式的请求:

    <img src="https://xxx.com/abc?t=attacker&a=1000000">
    
  3. 无感知攻击
    用户无需任何主动交互即可触发攻击

2 攻击原理

在这里插入图片描述
攻击条件

  1. 用户已登录存在 CSRF 漏洞的网站。

  2. 用户需要被诱导打开攻击者构造的恶意网站/恶意链接(可能是网址、图片、短信、音视频)。

3 风险场景

  1. 从用户行为角度解析
    通常认为 CSRF 攻击是攻击者利用受害者已登录状态诱导其点击链接。但从用户行为角度看,用户在多个标签页同时打开不同网站,且部分网站处于登录状态时,若在一个标签页中访问了恶意网站,该恶意网站可能利用其他标签页中已登录网站的 Cookie 等身份信息发起 CSRF 攻击。这意味着用户的多标签页浏览习惯在某些情况下会增加 CSRF 攻击风险。

  2. 与业务逻辑结合解析
    CSRF 漏洞不仅存在于常见的转账、修改密码等操作中,还可能与特定业务逻辑紧密相关。例如在一些项目管理系统中,若添加项目成员功能存在 CSRF 漏洞,攻击者可诱导管理员添加恶意成员,进而获取项目相关信息或权限,这种与特定业务结合的 CSRF 攻击往往更隐蔽,不易被常规安全检测发现。

本人工作中遇到的 CSRF 漏洞以第二种业务场景为主。

4 防御原则

“服务端必须验证每个状态变更请求的合法性,不能仅依赖浏览器自动提交的凭证”

  • 前端框架职责:安全传输 CSRF Token
  • 后端框架职责:严格验证请求来源和 Token 有效性
  • 架构师职责:为敏感操作启用二次验证

5 官方框架

5.1 前端框架

5.1.1 Vue 安全指南

HTTP security vulnerabilities, such as cross-site request forgery (CSRF/XSRF) and cross-site script inclusion (XSSI), are primarily addressed on the backend, so they aren’t a concern of Vue’s. However, it’s still a good idea to communicate with your backend team to learn how to best interact with their API, e.g., by submitting CSRF tokens with form submissions.

Vue 对 CSRF 的基本立场

CSRF 属于后端负责解决的安全范畴,而非 Vue 框架的职责。Vue 在前端的安全防护中,更侧重于 XSS(跨站脚本攻击)等与 DOM 渲染相关的风险,而 CSRF 的防护需要前后端协同完成。

5.1.2 Angular 安全手册

默认实现

  • 通过拦截器从名为XSRF-TOKEN的Cookie中读取令牌,并将其设置为请求头X-XSRF-TOKEN
  • 仅对相对URL的变更请求(如POST)发送该头,GET/HEAD或绝对URL请求不发送。

工作流程

  • 服务器在页面加载或首次GET请求时,向客户端设置XSRF-TOKEN Cookie(需包含唯一用户令牌)。
  • 后续请求中,服务器验证Cookie与X-XSRF-TOKEN头是否一致,确保请求来自合法客户端。

自定义配置

若后端使用不同的 Cookie 或请求头名称,可通过HttpClientXsrfModule.withOptions()配置:

// app.config.ts 示例
import { HttpClientXsrfModule } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    importProvidersFrom(HttpClientModule),
    importProvidersFrom(
      HttpClientXsrfModule.withOptions({
        cookieName: 'My-Xsrf-Cookie',  // 自定义Cookie名称
        headerName: 'My-Xsrf-Header'    // 自定义请求头名称
      })
    )
  ]
};

关键注意事项

  1. 服务器端配合:Angular 仅实现客户端防护,服务器必须设置 Cookie 并验证请求头,否则防护失效。
  2. 多应用隔离:同一域名下的多个 Angular 应用需设置唯一 Cookie 名称,避免令牌冲突。

5.1.3 React 官方文档

React 官方文档 中没有关于 CSRF 相关说明,因此官方默认还是由后端来看护。

5.2 后端框架

5.2.1 Spring Security (Java)

官方说明

CSRF防护概述

Spring Security 默认对 POST 等不安全的 HTTP 方法提供 CSRF 防护,无需额外代码。其防护通过CsrfFilter实现,该过滤器包CsrfTokenRepositoryCsrfTokenRequestHandler等组件,处理流程包括判断请求是否需要 CSRF 防护、加载并验证 Token、处理访问拒绝异常等。

CsrfToken管理

  1. 存储方式
    • 会话存储:默认使用HttpSessionCsrfTokenRepository将Token存储在会话中,可通过配置显式指定。
    • Cookie存储:使用CookieCsrfTokenRepository将Token存储在Cookie中,默认Cookie名为XSRF-TOKEN,可配置HttpOnly属性,若JavaScript需要读取Cookie,需将HttpOnly设为false。
  2. 自定义存储(个人推荐):可实现CsrfTokenRepository接口,自定义Token存储位置。

5.2.2 Django (Python)

如何使用 Django 提供的 CSRF 防护功能

CSRF 防护基础配置

  • 中间件启用:CSRF 中间件django.middleware.csrf.CsrfViewMiddleware默认激活,若覆盖MIDDLEWARE配置,需确保该中间件排在处理视图中间件之前。
  • 模板标签使用:在内部 URL 的 POST 表单模板中,需使用{% csrf_token %}标签,且视图需使用RequestContextrender()函数等以保证标签正常工作。

AJAX 请求的 CSRF 防护

  • 获取令牌
    • CSRF_USE_SESSIONSCSRF_COOKIE_HTTPONLYFalse时,从csrftoken cookie 中获取令牌,可通过自定义getCookie函数或 JavaScript Cookie 库获取。
    • CSRF_USE_SESSIONSCSRF_COOKIE_HTTPONLYTrue时,在模板中通过{% csrf_token %}生成令牌,再从 DOM 中读取。
  • 设置令牌:在 AJAX 请求中,通过设置X-CSRFToken请求头传递令牌,如使用fetch()时设置headers: {'X-CSRFToken': csrftoken},并确保mode: 'same-origin'

其他场景下的 CSRF 防护

  • Jinja2 模板:Django 的 Jinja2 模板后端在上下文中添加{{ csrf_input }},相当于 Django 模板语言中的{% csrf_token %}
  • 装饰器使用:可使用csrf_protect()装饰器对特定视图进行保护,不建议单独使用,可与中间件同时使用。

5.2.3 Express.js (Node.js)

官方CSURF已经不再维护,该中间件存在大量安全漏洞报告,不建议继续使用。

官方文档推荐使用 npm csrf 模块

5.3 框架防御机制对比

框架防御方案默认状态关键配置
Spring同步令牌模式启用CsrfTokenRepository
Django模板Token嵌入启用CsrfViewMiddleware
Express需手动集成 npm csrf 模块参考 csrf 模块
React需手动集成axios拦截器附加Header
Vue需手动集成全局请求拦截器
AngularHttpClientXsrfModule需手动配置withOptions()配置

6 防御措施

设置 Cookie 的各类安全配置虽能显著降低 CSRF(跨站请求伪造)风险,但无法完全防御。以下结合具体技术案例,从 Cookie 关键配置的防御效果、局限性及绕过方式展开分析:

6.1 Cookie核心安全配置的防御机制与案例

6.1.1 HttpOnly 属性:阻断前端JS获取Cookie

  • 防御原理:设置HttpOnly: true后,Cookie仅能通过HTTP请求传输,前端JS(如document.cookie)无法读取,防止XSS攻击间接利用 Cookie发起 CSRF。

  • 技术案例
    若网站未启用 HttpOnly,攻击者可在恶意页面中通过JS读取用户登录Cookie(如session_id),并构造伪造请求。例如:

    // 恶意页面JS代码
    const cookie = document.cookie;
    fetch('https://xxx.com/transfer?a=1000&t=attacker', {
      method: 'POST',
      headers: {'Content-Type': 'application/x-www-form-urlencoded'},
      body: `cookie=${cookie}`
    });
    

    启用 HttpOnly 后,document.cookie无法获取目标 Cookie,上述攻击失效。

  • 局限性
    仅防御 XSS 间接利用 Cookie 的场景,无法阻止攻击者直接利用用户浏览器自动携带的 Cookie 发起 CSRF(如通过图片、表单等静默提交请求)。

6.1.2 SameSite 属性:限制跨站请求携带 Cookie

  • 防御原理:通过SameSite属性控制Cookie是否在跨站请求中发送,可选值:

    • Strict:仅允许同站请求携带Cookie;
    • Lax:允许部分跨站请求(如GET方法的顶级导航)携带Cookie;
    • None:允许所有跨站请求携带Cookie(需配合Secure属性)。
  • 案例

    • 场景1:转账操作 CSRF
      用户已登录银行网站a.com,攻击者在b.com构造转账表单:

      <form action="https://a.com/abc" method="post">
        <input type="hidden" name="amount" value="1000">
        <input type="hidden" name="to" value="attacker">
        <button type="submit">点击领取福利</button>
      </form>
      

      若银行网站 Cookie 设置SameSite: Strict,用户在b.com提交表单时,浏览器不会携带a.com的Cookie,攻击失败

    • 场景2:GET请求跨站导航
      若Cookie设置SameSite: Lax,用户点击b.com的链接https://a.com/xxx?amount=1000&to=attacker时,浏览器会携带Cookie(因属于GET方法的顶级导航),此时仍可能触发CSRF。

  • 局限性

    • Lax模式无法防御GET请求的跨站导航攻击;
    • None模式需配合Secure属性,但仍可能因用户浏览器兼容性问题(如旧版本浏览器不支持SameSite)导致防御失效。

6.1.3 Secure属性:仅允许HTTPS传输Cookie

  • 防御原理:设置Secure: true后,Cookie仅在HTTPS连接中传输,防止中间人攻击在HTTP信道中窃取Cookie。
  • 技术案例
    若用户通过HTTP访问网站,攻击者可在公共 WiFi 环境中拦截请求,获取 Cookie 并伪造 CSRF 请求。启用 Secure 后,浏览器仅在HTTPS 连接下发送 Cookie,降低窃密风险。
  • 局限性
    无法防御 HTTPS 环境下的 CSRF 攻击(如攻击者通过HTTPS站点发起跨站请求),仅能保障Cookie传输过程的安全性。

6.1.4 Domain / Path 属性:限制 Cookie 作用域

  • 防御原理
    • Domain指定Cookie可被哪些域名访问(如Domain: example.com限制仅example.com及其子域名可使用);
    • Path指定Cookie可被哪些路径访问(如Path: /admin限制仅example.com/admin路径可使用)。
  • 技术案例
    若网站a.example.com的Cookie设置Domain: example.com,则子域名b.example.com可访问该Cookie,攻击者可能通过b.example.com发起 CSRF。若正确设置Domain: a.example.com,则可限制Cookie仅在当前子域名生效。
  • 局限性
    若Domain设置过宽(如根域名example.com),可能导致子域名漏洞被利用;Path配置错误也可能扩大Cookie作用域。

6.2 Cookie 配置无法完全防御 CSRF 的核心原因

6.2.1 浏览器自动携带 Cookie 的机制未被根本改变

  • 即使启用SameSite: Strict,仍存在例外场景:
    • 用户在新标签页手动输入目标URL(如https://a.com/transfer?amount=1000),浏览器会携带Cookie,攻击者可通过社会工程学诱导用户执行;
    • 跨站POST请求可通过iframe+表单自动提交绕过SameSite限制(需用户主动点击,但仍存在风险)。

6.2.2 与业务逻辑结合的 CSRF 漏洞无法仅靠 Cookie 防御

  • 案例:某论坛允许用户通过GET请求修改头像(如https://forum.com/change-avatar?url=恶意图片链接),攻击者可构造图片链接:

    <img src="https://xxx.com/change-avatar?url=https://attacker.com/xxx.jpg">
    

若用户已登录论坛且Cookie未设置SameSite: Strict,浏览器会携带Cookie发送请求,导致头像被篡改。此时需配合后端 CSRF 令牌(如Token)验证,而非仅依赖Cookie配置。

6.2.3 用户行为与浏览器兼容性引入的风险

  • 用户可能禁用SameSite属性(如旧版浏览器默认不支持),或通过插件修改浏览器行为,导致Cookie配置失效;
  • 跨站请求若通过第三方资源(如 CDN、广告框架)发起,可能绕过部分Cookie限制。

6.3 完整防御 CSRF 的综合方案(Cookie 配置+其他措施)

6.3.1 Cookie配置最佳实践(推荐)

  • 组合使用:HttpOnly: true + SameSite: Strict + Secure: true + 精准Domain/Path

  • 示例:

    Set-Cookie: session_id=12345; HttpOnly; SameSite=Strict; Secure; Domain=example.com; Path=/
    

6.3.2 后端 CSRF 令牌 Token 机制(推荐)

  • 使用 token 的本质是增强请求参数的随机性 ,不被攻击者轻易猜测到请求的参数值,因此 token 的生成需要使用安全随机数

客户端令牌 token 通常作为一种身份标识,由服务器端生成的一串字符串,当第一次登录后,服务器生成一个 token 返回给客户端,以后客户端只需带上 token 来请求数据即可,无需再次带上用户名和密码。

  • 如果来自浏览器请求中的 token 值与服务器发送给用户的 token 不匹配,或者请求中 token 不存在,则拒绝该请求。

  • 在《华为Web开发规范》中要求: “CSRF Token 存储在自定义请求头或集成框架的 session 中,推荐使用自定义请求头存储”。

6.3.3 Referer / Origin 头验证(辅助)

  • 检查请求的RefererOrigin字段是否来自可信域名。例如:

    # Django后端示例
    def transfer(request):
        referer = request.META.get('HTTP_REFERER', '')
        if not referer.startswith('https://xxx.com'):
            return HttpResponseForbidden("非法请求")
        # 处理转账逻辑
    
  • 局限性:Referer可被用户或代理服务器修改,仅作辅助防御。

6.4 防御结论

Cookie 的安全配置( HttpOnly / SameSite / Secure 等)是防御 CSRF 的重要环节,但无法单独实现完全防护。其核心原因在于:

  1. Cookie 配置仅能控制浏览器是否携带 Cookie,无法阻止用户主动发起的跨站请求;
  2. 业务逻辑层的漏洞(如GET请求执行敏感操作)需结合后端令牌机制防御;
  3. 浏览器兼容性与用户行为差异可能绕过部分Cookie限制。

最佳实践:以“Cookie 安全配置+后端 CSRF 令牌”为核心,辅以 Referer 验证、接口幂等性设计等多层防御,才能有效降低 CSRF 风险。

7 测试方法

7.1 Burp Suite(工具)

使用 Repeater 模块:

  • 手动修改 / 删除 CSRF 令牌,测试服务器响应。
  • 测试不同令牌值(空值、过期值),验证防御逻辑。

7.2 防御机制验证

检查敏感操作是否仅允许 POST

Cookie 安全属性测试

  • SameSite 属性:
    用浏览器开发者工具检查 Cookie 是否设置SameSite=Strict。
    在不同源页面中触发请求,观察 Cookie 是否被携带。
    在这里插入图片描述

  • HttpOnly 属性:
    用浏览器开发者工具检查 Cookie 是否勾选HttpOnly。
    在这里插入图片描述

请求头验证

  • Referer 检查:
    用工具修改请求的Referer头,验证服务器是否拒绝非法来源的请求。

  • Origin 检查:
    对跨域请求,验证服务器是否验证Origin头与目标域名一致。

本文是「Web安全基础」系列的第 3 篇,点击专栏导航查看全部系列内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

介一笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值