当前位置: 首页 > news >正文

Web安全之CSRF攻击详解与防护

在互联网应用中,安全性问题是开发者必须时刻关注的核心内容之一。跨站请求伪造(Cross-Site Request Forgery, CSRF),是一种常见的Web安全漏洞。通过CSRF攻击,黑客可以冒用受害者的身份,发送恶意请求,执行诸如转账、订单提交等操作,导致严重的安全后果。

本文将详细讲解CSRF攻击的原理及其防御方法,结合电商交易系统的场景给出错误和正确的示范代码,并分析常见的安全问题与解决方案,帮助开发者全面理解和防御CSRF攻击。

1. CSRF攻击概述

1.1 CSRF的原理

CSRF攻击是指黑客通过欺骗用户在不知情的情况下向受信任的服务器发送请求,从而执行用户并未授权的操作。由于浏览器的同源策略,浏览器会自动携带当前登录用户的身份凭证(如Cookie),导致服务器误以为请求是合法用户发出的。

常见的CSRF攻击流程如下:

  1. 用户登录电商系统,并在浏览器中保持会话(比如通过Cookie保存登录状态)。
  2. 攻击者构造一个恶意网站,诱导用户访问该网站。
  3. 恶意网站通过用户的浏览器向电商系统发送请求,例如提交订单、修改账户信息等。
  4. 由于用户已经登录,服务器在收到请求时会认为是合法请求,从而执行攻击者的恶意操作。
1.2 CSRF的危害
  • 账户盗用:攻击者可以伪造请求进行账户操作,如修改密码、转账等。
  • 资金损失:在电商交易系统中,CSRF可以被用来提交伪造订单、篡改收货地址、转移资金等。
  • 信息泄露:攻击者可能通过CSRF请求获取用户的敏感信息。

2. 电商交易系统中的CSRF攻击示例

为了更直观地理解CSRF攻击的危害,我们以电商交易系统为例,演示错误的代码实现以及如何修复它。

2.1 错误示范:未防护CSRF的订单提交

在一个简单的电商交易系统中,用户可以通过提交订单来购买商品。假设服务器端的订单提交接口是通过POST请求进行的,代码如下:

// 订单提交控制器
@PostMapping("/submitOrder")
public String submitOrder(@RequestParam("productId") String productId, @RequestParam("quantity") int quantity,HttpSession session) {// 获取当前用户信息User user = (User) session.getAttribute("currentUser");// 创建订单Order order = new Order();order.setUserId(user.getId());order.setProductId(productId);order.setQuantity(quantity);// 保存订单到数据库orderService.saveOrder(order);return "orderSuccess";
}

这种实现存在明显的安全问题:攻击者可以诱导用户访问恶意链接,从而提交伪造的订单。

例如,攻击者可以构造如下HTML页面,并诱导用户点击:

<html>
<body><form action="http://ecommerce.com/submitOrder" method="POST"><input type="hidden" name="productId" value="123"><input type="hidden" name="quantity" value="10"><input type="submit" value="Submit order"></form>
</body>
</html>

如果用户在登录状态下点击了该页面的提交按钮,电商系统将生成一个伪造的订单,而用户对此一无所知。

2.2 错误示范:CSRF攻击的真实危害

在实际的电商系统中,攻击者可能会诱导用户执行更为严重的操作,比如修改收货地址、提交高价商品订单等。这些操作可以通过隐藏的表单字段自动完成,用户根本不需要手动提交。

2.3 CSRF防御的基本原则

防御CSRF攻击的核心在于服务器能够验证每一个请求的合法性。一般来说,CSRF防御的主要手段有:

  1. Token校验:为每一个请求生成一个唯一的Token,服务器通过该Token判断请求是否合法。
  2. Referer验证:通过检查HTTP请求头中的Referer字段,验证请求是否来自合法的页面。
  3. SameSite Cookie:通过设置Cookie的SameSite属性,限制跨站点的请求携带Cookie。

3. Token防护CSRF

3.1 使用Token防护CSRF

