CSRF漏洞原理和进阶利用
CSRF漏洞原理和进阶利用
前言
最近打算重新学习下csrf和xss,这些top10漏洞原理其实深究起来也不简单,就当是巩固基础知识了。
csrf跨站请求伪造攻击,本质上是攻击者伪造请求并加以包装(可以是一个url,也可以是一个html页面),当受害者执行了攻击者伪造的请求会后携带已有的cookie到目标网站执行指定操作。
在正式学习之前,需要了解浏览器和服务端通信机制,以下几种概念是很重要的。
| 概念 | 描述 | 相关风险 | 防护措施 |
|---|---|---|---|
| Cookie | 浏览器存储的用户信息(如 session_id)用于保持登录状态,随请求自动发送。 |
攻击者可通过跨站请求利用用户的 cookie,执行未授权操作。 | 设置 SameSite 属性(Strict 或 Lax),防止跨站请求携带 cookie。 |
| Session | 服务器存储的关于用户的会话数据,用于标识和管理用户登录状态。 | 如果 session 未保护,可能被攻击者伪造请求。 | 使用强加密和 session token,并确保 session 生命周期短,避免长期有效。 |
| CSRF | 跨站请求伪造,攻击者诱使用户访问恶意站点,利用用户登录状态发起恶意请求。 | 攻击者可以伪造用户的请求,执行危险操作。 | 使用 CSRF token(防止恶意请求)、限制跨站请求带 cookie。 |
| Token | 随机生成的不可预测的字符串,作为请求的标识符,防止 CSRF 攻击。 | 如果 token 不验证,攻击者可伪造请求。 | 使用 CSRF token:每个请求携带不同的 token,且绑定用户 session。 |
| Referer | 浏览器请求头,指明请求来源的 URL,可以用来检测请求是否来自合法网站。 | 攻击者可能伪造请求头,绕过 referer 检查。 | 使用 SameSite cookie、严格的 CORS 策略、以及 CSRF token 来防止伪造请求。 |
| SameSite | Cookie 属性,用于限制跨站请求时是否携带 cookie。可能的值为 Strict、Lax、None。 |
默认情况下,跨站请求会带上用户的 cookie。 | 设置 SameSite=Strict 或 Lax,防止跨站点请求带 cookie。 |
| CORS | 跨源资源共享,控制不同源的请求是否被允许访问服务器资源。主要用于控制跨域请求,特别是 JavaScript 脚本发起的请求。 | 不受信任的站点可以发送跨站请求并操作用户数据。 | 设置 CORS 策略,通过服务器的 Access-Control-Allow-Origin 头,限制仅允许特定来源的请求访问敏感数据。 |
| XSS | 跨站脚本攻击,通过注入恶意 JavaScript 代码窃取用户信息或执行未经授权的操作。 | 通过注入脚本,攻击者可以窃取 cookie 或绕过 CSRF 防护。 | 使用 内容安全策略(CSP),防止恶意脚本执行,并配合 CSRF token 防止跨站伪造请求。 |
3个前提条件
┌─────────────────────────────────────────────┐
│ 条件1:用户已登录目标网站,会话凭证有效 │
│ → 用户在目标网站完成登录,浏览器保存 Cookie/Session 等认证信息,且未过期、未注销 │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ 条件2:目标网站仅依赖 Cookie 验证身份 │
│ → 服务器未添加额外校验(如 CSRF Token、Referer 检查),请求参数可被攻击者完全预测 │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ 条件3:用户被诱导触发恶意请求 │
│ → 用户在登录状态下,访问攻击者控制的页面/链接,触发自动发送的恶意请求 │
└─────────────────────────────────────────────┘
靶场一:无防护的CSRF漏洞
靶场地址:
https://portswigger.net/web-security/csrf/lab-no-defenses
在注册登录账户和邮箱后,在更新email一栏抓包

观察抓到的数据包,发现唯一校验值就是cookie

那么攻击思路很简单,只需要伪造一个请求,使得用户在自己已经登陆的浏览器上访问我的数据包即可

利用bp的工具生成csrf的poc

随后复制到exploit server

保存后发送成功完成实验

、
靶场二:取决于请求方法的CSRF令牌验证
本实验室的电子邮件更改功能容易受到 CSRF 的攻击。它试图阻止 CSRF 攻击,但只对某些类型的请求应用防御。
靶场地址:
portswigger.net/web-security/csrf/lab-token-validation-depends-on-request-method
前面步骤还是一样,先抓到请求的数据包观察一手

