最新Spring Security实战教程(十一)CSRF攻防实战 - 从原理到防护的最佳实践

最新Spring Security实战教程(十一)CSRF攻防实战 - 从原理到防护的最佳实践
在这里插入图片描述
🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志
🎐 个人CSND主页——Micro麦可乐的博客
🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战
🌺《RabbitMQ》专栏19年编写主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战
🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解
🌛《开源项目》本专栏主要介绍目前热门的开源项目,带大家快速了解并轻松上手使用
✨《开发技巧》本专栏包含了各种系统的设计原理以及注意事项,并分享一些日常开发的功能小技巧
💕《Jenkins实战》专栏主要介绍Jenkins+Docker的实战教程,让你快速掌握项目CI/CD,是2024年最新的实战教程
🌞《Spring Boot》专栏主要介绍我们日常工作项目中经常应用到的功能以及技巧,代码样例完整
🌞《Spring Security》专栏中我们将逐步深入Spring Security的各个技术细节,带你从入门到精通,全面掌握这一安全技术
如果文章能够给大家带来一定的帮助!欢迎关注、评论互动~

最新Spring Security实战教程(十一)CSRF攻防实战 - 从原理到防护的最佳实践

回顾链接:
最新Spring Security实战教程(一)初识Spring Security安全框架
最新Spring Security实战教程(二)表单登录定制到处理逻辑的深度改造
最新Spring Security实战教程(三)Spring Security 的底层原理解析
最新Spring Security实战教程(四)基于内存的用户认证
最新Spring Security实战教程(五)基于数据库的动态用户认证传统RBAC角色模型实战开发
最新Spring Security实战教程(六)最新Spring Security实战教程(六)基于数据库的ABAC属性权限模型实战开发
最新Spring Security实战教程(七)方法级安全控制@PreAuthorize注解的灵活运用
最新Spring Security实战教程(八)Remember-Me实现原理 - 持久化令牌与安全存储方案
最新Spring Security实战教程(九)前后端分离认证实战 - JWT+SpringSecurity无缝整合
最新Spring Security实战教程(十)权限表达式进阶 - 在SpEL在安全控制中的高阶魔法

专栏更新完毕后,博主将会上传所有章节代码到ZEEKLOG资源免费给大家下载,如你不想等后续章节代码需提前获取,可以私信或留言!

1. 前言

在前面学习的章节中,相信大家一定看一个配置 .csrf() , 回忆一下之前使用 Spring Security 默认页登录的时候,该配置 Spring Security 默认开启,主要做用于 CSRF 防护, 如果你现在还不了解什么是 CSRF 防护,没关系通过本章节,博主带着大家一起深入学习这个知识点~


2. CSRF 攻击原理

跨站请求伪造(CSRF)是一种利用受信任用户的身份,诱使用户在已登录的应用中执行非预期操作的攻击手段。

当用户在某个站点(如银行)登录并持有有效 Session Cookie 后,攻击者可通过精心构造的请求(例如隐藏在图片或表单中的 POST 请求)在用户不知情的情况下向该站点发起请求,并携带用户的 Cookie,从而完成诸如转账、修改邮箱等敏感操作。

2.1 攻击原理图解

用户访问了A站点,获得了Session或Cookie后,
用户不经意间访问到了恶意网站,此刻恶意网站伪造对A站点的危险请求
在这里插入图片描述

2.2 攻击示例

下面示例展示了一个最常见的 CSRF 攻击场景:用户登录了 https://bank.com 后,攻击者在自己的网站 https://evil.com 上放置如下 HTML 片段:

<!-- 在恶意页面上渲染时,立即向 bank.com 发起转账请求 --><imgsrc="https://bank.com/transfer?amount=1000&to=attacker"/>

3. Spring Security防御机制解析

3. 1 同步令牌模式(Synchronizer Token Pattern)

同步令牌模式是 Spring Security 的默认方案, 服务器在渲染每个需要保护的表单页面时,向用户 Session 中存入一个随机生成的 Token,并在表单中以隐藏字段输出;提交时,服务器验证该字段与 Session 中的 Token 是否一致,若不匹配则拒绝请求。此模式能有效防止 CSRF 攻击,因为攻击者无法从第三方域读取到该随机 Token

核心防御流程

在这里插入图片描述
服务端生成随机Token(每个Session唯一)Token嵌入HTML表单的隐藏字段或HTTP头客户端提交请求时必须携带有效Token服务端校验Token合法性

3. 2 双重提交 Cookie(Double Submit Cookie)

服务器在首次响应页面时,通过 Set-Cookie 设置一个随机 Token,同时在页面中通过脚本将该 Token 读出并写入一个请求头(或隐藏表单字段)。服务器接收请求后,比较 Cookie 中的 Token 与请求中携带的 Token 是否一致。由于浏览器同源策略不能让第三方域读取 Cookie,攻击者无法同步两个值。

浏览器支持在 Set-Cookie 响应头中声明 SameSite 属性,用来限制 Cookie 在跨站请求时是否发送。设置为 Strict 或 Lax 模式,可从源头上阻止大部分 CSRF 请求

