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攻击,保护用户的个人信息与财产安全。在实际开发中,安全问题应始终放在优先位置,只有不断优化和完善,才能打造出安全、可靠的应用系统。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/419323.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++进阶】hash表的封装

文章目录 hash表哈希表的关键组成部分哈希表的优缺点优点&#xff1a;缺点&#xff1a; 常见应用场景 开放定址法实现hash表负载因子 (Load Factor)负载因子的意义负载因子的影响再散列 (Rehashing)示例 整体框架insertFinderasehash桶封装框架insertfinderase~HashTable() 总结…

银行结算业务

1.1 银行本票 银行本票是由银行签发的,承诺自己在见票时无条件支付票款给收款人或持票人的业务。银行本票按票面划分为定额本票和不定额本票,按币种划分为人民币银行本票和外币银行本票。人民币银行本票仅在同一交换区域内使用,资金清算利用当地人民银行组织的资金清算形式…

多个vue项目部署到nginx服务器

文章目录 需求一、项目打包1.vue.config.js2.request.js文件3.打包 二、nginx配置 需求 同一个域名安装多个vue项目。 比如&#xff1a;域名为 https://domain.com 后缀。那么通过不同的后缀就能去访问不同的项目地址。 https://domain.com&#xff0c;不加任何后缀&#x…

【第0006页 · 数组】寻找重复数

【前言】本文以及之后的一些题解都会陆续整理到目录中&#xff0c;若想了解全部题解整理&#xff0c;请看这里&#xff1a; 第0006页 寻找重复数 今天想讨论的一道题在 LeetCode 上评论也是颇为“不错”。有一说一&#xff0c;是道好题&#xff0c;不过我们还是得先理解了它才…

微信小程序中如何监听元素进入目标元素

