深入解析CSRF攻击:从攻击机制到多层次防护策略

CSRF

一、什么是CSRF

  1. CSRF(Cross-site request forgery)跨站请求伪造
    • 攻击者诱导受害者进入第三方网站 ,在第三方网站中,向被攻击网站 发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的

二、CSRF的攻击类型

GET类型的CSRF

  • 用户信任http://bank.com网站,并且保存登录状态
  • 诱导用户点击 图片<img src="http://bank.com/transfer?to=attacker&amount=1000">
  • http://bank.com网站误以为是用户自己发送的请求,并以用户的名义执行了攻击者自定义的操作,触发转账

POST类型的CSRF

  • 构造隐藏表单 ,利用JavaScript自动提交表单到目标网站

    html 复制代码
     <form action="http://bank.example/withdraw" method=POST>
        <input type="hidden" name="account" value="xiaoming" />
        <input type="hidden" name="amount" value="10000" />
        <input type="hidden" name="for" value="hacker" />
    </form>
    <script> document.forms[0].submit(); </script> 

链接类型的CSRF

  • 在论坛中发布的图片中嵌入**恶意链接,**或者以广告的形式诱导用户中招

    html 复制代码
      <a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank">
      重磅消息!!
      <a/>

三、防护策略

CSRF的特点与策略

复制代码
 ##### 特点

 * CSRF(通常)发生在第三方域名。
 * CSRF攻击者不能获取到Cookie等信息,只是使用。
复制代码
 ##### 对应策略

 * **阻止不明外域的访问**
   * 同源检测
   * Samesite Cookie
 * **提交时要求附加本域才能获取的信息**
   * CSRF Token
   * 双重Cookie验证

同源检测

复制代码
 ##### 原理

 * 检查请求的 `Referer` 或 `Origin` 头是否来自**合法域名**。

 * **注意** :部分浏览器可能不发送 `Referer`(如隐私模式),需结合其他防御手段。

   ```js
   // 后端
   app.post('/transfer', (req, res) => {
     const referer = req.get('Referer');
     const origin = req.get('Origin');
     const allowedDomains = ['https://your-legitimate-site.com'];

     if (!allowedDomains.includes(referer) && !allowedDomains.includes(origin)) {
       return res.status(403).send('Invalid request source');
     }
     // 处理合法请求
   });
   ```
  • Set-Cookie响应头新增Samesite属性,它用来标明这个 Cookie是个"同站 Cookie",同站Cookie只能作为第一方Cookie,不能作为第三方Cookie。

  • 假如淘宝网站用来识别用户登录与否的 Cookie 被设置成了 Samesite=Strict,那么用户从百度搜索页面甚至天猫页面的链接点击进入淘宝后,淘宝都不会是登录状态,因为淘宝的服务器不会接受到那个Cookie,其它网站发起的对淘宝的任意请求都不会带上那个 Cookie。

复制代码
 ##### Samesite 有以下属性值

 | 模式         | 行为                                                 | 适用场景                 |
 |:-----------|:---------------------------------------------------|:---------------------|
 | **Strict** | **完全禁止**跨站请求携带 Cookie(包括用户点击其他站点的链接跳转)             | 极高敏感操作(如支付、修改密码)     |
 | **Lax**    | 允许**安全的跨站** GET 请求(如导航到目标站的链接)携带 Cookie            | 大多数场景(浏览器默认行为)       |
 | **None**   | 允许**所有跨站请求** 携带 Cookie,但必须同时设置 `Secure` 属性(仅HTTPS) | 需要跨站 Cookie 的第三方集成场景 |

 ```js
 // 后端设置Cookie
 app.post('/api/login', (req, res) => {
     res.cookie('CSRF-TOKEN', crypto.randomBytes(32).toString('hex'), {
         secure: true,
         sameSite: 'Lax',
         httpOnly: false  // 允许前端读取
     });
     res.send('OK');
 });

 // 验证
 app.post('/api/transfer', (req, res) => {
     if (req.cookies['CSRF-TOKEN'] !== req.headers['x-csrf-token']) {
         return res.status(403).send('CSRF验证失败');
     }
     // 处理业务逻辑
 });
 ```

 ```js
 // 前端Axios全局配置自动携带Token
 axios.interceptors.request.use(config => {
     const token = document.cookie.match(/CSRF-TOKEN=([^;]+)/)?.[1];
     config.headers['X-CSRF-TOKEN'] = token;
     return config;
 });
 ```

CSRF Token