发现这里存在一串csrf校验值,长度很长难以猜测,尝试修改该值看后端是否真正校验
结果发现无论删除还是修改都不行,看来是真的存在校验


既然POST请求会有校验,那GET请求呢?

修改为get请求包后成功执行,随后将参数删掉发现也能成功

说明对参数csrf的校验仅在POST提交时生效,那么同上将GET请求包生成csrf的poc即可


靶场三:csrf令牌验证取决于令牌是否存在
某些应用程序会在令牌存在时正确验证令牌,但如果令牌被省略则跳过验证。
在这种情况下,攻击者可以删除包含令牌的整个参数(而不仅仅是其值)以绕过验证并发起 CSRF 攻击
靶场地址:
portswigger.net/web-security/csrf/lab-token-validation-depends-on-token-being-present
抓取数据包,发现和上题目一样

尝试把csrf参数删掉,发现能成功修改,说明后端在没有接受到该参数的值时不进行令牌校验的

后续只需要把csrf删掉生成poc即可

靶场四:csrf令牌未绑定到用户会话
某些应用程序不会验证令牌是否与发出请求的用户属于同一会话。相反,应用程序维护它已发布的全局令牌池,并接受出现在该池中的任何令牌。
在这种情况下,攻击者可以使用自己的帐户登录应用程序,获取有效令牌,然后将该令牌提供给受害者用户进行 CSRF 攻击。
简而言之,cookie session和token未绑定. 可以使用自己的token修改别人的邮箱账号.
靶场地址:
portswigger.net/web-security/csrf/lab-token-not-tied-to-user-session
首先登录A邮箱抓取数据,发现无论是更改请求方法还是对csrf进行删改啥的都不行

也就是说对于令牌的鉴权一定是存在,但是接下来可以测试令牌有没有和session绑定,也A用户的令牌方B用户上能不能用
这是A用户第一次修改邮箱的包

这是第二次

这里发现session是固定的,但是令牌是随机动态生成的,也就是说session和csrf不是绑定的,那么就可以用自己的token伪造csrf请求
抓A的包

抓B的包

用A的token替换B的token,成功修改

后面生成poc并提交即可

靶场五:CSRF 令牌绑定到非会话 Cookie
这个把靶场四的区别就是靶场四的cookie未和csrf令牌绑定,靶场五的cookie和csrf令牌绑定了,但是绑定的是非会话cookie(csrfkey),而不是验证身份的session,所以攻击者需同时注入自己的 csrfKey 和对应的 csrf 令牌,使受害者请求中两者匹配。
靶场地址:
portswigger.net/web-security/csrf/lab-token-tied-to-non-session-cookie
登录抓包后发现像靶场四那样拿自己的csrf替换已经无法实现攻击了

也就是说csrf令牌已经和cookie绑定了。如果和验证身份的session绑定的话那么无法实现csrf,接下来试试同时替换csrfkey和csrf
A的数据包

B的数据包

同时替换csrfkey和csrf,发现成功攻击

接下来就是如何构造poc,同步注入非会话cookie和令牌
很显然像直接用bp设计csrf poc不可行因为cookie是存储在浏览器上的而不是请求体中
1 | |
通过在首页搜索发现,搜索东西时会带有csrfkey,那么可以通过url的set-cookie来注入

可以使用playload替换成自己的csrfkey

接下来在替换csrf令牌后生成poc

然后设置图片链接到搜索页面替换成我们的csrfkey,一旦对方打开网址,就会自动加载”这个图片”,简而言之相当于去访问了这个精心设计好的网址,而因为这并非一个图片,必然会报错,后面onerror就是报错后的动作,即提交表单.
1 | |
最终poc
1 | |
靶场六:CSRF 令牌只是从cookie 中复制一次
应用设计的 “双重提交” 防御本应是:服务器生成随机 CSRF 令牌,同时存入用户会话(服务器端)和客户端 Cookie,后续请求需同时携带 “会话绑定的令牌”(请求参数)和 “Cookie 令牌”,服务器校验两者是否匹配且与会话关联。
但靶场六的实现偷工减料:跳过服务器端会话绑定和令牌有效性校验,仅要求 “请求参数中的 csrf 值” 与 “Cookie 中的 csrf 值” 完全相同,哪怕这个值是攻击者自定义的,只要两者一致,服务器就认为请求合法。
靶场地址:
portswigger.net/web-security/csrf/lab-token-duplicated-in-cookie
抓取数据包,发现cookie中存在一个和请求体中一样的csrf令牌,也就是说令牌和cookie要对齐