SameSite=Strict:绝不在第三方请求中发送该 Cookie;SameSite=Lax:仅允许在“安全”的跨站 GET 导航中发送。

4. 实战代码示例

这里我们将针对上述三种防护机制,进行相关代码演示

4.1 在 Spring Security 中启用 CSRF 防护

Spring Security 默认开启 CSRF 保护,采用的是同步令牌模式。下面展示如何单体、前后分离中集成

❶ Thymeleaf 模板中集成

@Configuration@EnableWebSecuritypublicclassSecurityConfig{@BeanpublicSecurityFilterChainfilterChain(HttpSecurity http)throwsException{ http .csrf(csrf -> csrf // 可自定义 CsrfTokenRepository,例如 CookieCsrfTokenRepository.withHttpOnlyFalse()).authorizeHttpRequests(auth -> auth .anyRequest().authenticated()).formLogin(withDefaults());return http.build();}}

在 Thymeleaf 页面中添加隐藏字段,设置Token

<!-- Thymeleaf 模板:form.html --><formth:action="@{/transfer}"method="post"><!-- 输出 CSRF 隐藏字段 --><inputtype="hidden"th:name="${_csrf.parameterName}"th:value="${_csrf.token}"/><inputtype="number"name="amount"/><buttontype="submit">Transfer</button></form>

在控制器中,Spring Security 自动会在每次 POST 请求时校验表单中的 ${_csrf.token}Session 中的令牌是否匹配,
不匹配则抛出InvalidCsrfTokenException

❷ 前后端分离适配方案

下面演示在前后分离中的适配,前端在请求前 初始化时获取CSRF Token

// 自定义CSRF令牌处理器publicclassSpaCsrfTokenRequestHandlerextendsCsrfTokenRequestAttributeHandler{@Overridepublicvoidhandle(HttpServletRequest request,HttpServletResponse response,Supplier<CsrfToken> csrfToken){// 将CSRF Token暴露给前端JavaScriptCsrfToken token = csrfToken.get();if(token !=null){ response.setHeader(token.getHeaderName(), token.getToken());}}}//SecurityConfig.java@Configuration@EnableWebSecuritypublicclassSecurityConfig{@BeanpublicSecurityFilterChainsecurityFilterChain(HttpSecurity http)throwsException{ http .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).csrfTokenRequestHandler(newSpaCsrfTokenRequestHandler()))// 其他配置...return http.build();}}

❸ 自定义令牌存储策略

// 使用Redis存储CSRF令牌(分布式场景)@BeanpublicCsrfTokenRepositoryredisCsrfTokenRepository(RedisTemplate<String,String> redisTemplate){returnnewCsrfTokenRepository(){@OverridepublicCsrfTokengenerateToken(HttpServletRequest request){returnnewDefaultCsrfToken("X-CSRF-TOKEN","_csrf", UUID.randomUUID().toString());}@OverridepublicvoidsaveToken(CsrfToken token,HttpServletRequest request,HttpServletResponse response){String sessionId = request.getSession().getId();if(token ==null){ redisTemplate.delete(sessionId);}else{ redisTemplate.opsForValue().set(sessionId, token.getToken(),30, MINUTES);}}@OverridepublicCsrfTokenloadToken(HttpServletRequest request){String sessionId = request.getSession().getId();String token = redisTemplate.opsForValue().get(sessionId);return token !=null?newDefaultCsrfToken("X-CSRF-TOKEN","_csrf", token):null;}};}

前端演示代码

// 初始化时获取CSRF Tokenfetch('/csrf',{ credentials:'include'}).then(res=>{const token = res.headers.get('X-CSRF-TOKEN'); axios.defaults.headers.common['X-CSRF-TOKEN']= token;});// 所有POST请求自动携带Token axios.interceptors.request.use(config=>{if(['post','put','delete'].includes(config.method.toLowerCase())){ config.headers['X-CSRF-TOKEN']=getCSRFToken();}return config;});

4.2 双重Cookie验证

实际上在我们日常开发中,使用 Spring Security 同步令牌方案,基本能满足我们大部分需求,这里就简单演示一下双重Cookie验证

publicclassDoubleCookieCsrfFilterextendsOncePerRequestFilter{@OverrideprotectedvoiddoFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain)throwsServletException,IOException{CsrfToken token =(CsrfToken) request.getAttribute(CsrfToken.class.getName());if(requiresValidation(request)){String headerToken = request.getHeader(token.getHeaderName());String cookieToken =getCookieValue(request,"CSRF-TOKEN");if(!token.getToken().equals(headerToken)||!token.getToken().equals(cookieToken)){ response.sendError(HttpStatus.FORBIDDEN.value());return;}} filterChain.doFilter(request, response);}privatebooleanrequiresValidation(HttpServletRequest request){return"POST".equalsIgnoreCase(request.getMethod())||"PUT".equalsIgnoreCase(request.getMethod())||"DELETE".equalsIgnoreCase(request.getMethod());}}

4.3 SameSite Cookie策略

限制cookie的跨站请求

@BeanpublicSecurityFilterChainsecurityFilterChain(HttpSecurity http)throwsException{ http .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).csrfTokenRequestHandler(newCsrfTokenRequestAttributeHandler())).sessionManagement(session -> session .sessionCookiePolicy(cookie -> cookie.sameSite(SameSite.STRICT)));return http.build();}