复制代码
 ##### 原理

 * 服务器生成一个**随机且唯一** 的 Token,嵌入**表单或 HTTP 请求头**中,提交时验证 Token 是否合法。

 * Token 需满足:每次会话或请求唯一、加密随机、不可预测。

   ```js
   // 后端
   HttpServletRequest req = (HttpServletRequest)request; 
   HttpSession s = req.getSession(); 
    
   // 从 session 中得到 csrftoken 属性
   String sToken = (String)s.getAttribute("csrftoken"); 
   if(sToken == null){ 
      // 产生新的 token 放入 session 中
      sToken = generateToken(); 
      s.setAttribute("csrftoken",sToken); 
      chain.doFilter(request, response); 
   } else{ 
      // 从 HTTP 头中取得 csrftoken 
      String xhrToken = req.getHeader("csrftoken"); 
      // 从请求参数中取得 csrftoken 
      String pToken = req.getParameter("csrftoken"); 
      if(sToken != null && xhrToken != null && sToken.equals(xhrToken)){ 
          chain.doFilter(request, response); 
      }else if(sToken != null && pToken != null && sToken.equals(pToken)){ 
          chain.doFilter(request, response); 
      }else{ 
          request.getRequestDispatcher("error.jsp").forward(request,response); 
      } 
   }
   ```

   ```html
   <!-- 前端表单嵌入 --!>
   <!-- 后端渲染页面时注入 Token -->
   <form action="/transfer" method="POST">
     <input type="hidden" name="_csrf" value="{{ csrfToken }}">  <!-- 例如通过模板引擎注入 -->
     <input type="number" name="amount">
     <button>Submit</button>
   </form>
   ```

   ```js
   // 前端AJAX请求
   // 从 Meta 标签或 Cookie 获取 Token(需后端配合)
   const csrfToken = document.querySelector('meta[name="csrf-token"]').content;

   // 发送请求时携带 Token
   axios('/api/transfer', {
     method: 'POST',
     headers: {
       'Content-Type': 'application/json',
       'X-CSRF-TOKEN': csrfToken  // 通过 HTTP 头传递
     },
     body: JSON.stringify({ amount: 100 })
   });
   ```

双重Cookie验证

复制代码
 ##### 原理

 * **无状态验证**:无需服务器存储Token,通过客户端自行携带两份Token副本。
 * **跨域限制**:攻击者无法通过跨域请求同时篡改请求参数和Cookie。
 * **动态绑定**:Token与用户会话或请求上下文动态绑定,增强安全性。
复制代码
 ##### 流程

 * **生成Token** :
   * 用户登录时,服务器生成随机Token,**写入Cookie** (如`CSRF-Token=abc123`)。
 * **前端传递Token** :
   * 前端通过JavaScript读取**Cookie中的Token**,将其添加到请求参数或HTTP头中。
 * **服务器验证** :
   * 服务器比对请求**参数/头中的Token** 与**Cookie中的Token**是否一致。
复制代码
 ##### 与CSRF的区别

 | **特性**  | **双重Cookie验证**     | **传统CSRF Token**           |
 |:--------|:-------------------|:---------------------------|
 | 服务端状态管理 | 无状态                | 需存储Token(Session/DB/Redis) |
 | 实现复杂度   | 简单,无需存储逻辑          | 需处理Token生成、存储、验证           |
 | 抗XSS能力  | 弱(依赖前端读取Cookie)    | 强(Token可设置为HttpOnly)       |
 | 跨域支持    | 需处理CORS和Cookie跨域策略 | 需处理Token传递方式               |

 ```js
 // 后端设置Cookie并验证
 // server.js
 const express = require('express');
 const cookieParser = require('cookie-parser');
 const cors = require('cors');
 const crypto = require('crypto');

 const app = express();
 app.use(express.json());
 app.use(cookieParser());

 // 配置CORS(根据实际部署调整)
 app.use(cors({
   origin: 'http://localhost:8080', // Vue前端地址
   credentials: true // 允许携带Cookie
 }));

 // 登录接口:生成Token并设置Cookie
 app.post('/api/login', (req, res) => {
   const { username, password } = req.body;
   
   // 模拟用户验证
   if (username === 'admin' && password === '123456') {
     // 生成随机Token
     const csrfToken = crypto.randomBytes(32).toString('hex');
     
     // 设置Token到Cookie(允许前端读取)
     res.cookie('CSRF-TOKEN', csrfToken, {
       httpOnly: false,    // 必须允许前端JS读取
       secure: process.env.NODE_ENV === 'production',
       sameSite: 'Lax',    // 允许安全跨站请求
       maxAge: 86400000    // 24小时有效期
     });
     
     return res.json({ success: true });
   }
   res.status(401).json({ error: '认证失败' });
 });

 // 敏感操作接口:验证双重Cookie
 app.post('/api/transfer', (req, res) => {
   const cookieToken = req.cookies['CSRF-TOKEN'];
   const headerToken = req.headers['x-csrf-token'];
   
   if (!cookieToken || cookieToken !== headerToken) {
     return res.status(403).json({ error: 'CSRF验证失败' });
   }
   
   // 处理业务逻辑
   res.json({ success: true, amount: req.body.amount });
 });

 app.listen(3000, () => {
   console.log('Server running on http://localhost:3000');
 });
 ```

 ```js
 // 前端配置Axios全局拦截器 
 import axios from 'axios';

 const instance = axios.create({
   baseURL: 'http://localhost:3000/api',
   withCredentials: true // 允许跨域携带Cookie
 });

 // 请求拦截器:自动添加Token
 instance.interceptors.request.use(config => {
   // 从Cookie中获取Token
   const csrfToken = document.cookie
     .split('; ')
     .find(row => row.startsWith('CSRF-TOKEN='))
     ?.split('=')[1];
   
   if (csrfToken) {
     config.headers['X-CSRF-TOKEN'] = csrfToken;
   }
   return config;
 });

 export default instance;
 ```