Token校验是防护CSRF攻击最常用且最有效的方法。它的工作原理是:

  • 在表单页面加载时,服务器生成一个唯一的Token,并将其嵌入到表单中。
  • 用户提交表单时,Token会一同提交到服务器。
  • 服务器接收到请求后,会验证该Token是否与会话中的Token一致,只有匹配时才允许执行后续操作。

以下是如何通过Token防护CSRF攻击的示例。

3.2 在表单中添加CSRF Token

首先,我们需要为每一个请求生成唯一的Token,并在提交时携带该Token。以下是Spring Boot中的CSRF防护机制的实现。

  1. 生成CSRF Token
    Spring Security默认提供了CSRF防护机制。我们可以通过以下配置启用它:

    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf() // 开启CSRF防护.and().authorizeRequests().antMatchers("/submitOrder").authenticated() // 需要登录.and().formLogin().loginPage("/login").permitAll();}
    }
    

    Spring Security会自动为每个页面生成一个CSRF Token,并将该Token嵌入到页面中的隐藏字段或HTTP头中。

  2. 在表单中添加CSRF Token
    Spring Security会在每个表单中自动包含一个CSRF Token。表单代码如下:

    <form action="/submitOrder" method="POST"><input type="hidden" name="_csrf" value="${_csrf.token}"/> <!-- 自动生成的CSRF Token --><input type="text" name="productId" placeholder="Product ID"/><input type="number" name="quantity" placeholder="Quantity"/><input type="submit" value="Submit Order"/>
    </form>
    
  3. 服务器端验证CSRF Token
    当用户提交表单时,Spring Security会自动验证CSRF Token。如果Token验证失败,将会抛出异常,阻止请求的执行。

3.3 限制 CSRF Token 的有效期

在 CSRF 防护机制中,限制 Token 的有效期可以通过以下步骤实现:

  1. 生成带有时间戳的 Token: CSRF Token 在生成时附加一个时间戳,以标识其生成的时间。
  2. 校验 Token 时检查有效期: 在验证 CSRF Token 的同时,检查其时间戳是否在允许的时间范围内,过期的 Token 将视为无效,要求用户重新提交表单或刷新页面。
3.3.1 生成带时间戳的 CSRF Token

在生成 CSRF Token 时,我们可以在 Token 中附加一个时间戳来记录生成时间。例如,可以通过 base64 编码将随机生成的 Token 和当前时间戳一起组合。

import java.util.Base64;
import java.util.Date;public class CsrfTokenGenerator {private static final long TOKEN_VALIDITY = 5 * 60 * 1000; // Token 有效期为 5 分钟// 生成带时间戳的 CSRF Tokenpublic static String generateCsrfToken() {String token = generateRandomToken(); // 生成随机Tokenlong timestamp = System.currentTimeMillis(); // 获取当前时间戳String tokenWithTimestamp = token + ":" + timestamp;return Base64.getEncoder().encodeToString(tokenWithTimestamp.getBytes()); // Base64 编码}private static String generateRandomToken() {// 此处生成随机 Token,简单示例为随机UUIDreturn java.util.UUID.randomUUID().toString();}
}
3.3.2 验证带时间戳的 CSRF Token

在服务器端对 Token 进行验证时,除了常规的 Token 匹配,还需要校验时间戳,确保 Token 在有效期内。

import java.util.Base64;public class CsrfTokenValidator {private static final long TOKEN_VALIDITY = 5 * 60 * 1000; // 5 分钟有效期public static boolean validateCsrfToken(String token) {try {// 解码 TokenString decodedToken = new String(Base64.getDecoder().decode(token));String[] parts = decodedToken.split(":");if (parts.length != 2) {return false; // Token 格式错误}String csrfToken = parts[0]; // 获取CSRF Tokenlong timestamp = Long.parseLong(parts[1]); // 获取时间戳long currentTime = System.currentTimeMillis();if (currentTime - timestamp > TOKEN_VALIDITY) {return false; // Token 已过期}// 验证Token本身的正确性(与Session中的Token对比)return csrfToken.equals(getStoredToken()); // 假设getStoredToken()获取服务器端存储的Token} catch (Exception e) {return false; // 解码或校验失败}}private static String getStoredToken() {// 这里从服务器Session或者数据库中获取已存储的CSRF Tokenreturn "stored-token-example";}
}
3.3.3 CSRF Token 使用中的代码示例