随后同时修改csrf和cookie中的令牌,发现后端不能存在令牌池,也不会对其进行身份验证,只要二者对齐就能通过校验

那么攻击思路就有了,攻击者甚至不需要获取自己的有效令牌,可以随意构造csrf,同时设置cookie的csrf使得二者对齐即可
1 | |
靶场七:基于 Referer 的 CSRF 防御(Referer 验证取决于标头是否存在)
靶场七的核心漏洞是 CSRF 防御仅在Referer标头存在时生效,若标头缺失则直接跳过校验—— 本质是防御机制存在 “不安全回退”,攻击者可通过隐藏Referer标头,让跨域 CSRF 请求绕过校验。
靶场地址:
portswigger.net/web-security/csrf/lab-referer-validation-depends-on-header-being-present
Referer 标头的作用
Referer是浏览器自动携带的请求头,记录 “当前请求的来源页面 URL”(比如从 A 页面跳转到 B 页面,B 页面的请求会携带Referer: A页面URL)。靶场的防御逻辑:仅允许Referer为靶场自身域名的请求(同域请求),拒绝跨域Referer(如漏洞利用服务器域名)的请求。
首先抓取数据包,按照前面的直接生成poc的做法是不行的,我们可以验证一下
把referer随便改发现不对,说明后端有验证的

随后把referer那三行删掉发现可以修改

说明 CSRF 防御仅在Referer标头存在时生效,若标头缺失则直接跳过校验
后面构造poc时,我们需要浏览器自动删除,所以payload如下所示:(在head中加入无referer的选项)
1 | |
最终的poc
1 | |
靶场八:可绕过的 Referer 验证(验证逻辑缺陷)
服务器对 Referer 的验证逻辑存在严重缺陷 ——只要 Referer 字符串中包含目标域名片段(如web-security-academy.net),就认为是合法请求,而非严格验证完整的同源域名。
靶场地址:
发现这次删除和修改都无法成功

然后测试,不检查完整域名匹配,仅检查字符串是否包含目标域名片段

可以构造以下poc
1 | |
两大核心代码的原理与作用
1. history.pushState('', '', '/?靶场完整域名')
- 底层原理:浏览器
History API的前端本地操作,可在「不刷新页面、不跨域」的前提下,修改地址栏 URL 的查询参数(?后内容)。 - 核心作用:给 Referer 注入 “靶场域名片段”—— 修改后地址栏 URL 包含靶场完整域名(如
https://漏洞服务器/?0a32005704cca37a806103d700e500a8.web-security-academy.net),而浏览器默认将「地址栏 URL」作为 Referer 值,从而让 Referer 满足靶场 “含自身域名即合法” 的校验条件。 - 关键价值:不触发页面刷新、不暴露攻击痕迹,确保用户无感知,同时精准匹配靶场校验规则。
2. <meta name="referrer" content="unsafe-url">
- 底层原理:HTML 原生标签,用于控制浏览器发送请求时
Referer标头的 “发送规则”,content="unsafe-url"表示强制发送「完整 URL(协议 + 域名 + 路径 + 查询参数)」,即使是跨域请求也不截断任何部分。 - 核心作用:保障注入的靶场域名不丢失 —— 浏览器默认跨域请求会截断 Referer 的查询参数(仅保留 “协议 + 域名”),该标签强制完整发送 URL,确保
history.pushState注入的靶场域名能被靶场检测到。 - 关键价值:解决 “浏览器默认截断查询参数” 的问题,是
history.pushState生效的前提(无此标签,注入的靶场域名会被浏览器删除,校验失败)。
防护手段
| 防护方法 | 适用场景 | 核心价值 |
|---|---|---|
| CSRF Token | 所有有状态服务 | 通用核心防护 |
| SameSite Cookie | 所有场景 | 基础兜底,无开发成本 |
| 双重 Cookie 验证 | 无状态服务 / 同源场景 | 无需 Session,适配 JWT 架构 |
| 自定义请求头验证 | 跨域 / 前后端分离 | 拦截跨域伪造请求 |
| JWT 无状态防护 | 微服务 / 分布式系统 | 无状态,适配分布式架构 |
| Referer/Origin 验证 | 所有场景(补充) | 辅助拦截非法来源 |
| 二次验证(验证码) | 转账 / 改密码等高敏感操作 | 兜底防护,最彻底 |
| 频率限制 / IP 绑定 | 批量 CSRF 攻击防护 | 增加攻击成本,拦截异常请求 |