分布式校验

复制代码
 ##### 原理

 * **分布式Token管理**:统一生成、存储和验证Token,确保跨服务请求的合法性。
 * **动态同步机制**:解决多节点间的Token状态同步问题。
 * **去中心化验证**:减少对单点服务的依赖,提升系统可用性。
复制代码
 ##### 中央认证服务(推荐)

 * **原理**:由中心化服务统一生成Token,各服务节点通过API验证Token。
  • 使用Session存储CSRF Token会带来很大的压力,使用计算出来的Token,而非随机生成的字符串Token,这样在校验时无需再去读取存储的Token,只用再次计算一次即可(Encrypted Token Pattern方式)

总结

复制代码
 ##### **基础防御组合:CSRF Token + SameSite Cookie**

 *

   ##### **适用场景**:

   * **传统多页面Web应用**(如Django、Rails应用)
   * **混合型架构**(部分服务端渲染 + 部分AJAX请求)
 *

   ##### **实现方式**:

   * **CSRF Token**:

     * 服务端生成随机Token,嵌入表单或HTTP头(如`X-CSRF-TOKEN`)。
     * 提交请求时验证Token合法性。
   * **SameSite Cookie**:

     * 设置会话Cookie的`SameSite=Lax`,限制跨站请求自动携带Cookie。
     * 敏感操作Cookie设为`SameSite=Strict`(如支付确认)。
复制代码
 ##### **增强防御组合:加密Token + 双重Cookie验证**

 *

   ##### **适用场景**:

   * **单页应用(SPA)**(如Vue、React应用)
   * **无状态API服务**(如RESTful接口)
 * **实现方式**:

   * **加密Token**:

     * 使用AES或HMAC加密Token,嵌入用户ID、时间戳等元数据。
     * 防止Token被窃取后直接重放。
   * **双重Cookie验证**:

     * 服务端设置`CSRF-TOKEN` Cookie(`SameSite=None; Secure`)。
     * 前端读取该Cookie并添加到请求头(如`X-CSRF-Token`)。
     * 服务端比对Cookie和请求头的Token是否一致。
复制代码
 ##### **高安全组合:JWT + Origin验证 + SameSite Strict**

 * **适用场景**:

   * **高敏感操作**(如金融交易、密码修改)

   * **跨域API服务**(需严格限制来源)

 * **实现方式**:

   * **JWT(JSON Web Token)** :(详情可看[前端鉴权之JWT-CSDN博客](https://link.juejin.cn?target=https%3A%2F%2Fblog.csdn.net%2F2301_76603086%2Farticle%2Fdetails%2F147397766%3Fspm%3D1001.2014.3001.5501 "https://blog.csdn.net/2301_76603086/article/details/147397766?spm=1001.2014.3001.5501"))

     * 用户登录后签发JWT,包含用户身份和操作权限。
     * 请求时通过`Authorization: Bearer <token>`传递。
   * **Origin验证**:

     * 检查请求头中的`Origin`或`Referer`是否为合法域名。
   * **SameSite Strict**:

     * 设置关键Cookie为`SameSite=Strict`,完全禁止跨站请求携带。
相关推荐
同志3271316 分钟前
用HTML+CSS做了一个网易云音乐客户端首页
前端·css
小猪欧巴哟18 分钟前
pnpm install 安装项目依赖遇到 illegal operation on a directory, symlink 问题
前端·vue.js
独角仙梦境18 分钟前
🚀🚀🚀学习这个思路,你也能手撸自己的专属vip脚手架🚀🚀🚀
前端
CJWbiu21 分钟前
Github Action + docker 实现自动化部署
前端·自动化运维
关山22 分钟前
在TS中如何在子进程中动态实例化一个类
前端
吃瓜群众i22 分钟前
兼容IE8浏览器的8个实用知识点
前端·javascript
上海云盾商务经理杨杨24 分钟前
AI如何重塑DDoS防护行业?六大变革与未来展望
人工智能·安全·web安全·ddos
前端烨26 分钟前
vue3子传父——v-model辅助值传递
前端·vue3·组件传值
Mintopia1 小时前
Three.js 在数字孪生中的应用场景教学
前端·javascript·three.js
夕水1 小时前
自动化按需导入组件库的工具rust版本完成开源了
前端·rust·trae