结语

CSRF 攻击凭借“利用用户身份” 的特点,对任何依赖 Cookie 的状态修改接口都构成威胁。本文从攻击原理入手,详细介绍了同步令牌双重提交 CookieSameSite 属性等防护方案,并给出了对应代码供小伙伴们参考!

希望这个章节的内容能够帮助小伙伴们更深入地理解 CSRF 的知识,在实际项目中设计出更灵活高效的安全策略。如果你在实践过程中有任何疑问或更好的扩展思路,欢迎在评论区留言,最后希望大家 一键三连 给博主一点点鼓励!


下一章节:最新Spring Security实战教程(十二)CORS安全配置 - 跨域请求的安全边界设定

Read more

小白入门:前端前端调用 AI 接口全流程(附具体案例)

很多前端新手在调用 AI 接口时会犯怵:不知道 “怎么怎么传参数?”“流式响应怎么处理?”“不同功能(润色 / 扩写)调用方式不一样吗?” 其实很简单!本文以 “智能文本处理工具” 为例,手把手教你从 0 到 1 调用 AI 接口,包含润色、扩写等功能,看完就能上手。 准备工作:先看懂这 3 个核心文件 在开始前,我们需要明确项目中 3 个关键文件的作用(这些文件你可能已经有了,只是不知道怎么用): * vite.config.js:配置后端接口代理,解决跨域问题 * apiClient.js:封装好的 HTTP 请求工具,帮你发请求 * aiService.js:封装好的 AI 功能函数(

By Ne0inhk
《算法闯关指南:优选算法--前缀和》--27.寻找数组的中心下标,28.除自身以外数组的乘积

《算法闯关指南:优选算法--前缀和》--27.寻找数组的中心下标,28.除自身以外数组的乘积

🔥草莓熊Lotso:个人主页 ❄️个人专栏: 《C++知识分享》《Linux 入门到实践:零基础也能懂》 ✨生活是默默的坚持,毅力是永久的享受! 🎬 博主简介: 文章目录 * 前言: * 27. 寻找数组的中心下标 * 解法(前缀和): * 算法思路: * C++算法代码: * 算法总结&&笔记展示: * 28. 除自身以外数组的乘积 * 解法(前缀和数组): * 算法思路: * C++算法代码: * 算法总结&&笔记展示: * 结语: 前言: 聚焦算法题实战,系统讲解三大核心板块:优选算法:剖析动态规划、二分法等高效策略,学会寻找“最优解”。 递归与回溯:掌握问题分解与状态回退,攻克组合、排列等难题。 贪心算法:理解“

By Ne0inhk
【优选算法必刷100题】第029-030题(前缀和):和为k的子数组,和可被k整除的子数组

【优选算法必刷100题】第029-030题(前缀和):和为k的子数组,和可被k整除的子数组

🔥个人主页:Cx330🌸 ❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》 《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔 🌟心向往之行必能至 🎥Cx330🌸的简介: 目录 前言: 29. 和为k的子数组 算法原理(前缀和+哈希): 前缀和解法代码(C++): 博主手记(字体还请见谅哈): 30. 和可被k整除的子数组 算法原理(前缀和+哈希): 前置知识补充: 前缀和解法代码(C++): 博主手记(字体还请见谅哈): 结尾: 前言: 聚焦算法题实战,系统讲解三大核心板块:“精准定位最优解”——优选算法,“简化逻辑表达,系统性探索与剪枝优化”——递归与回溯,“以局部最优换全局高效”——贪心算法,讲解思路与代码实现,帮助大家快速提升代码能力 前缀和专题 29.

By Ne0inhk
【数据结构与算法】单链表的综合运用:1.合并两个有序链表 2.分割链表 3.环形链表的约瑟夫问题

【数据结构与算法】单链表的综合运用:1.合并两个有序链表 2.分割链表 3.环形链表的约瑟夫问题

🔥小龙报:个人主页 🎬作者简介:C++研发,嵌入式,机器人等方向学习者 ❄️个人专栏:《C语言》《【初阶】数据结构与算法》 ✨ 永远相信美好的事情即将发生 文章目录 * 前言 * 一、合并两个有序链表 * 1.1题目 * 1.2 算法原理 * 1.3代码 * 二、分割链表 * 2.1题目 * 2.2 算法原理 * 2.3代码 * 三、环形链表的约瑟夫问题 * 3.1题目 * 3.2 算法原理 * 3.3代码 * 总结与每日励志 前言 链表是C语言数据结构的核心内容,也是算法面试的高频考点,其灵活的指针操作与逻辑构建对编程思维要求颇高。本文聚焦链表经典实操题型,从合并有序链表、分割链表到环形链表约瑟夫问题,由浅入深拆解解题思路,

By Ne0inhk