Page({onLoad: function(){// 如果目标节点&#xff08;用选择器 .target-class 指定&#xff09;进入显示区域以下 100px 时&#xff0c;就会触发回调函数。wx.createIntersectionObserver().relativeToViewport({bottom: 100}).observe(.target-class, (res) > {res.inter…

合宙4G模组Air780EX——产品规格书

Air780EX 是合宙通信推出的LTE Cat.1 bis通信模块&#xff1b; Air780EX采用移芯EC618平台&#xff0c;支持LTE 3GPP Rel.13 技术&#xff1b; Air780EX 是4G全网通模块&#xff0c;可适应不同的运营商和产品&#xff0c;确保产品设计的最大灵活性。 其主要特点和优势可以总…

maven配置文件常用模板

注释很详细&#xff0c;直接上代码 项目结构 内容 父项目 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi…

高德地图SDK Android版开发 10 InfoWindow

高德地图SDK Android版开发 10 InfoWindow 前言相关类和方法默认样式Marker类AMap类AMap.OnInfoWindowClickListener 接口 自定义样式(视图)AMap 类AMap.ImageInfoWindowAdapter 接口 自定义样式(Image)AMap.ImageInfoWindowAdapter 接口 示例界面布局MapInfoWindow类常量成员变…

【数学建模国赛思路预约】2024全国大学生数学建模竞赛助攻思路、代码、论文

2024年全国大学生数学建模大赛马上就要开始了&#xff0c;大家有没有准备好呢&#xff0c;今年将会和之前一样&#xff0c;将会在比赛赛中时期为大家提供比赛各题的相关解题思路、可运行代码参考以及成品论文。 一、分享计划表如下所示 1、 赛中分享内容包括&#xff08;2023国…

高并发内存池(二):​整体框架的介绍与ThreadCache的实现

目录 整体框架介绍 ThreadCache的主体框架 自由链表-FreeList 内存对齐-RoundUp 计算桶位置-Index 基础版 进阶版 线程局部存储 __declspec(thread) 关键字 实现线程无锁 申请内存-Allocate 释放内存-Deallocate 从中心缓存中申请内存 整体框架介绍 高并发内存池…

机器学习引领未来:赋能精准高效的图像识别技术革新

图像识别技术近年来取得了显著进展,深刻地改变了各行各业。机器学习,特别是深度学习的突破,推动了这一领域的技术革新。本文将深入探讨机器学习如何赋能图像识别技术,从基础理论到前沿进展,再到实际应用与挑战展望,为您全面呈现这一领域的最新动态和未来趋势。 1. 引言 …

kubernetes集群部署Confluence 7.2.0+mysql 5.7(自测有效)

背景介绍&#xff1a; Confluence是一个专业的企业知识管理与协同软件。使用简单&#xff0c;但它强大的编辑和站点管理特征能够帮助团队成员之间共享信息、文档协作、集体讨论&#xff0c;信息推送。 这里介绍的使用的是Confluence 7.2.0版本的。 一、在kubernetes集群部署 1…

本地零阶提示优化

本文探讨了如何优化大型语言模型&#xff08;LLM&#xff09;中的提示&#xff08;prompt&#xff09;&#xff0c;以更有效地利用这些黑盒模型的能力。传统的优化方法倾向于寻找全局最优解&#xff0c;但在某些情况下这种做法可能表现不佳。通过对提示优化进行深入的研究&…

01 Docker概念和部署

目录 1.1 Docker 概述 1.1.1 Docker 的优势 1.1.2 镜像 1.1.3 容器 1.1.4 仓库 1.2 安装 Docker 1.2.1 配置和安装依赖环境 1.3镜像操作 1.3.1 搜索镜像 1.3.2 获取镜像 1.3.3 查看镜像 1.3.4 给镜像重命名 1.3.5 存储&#xff0c;载入镜像和删除镜像 1.4 Doecker…

汽车功能安全--TC3xx之PBIST、MONBIST

目录 1.PMS 电源监控速览 2.PBIST 3.MONBIST 4.小结 1.PMS 电源监控速览 英飞凌TC3xx芯片的四种硬件机制&#xff0c;分别是&#xff1a; PMS:PBIST: Power Built-in Self Test. MCU:LBIST: Logic Built-in Self Test. PMS:MONBIST: Monitor Built-in Self Test. VMT:MBI…

史上最全的Linux常用命令汇总(超全面!超详细!)收藏这一篇就够了!

command &#xff1a;命令名&#xff0c;相应功能的英文单词或单词的缩写[-options] &#xff1a;选项&#xff0c;可用来对命令进行控制&#xff0c;也可以省略parameter &#xff1a;传给命令的参数&#xff0c;可以是 零个、一个 或者 多个 查阅命令帮助信息 -help 说明&…

【高阶数据结构】B树、B+树、B*树

B树、B树、B*树 1. 常见的搜索结构2. B树概念3. B树的插入分析4. B树的插入实现4.1 B树的节点设计4.2 B树的部分插入实现14.3 B树的查找4.4 B树的部分插入实现24.5 插入key的过程4.7 B树的插入完整代码4.8 B树的简单验证4.9 B树的删除4.10 B树的性能分析 5. B树6. B*树7. 总结8…

【C++】STL学习——list模拟实现

目录 list介绍list结构介绍节点类的实现迭代器的实现构造函数运算符重载--运算符重载运算符重载!运算符重载*运算符重载->运算符重载 const迭代器的实现多参数模板迭代器list函数接口总览默认成员函数构造函数1构造函数2构造函数3 析构函数拷贝构造函数赋值重载函数 迭代器b…

开放式系统互连(OSI)模型的实际意义

0 前言 开放式系统互连&#xff08;OSI&#xff0c;Open Systems Interconnection&#xff09;模型&#xff0c;由国际标准化组织&#xff08;ISO&#xff09;在1984年提出&#xff0c;目的是为了促进不同厂商生产的网络设备之间的互操作性。 定义了一种在层之间进行协议实现…

【C++】STL容器详解【下】

目录 一、list容器 1.1 list基本概念 1.2 lsit构造函数 1.3 list数据元素插入和删除操作 1.4 list大小操作 1.5 list赋值操作 1.6 list数据的存取 1.7 list反转排序 二、set/multiset容器 2.1 set/multiset基本概念 2.2 set构造函数 2.3 set赋值操作 2.4 set大小操…