在电商交易系统的具体示例中,假设用户进行购物车的结算操作。我们可以通过 CSRF Token 限制请求的有效期,防止攻击者在很久之前窃取的 Token 被再次利用。

前端发送请求:

function checkout() {let csrfToken = getCookie('CSRF-TOKEN');fetch('/checkout', {method: 'POST',headers: {'X-CSRF-TOKEN': csrfToken, // 包含CSRF Token'Content-Type': 'application/json'},body: JSON.stringify({productId: 123,amount: 1})}).then(response => {if (response.status === 403) {alert('CSRF Token 过期或无效,请刷新页面再试');}});
}

服务器端验证:

在服务器端,通过 CsrfTokenValidator 对 Token 进行验证,确保其没有过期。

@PostMapping("/checkout")
public ResponseEntity<String> checkout(@RequestHeader("X-CSRF-TOKEN") String csrfToken) {if (!CsrfTokenValidator.validateCsrfToken(csrfToken)) {return new ResponseEntity<>("CSRF Token 无效或已过期", HttpStatus.FORBIDDEN);}// 处理购物车结算逻辑return new ResponseEntity<>("结算成功", HttpStatus.OK);
}
3.3.4 刷新过期 Token

如果用户的 CSRF Token 过期,前端可以通过一个特定的 API 进行 Token 刷新,重新获取有效的 CSRF Token。

@GetMapping("/refreshCsrfToken")
public ResponseEntity<String> refreshCsrfToken(HttpServletResponse response) {String newToken = CsrfTokenGenerator.generateCsrfToken();Cookie csrfCookie = new Cookie("CSRF-TOKEN", newToken);csrfCookie.setPath("/");csrfCookie.setHttpOnly(true);response.addCookie(csrfCookie);return new ResponseEntity<>("CSRF Token 已刷新", HttpStatus.OK);
}
3.3.5 Token 过期处理的优点
  • 安全性增强:即使攻击者获取到 CSRF Token,也只能在有限的时间内进行攻击。
  • 防止 Token 被长期滥用:定期刷新 Token,防止长期使用相同的 Token 增加攻击风险。

4. 使用Referer头验证

另一种防护CSRF攻击的方式是检查HTTP请求头中的Referer字段,验证请求是否来自同一个网站。虽然Referer验证并不是100%可靠(因为可能被用户代理修改),但可以作为一种补充手段。

我们可以通过Spring Security提供的Referer验证器实现这一机制:

@Override
protected void configure(HttpSecurity http) throws Exception {http.csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/submitOrder")).and().authorizeRequests().antMatchers("/submitOrder").authenticated();
}

通过这种方式,服务器会检查请求的来源,如果请求的来源不是本站域名,则拒绝执行该请求。

5. 同源检测(SameSite Cookie)

通过设置 cookie 的 SameSite 属性,可以防止跨站点请求时浏览器发送 cookie,从而减少 CSRF 攻击的风险。

  • SameSite=Lax: 允许导航链接发送 cookie,但跨站点 POST 请求不会发送 cookie。
  • SameSite=Strict: 完全禁止跨站点的请求发送 cookie,最大限度地防御 CSRF。

Cookie 设置示例:

http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().addFilterBefore(new CsrfFilter(csrfTokenRepository), UsernamePasswordAuthenticationFilter.class).headers().frameOptions().sameOrigin().httpStrictTransportSecurity().includeSubDomains(true).and().cookie().sameSite(SameSiteCookieAttributeValue.LAX);

6. 总结

本文详细介绍了CSRF攻击的原理和危害,并通过错误与正确示范,演示了如何防护CSRF攻击。CSRF攻击是一种常见且危险的安全漏洞,开发者应时刻保持警惕,采用诸如Token验证、Referer检查、SameSite Cookie等多层防护手段,确保Web应用的安全性。

通过掌握这些防护技巧,开发者可以有效抵御CSRF攻击,保护用户的个人信息与财产安全。在实际开发中,安全问题应始终放在优先位置,只有不断优化和完善,才能打造出安全、可靠的应用系统。

相关文章:

Web安全之CSRF攻击详解与防护

在互联网应用中&#xff0c;安全性问题是开发者必须时刻关注的核心内容之一。跨站请求伪造&#xff08;Cross-Site Request Forgery, CSRF&#xff09;&#xff0c;是一种常见的Web安全漏洞。通过CSRF攻击&#xff0c;黑客可以冒用受害者的身份&#xff0c;发送恶意请求&#x…...

IDEA运行Java程序提示“java: 警告: 源发行版 11 需要目标发行版 11”

遇到这个提示一般是在pom.xml中已经指定了构建的Java版本环境是11例如(此时添加了build插件的情况下虽然不能直接运行代码但是maven是可以正常打包构建)&#xff1a; <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><…...

车载测试| 汽车的五域架构 (含线控技术知识)

汽车的五域架构是一种将汽车电子控制系统按照功能进行划分的架构模式&#xff0c;主要包括动力域、底盘域、座舱域、自动驾驶域和车身域。&#xff08;汽车三域架构通常是指将汽车电子系统划分为三个主要领域&#xff1a;动力域、底盘域和智能座舱域&#xff08;或车身舒适域&a…...

【Linux】gcc/g++ 、make/Makefile、git、gdb 的使用

目录 1. Linux编译器-gcc/g1.1 编译器gcc/g的工作步骤1.2 函数库1.2.1 函数库的作用及分类1.2.2 动态链接和静态链接1.2.3 动态库和静态库的优缺点 1.3 gcc选项 2. Linux项目自动化构建工具-make/Makefile2.1 .PHONY2.2 尝试编写进度条程序 3. git3.1 安装 git3.2 下载项目到本…...

Elastic Stack--ES的DSL语句查询

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 学习B站博主教程笔记&#xff1a; 最新版适合自学的ElasticStack全套视频&#xff08;Elk零基础入门到精通教程&#xff09;Linux运维必备—Elastic…...

ARM基础知识---CPU---处理器

目录 一、ARM架构 1.1.RAM---随机存储器 1.2.ROM---只读存储器 1.3.flash---闪存存储器 1.4.时钟&#xff08;振晶&#xff09; 1.5.复位 二、CPU---ARM920T 2.1.R0~R12---通用寄存器 2.2.PC程序计数器 2.3.LR连接寄存器 2.4.SP栈指针寄存器 2.5.CPSR当前程序状态寄存…...

将星 x17 安装ubuntu 20.04 双系统

准备工作&#xff0c;包含关闭快速启动&#xff0c;关闭Secret Boot 1.进入控制面板选择小图标&#xff0c;找到电源选项 2.点击更改当前不可用的设置&#xff0c;关闭快速启动 3.开机启动时快速按F2&#xff0c;进入BIOS 4.选择Setup Utiltity&#xff0c;选择Security&#…...

E31.【C语言】练习:指针运算习题集(上)

Exercise 1 求下列代码的运行结果 #include <stdio.h> int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d",*(ptr - 1));return 0; } 答案速查: 分析&#xff1a; Exercise 2 求下列代码的运行结果 //在x86环境下 //假设结…...

git分支的管理

分支管理是 Git 版本控制系统中的一个核心功能&#xff0c;它涉及如何创建、管理、合并和删除分支&#xff0c;以便在团队协作和开发过程中更有效地组织代码。以下是分支管理中的一些关键概念和实践&#xff1a; 1. 分支的创建 创建新分支&#xff1a;在开发新功能、修复 bug…...

对于消息队列的一些思考

如何保证消息不被重复消费 唯一ID&#xff1a;你提到的通过唯一ID解决重复消费问题非常重要。这通常通过业务系统引入唯一消息ID&#xff08;如UUID&#xff09;来实现。在消费端&#xff0c;先检查消息ID是否已经被处理&#xff0c;未处理过的才进行处理&#xff0c;确保幂等…...

IM即时通讯软件-WorkPlus私有化部署的局域网即时通讯工具

随着企业对通讯安全和数据掌控的需求不断增加&#xff0c;许多企业开始选择私有化部署的即时通讯工具&#xff0c;以在内部局域网环境中实现安全、高效的沟通与协作。IM-WorkPlus作为一款受欢迎的即时通讯软件&#xff0c;提供了私有化部署的选项&#xff0c;使企业能够在自己的…...

AI大模型的饕餮盛宴,系统学习大模型技术,你想要的书都在这里了

AI大模型的饕餮盛宴&#xff0c;系统学习大模型技术&#xff0c;你想要的书都在这里了 要说现在最热门的技术&#xff0c;可谓非大模型莫属&#xff01;不少小伙伴都想要学习大模型技术&#xff0c;转战AI领域&#xff0c;以适应未来的大趋势&#xff0c;寻求更有前景的发展~~…...

支付宝开放平台-开发者社区——AI 日报「9 月 9 日」

1 离开 OpenAl 后&#xff0c;llya 拿了10亿美金对抗 Al 作恶 极窖公园 丨阅读原文 lya Sutskever, OpenAl的前联合创始人&#xff0c;成立了SS1 (Safe Superintelligence)&#xff0c;旨在构建安全的Al模型。SSl获得了10亿美元的融资&#xff0c;估值达到50亿美元&#xff…...

将AI与情境定位结合以确保品牌安全

你可能会看到一些广告&#xff0c;感觉它们跟你在线阅读或观看的内容有奇怪的关联。这就是上下文广告在起作用。这种基于广告的解决方案在不断变化的数字环境中逐步发展&#xff0c;已经成为每个广告主的必备工具。不过&#xff0c;这种广告不只是把广告和上下文进行匹配这么简…...

OpenAI 联合 SWE 发布 AI 软件工程能力测试集,Gru.ai 荣登榜首

在 9 月 3 日&#xff0c;Gru.ai 在 SWE-Bench-Verified 评估最新发布的数据中以 45.2% 的高分排名第一。SWE-Bench-Verified 是 OpenAI 联合 SWE 发布测试集&#xff0c;旨在更可靠的评估 AI 解决实际软件问题的能力。该测试集经由人工验证打标&#xff0c;被认为是评估 AI 软…...

一文读懂SpringMVC的工作原理

前言 MVC是经典的软件架构设计模式&#xff0c;几乎在各个领域各种开发语言中&#xff0c;均采纳了这个思想。此刻博主突然想到了Thinking in xxx系列设计书籍。换句话说&#xff0c;就是“各人自扫门前雪”和“术业有专攻”。当职责分配得当后&#xff0c;剩下的就是发挥各“…...

【python-斐波那契数列和完美数之间的区别】

斐波那契数列和完美数在数学领域中是两个截然不同的概念&#xff0c;它们之间存在明显的区别。以下是对这两个概念及其区别的详细阐述&#xff1a; 斐波那契数列 定义&#xff1a; 斐波那契数列&#xff0c;又称黄金分割数列&#xff0c;是一个在数学上具有重要意义的数列。它…...

【redis】本地windows五分钟快速安装redis

用处&#xff1a;本地自测&#xff0c;有时候公司redis环境不稳定&#xff0c;用自己的 1.下载&#xff0c;github下载一个解压缩在自己想要的位置 选择版本&#xff1a;Redis-7.4.0-Windows-x64-msys2-with-Service&#xff0c;zip GitHub - redis-windows/redis-windows: …...

arm64高速缓存基础知识

高速缓存的替换策略 随机法&#xff1a;随机地确定替换的高速缓存行&#xff0c;由一个随机数产生器产生随机数来确认替换行 FIFO法&#xff1a;选择最先调入的高速缓存行进行替换 LRU法&#xff1a;最少使用的行优先替换。 高速缓存的共享属性 内部共享的高速缓存通常指的…...

物管王 物业管理系统软件

物管王 物业收费管理系统软件 网络版...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...