SpringSecurity原理解析(八):CSRF防御解析

一、CsrfFilter

       CsrfFilter 主要功能是用来防止csrf攻击

一、什么是CSRF攻击

       跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者

       session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web

       应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS利用的是

       用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

       跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己

       曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。

       由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了

        web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却

       不能保证请求本身是用户自愿发出的。举个例子如下:

               

       

二、如何解决CSRF攻击?

2.1、检查Referer字段

         HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数

         据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。以上文银行操作为

         例,Referer字段地址通常应该是转账按钮所在的网页地址,应该也位于www.bankchina.com

         之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位

         于www.bankhacker.com之下,这时候服务器就能识别出恶意的访问。

         如下图所示:

                

         这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验,来检查 Referer字段。

         但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对

         此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,也无法保证浏览器

         没有安全漏洞影响到此Referer字段。并且也存在攻击者攻击某些浏览器,篡改其Referer

         字段的可能。

2.2、采用 CsrfToken 的方式解决CSRF攻击

         CSRF攻击是在用户登录且没有退出浏览器的情况下访问了第三方的站点而被攻击的,

         完全是携带了认证的cookie来实现的,我们只需要在服务端响应给客户端的页面中绑定

         随机的信息,然后提交请求后在服务端校验,如果携带的数据和之前的不一致就认为是

         CSRF攻击,拒绝这些请求即可。流程如下图所示:

                   

三、SpringSecurity 是如何解决CSRF攻击的

       从 Spring Security 4.0 开始,默认情况下会启用 CSRF 保护,以防止 CSRF 攻击应用程序,

      Spring Security CSRF 会针对 PATCH,POST,PUT 和 DELETE 方法进行防护。

3.1、开启/关闭 CSRF防御

         在SpringSecurity中默认是开启csrf防御的,下边看下如何来关闭csrf防御

        1)基于配置类的方式关闭csrf防御

             在自定义SpringSecurity配置文件中的configure方法中,通过 HttpSecurity 先调用csrf()

             方法获取CSRF,然后调用 disable() 方法就可以关闭 csrf防御;

             即:http.csrf().disable();

             如下图所示:

                    

         2)基于配置文件的方式关闭csrf防御

               在SpringSecurity配置文件中,在标签<security:http>内部添加标签

               <security:csrf disabled="true"/> 就可以关闭csrf防御,如下图所示:

                      

3.2、SpringSecurity 中CSRF防御实现原理

3.2.1、SpringSecurity中CSRF的实现流程

            1)当用户访问受保护的资源时,Spring Security 会检查请求中是否包含有效的 CSRF

                 令牌csrfToken,生成csrfToken保存到HttpSession或者Cookie中

            2)请求到来时,程序会从请求中获取提交的csrfToken,同时会从HttpSession中获取

                 之前存储的csrfToken进行比较,如果相同则认为是合法的请求,继续后面的操作,

                 如果不相等则认为是CSRF攻击,拒绝该请求

3.2.2、CSRF防御实现原理

1)CsrfToken

      CsrfToken是一个非常简单的接口,定义了Token令牌,消息头和请求参数。

      CsrfToken 接口定义如下:         

public interface CsrfToken extends Serializable {/*** 获取我们放置在请求头中CSRF随机值的名称*/String getHeaderName();/*** 获取请求体中的csrf随机值的参数名称*/String getParameterName();/*** 返回具体的Token值*/String getToken();}

      CsrfToken的默认实现是类 DefaultCsrfToken,DefaultCsrfToken也很简单,其只要功能是

      用来初始化 请求头中CSRF随机值的名称、请求体中CSRF随机值的参数名称 和 token;

      如下图所示:

             

      

2)CsrfTokenRepository

      CsrfTokenRepository 也是一个接口,其定义了token(CSRF令牌)的生成、存储和

      获取的相关方法;

      CsrfTokenRepository 是 Spring Security 中用于处理 CSRF 保护的重要组件之一。通过

      实现 CsrfTokenRepository 接口并重写其中的方法,我们可以根据具体的业务需求自定义

      CSRF 令牌的生成、存储和获取逻辑。其运行过程是在用户访问受保护的资源时被调用,

      用于确保请求的合法性。

      CsrfTokenRepository 定义如下:     

public interface CsrfTokenRepository {/*** 生成Token*/CsrfToken generateToken(HttpServletRequest request);/*** 存储生成的Token*/void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response);/*** 返回Token*/CsrfToken loadToken(HttpServletRequest request);}

      CsrfTokenRepository 在中有3个实现,即:

             CookieCsrfTokenRepository

             HttpSessionCsrfTokenRepository

             LazyCsrfTokenRepository

       默认实现是 HttpSessionCsrfTokenRepository,是一个基于HttpSession保存csrfToken

       的实现。

       HttpSessionCsrfTokenRepository 定义如下:

public final class HttpSessionCsrfTokenRepository implements CsrfTokenRepository {private static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf";private static final String DEFAULT_CSRF_HEADER_NAME = "X-CSRF-TOKEN";private static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN");private String parameterName = DEFAULT_CSRF_PARAMETER_NAME;private String headerName = DEFAULT_CSRF_HEADER_NAME;private String sessionAttributeName = DEFAULT_CSRF_TOKEN_ATTR_NAME;// 保存Token到session中@Overridepublic void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {if (token == null) {HttpSession session = request.getSession(false);if (session != null) {session.removeAttribute(this.sessionAttributeName);}}else {HttpSession session = request.getSession();session.setAttribute(this.sessionAttributeName, token);}}// 从session中加载token@Overridepublic CsrfToken loadToken(HttpServletRequest request) {HttpSession session = request.getSession(false);if (session == null) {return null;}return (CsrfToken) session.getAttribute(this.sessionAttributeName);}// 生成Token @Overridepublic CsrfToken generateToken(HttpServletRequest request) {return new DefaultCsrfToken(this.headerName, this.parameterName, createNewToken());}/*** Sets the {@link HttpServletRequest} parameter name that the {@link CsrfToken} is* expected to appear on* @param parameterName the new parameter name to use*/public void setParameterName(String parameterName) {Assert.hasLength(parameterName, "parameterName cannot be null or empty");this.parameterName = parameterName;}/*** Sets the header name that the {@link CsrfToken} is expected to appear on and the* header that the response will contain the {@link CsrfToken}.* @param headerName the new header name to use*/public void setHeaderName(String headerName) {Assert.hasLength(headerName, "headerName cannot be null or empty");this.headerName = headerName;}/*** Sets the {@link HttpSession} attribute name that the {@link CsrfToken} is stored in* @param sessionAttributeName the new attribute name to use*/public void setSessionAttributeName(String sessionAttributeName) {Assert.hasLength(sessionAttributeName, "sessionAttributename cannot be null or empty");this.sessionAttributeName = sessionAttributeName;}// 通过UUID来生成Token信息private String createNewToken() {return UUID.randomUUID().toString();}}

    

3)CsrfFilter

     CsrfFilter用于处理跨站请求伪造(即执行 CsrfTokenRepository生成 的CSRF令牌校验的拦截

     器)。

     检查表单提交的_csrf隐藏域的value与内存中保存的的是否一致,如果一致框架则认为

    登录页面是安全的,如果不一致,会报403forbidden错误。

     CsrfFilter 中处里请求的方法是 doFilterInternal,如下所示:

@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {request.setAttribute(HttpServletResponse.class.getName(), response);
// tokenRepository即 CsrfTokenRepository 对象
// 从session中加载 Token,即CSRF令牌CsrfToken csrfToken = this.tokenRepository.loadToken(request);boolean missingToken = (csrfToken == null);
// 如果是第一次访问就生成Token信息if (missingToken) {csrfToken = this.tokenRepository.generateToken(request);
// 把生成的Token信息存储在Session中this.tokenRepository.saveToken(csrfToken, request, response);}request.setAttribute(CsrfToken.class.getName(), csrfToken);request.setAttribute(csrfToken.getParameterName(), csrfToken);
// 匹配是否是需要做CSRF防御的相关请求if (!this.requireCsrfProtectionMatcher.matches(request)) {if (this.logger.isTraceEnabled()) {this.logger.trace("Did not protect against CSRF since request did not match "+ this.requireCsrfProtectionMatcher);}filterChain.doFilter(request, response);return;}
// 获取请求携带在header中的Token信息String actualToken = request.getHeader(csrfToken.getHeaderName());if (actualToken == null) {
// 从请求参数中获取Token信息actualToken = request.getParameter(csrfToken.getParameterName());}
// 判断请求中的Token是否和Session中存储的Token相等if (!equalsConstantTime(csrfToken.getToken(), actualToken)) {this.logger.debug(LogMessage.of(() -> "Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request)));
// Token不相等,说明是CSRF攻击,抛出访问拒绝的异常AccessDeniedException exception = (!missingToken) ? new InvalidCsrfTokenException(csrfToken, actualToken): new MissingCsrfTokenException(actualToken);this.accessDeniedHandler.handle(request, response, exception);return;}
// 说明是正常的访问,放过filterChain.doFilter(request, response);}

       

   

            

         

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

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

相关文章

【IP协议】IP协议报头结构

文章目录 IP 协议报头结构4位版本4位首部长度8位服务类型16位总长度16位标识、3位标志、13位片偏移8位生存时间8位协议16位首部校验和32源 IP 地址、32位目的 IP 地址 IP 协议报头结构 4位版本 实际上只有两个取值 4 > IPv4&#xff08;主流&#xff09;6 > IPv6 IPv2&…

浅谈人工智能之基于ollama本地大模型结合本地知识库搭建智能客服

浅谈人工智能之基于ollama本地大模型结合本地知识库搭建智能客服 摘要 随着人工智能技术的飞速发展,基于大型语言模型(LLMs)的智能客服系统逐渐成为提升企业服务质量和效率的关键工具。然而,对于注重数据隐私和安全的企业而言,使用云服务可能会引发数据泄露的风险。因此…

【C++题解】1996. 每个小组的最大年龄

欢迎关注本专栏《C从零基础到信奥赛入门级&#xff08;CSP-J&#xff09;》 问题&#xff1a;1996. 每个小组的最大年龄 类型&#xff1a;二维数组 题目描述&#xff1a; 同学们在操场上排成了一个 n 行 m 列的队形&#xff0c;每行的同学属于一个小组&#xff0c;请问每个小…

2022高教社杯全国大学生数学建模竞赛C题 问题一(1) Python代码演示

目录 问题 11.1 对这些玻璃文物的表面风化与其玻璃类型、纹饰和颜色的关系进行分析数据探索 -- 单个分类变量的绘图树形图条形图扇形图雷达图Cramer’s V 相关分析统计检验列联表分析卡方检验Fisher检验绘图堆积条形图分组条形图分类模型Logistic回归随机森林import matplotlib…

SPI学习笔记

SPI SPI是一种同步串行通信接口规范&#xff0c;它允许一个主设备与一个或多个从设备进行全双工通信。SPI用于短距离通信&#xff0c;主要应用于嵌入式系统。 SPI通信过程 1.初始化&#xff1a;SPI主机首先将SS或CS线拉低&#xff0c;以选择特定的从设备并开始通信。 2.数据…

linux文件系统权限详解

注:目录的执行权限代表是否可以进入。 一、文件权限控制对文件的访问: 可以针对文件所属用户、所属组和其他用户可以设置不同的权限 权限具有优先级。user权限覆盖group权限,后者覆盖other权限。 有三种权限类别:读取、写入和执行 读权限:对文件:可读取文件…

集群聊天服务器项目【C++】(五)网络模块和业务模块

经过前面介绍相关的库和工具&#xff0c;比如Json、CMake、muduo等&#xff0c;我们可以开始编写本项目的代码了。 1.项目目录创建 一般一个项目由以下结构组成&#xff1a; bin文件夹存放&#xff1a;可执行程序build文件夹存放&#xff1a;编译过程中的临时文件include文…

电子竞技信息交流平台|基于java的电子竞技信息交流平台系统小程序(源码+数据库+文档)

电子竞技信息交流平台系统小程序 目录 基于java的电子竞技信息交流平台系统小程序 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设…

“拍照赚钱”的任务定价(2017数学建模国赛b题)

文章目录 题目说明解题思路第一问第二问第三问第四问 部分结果图项目地址 题目 赛题地址 说明 数模国赛前的练手题。其实我个人感觉这道题很散&#xff0c;都是找一些规律进行总结统计&#xff0c;最多结合一些机器学习算法进行预测拟合之类的我刚开始用matlab&#xff0c;后…

【演化博弈论】:双方演化博弈的原理与过程

目录 一、演化博弈的原理1. 基本概念2. 参与者的策略3.演化过程 二、MATLAB 代码解读&#xff08;博弈参与主体&#xff08;双方&#xff09;策略选择的动态演化讨程&#xff09;三、MATLAB 代码解读&#xff08;博弈主体随着时间策略选择的动态演化讨程&#xff09;四、结论 演…

Java 枚举 新特性

Java 枚举&#xff08;enum&#xff09;自JDK 1.5引入以来&#xff0c;随着版本的升级不断增强。本文将回顾枚举的演进&#xff0c;尤其是结合switch语句的应用&#xff0c;展示枚举如何在现代Java中变得更加灵活。 1. JDK 1.5&#xff1a;Java 枚举的诞生 在JDK 1.5之前&…

TAG:BladeLLM 的纯异步推理架构

作者&#xff1a;张子鹏 PAI引擎团队 随着 GQA/MLA/MoE 等模型结构不断发展&#xff0c;大语言模型的推理逐步解除了显存限制&#xff0c;逐渐向着高并发、高吞吐的方向发展。推理引擎的运行时开销也变得不可忽视。主流 LLM 推理框架的运行时开销大致来自&#xff1a; Python …

黑马十天精通MySQL知识点

一. MySQL概述 安装使用 MySQL安装完成之后&#xff0c;在系统启动时&#xff0c;会自动启动MySQL服务&#xff0c;无需手动启动。 也可以手动的通过指令启动停止&#xff0c;以管理员身份运行cmd&#xff0c;进入命令行执行如下指令&#xff1a; 1 、 net start mysql80…

Excel图片批量插入单元格排版处理插件【图片大师】

为了方便大家在图片的插入排版的重复工作中解放出来&#xff0c;最近发布了一款批量插入图片的插件&#xff0c;欢迎大家下载&#xff0c;免费试用。 这是图片的文件夹&#xff1a; 主要功能如下: 1&#xff0c;匹配单元格名称的多张图批量插入到一个单元格 该功能支持设置图…

学LabVIEW编程,看编程书有些看不懂怎么办?

自学LabVIEW编程时&#xff0c;如果发现编程书籍内容难以理解&#xff0c;可以尝试以下几种方式来提高学习效果&#xff1a; 1. 从基础入手&#xff0c;逐步深入&#xff1a; LabVIEW是一种基于图形化编程的工具&#xff0c;不同于传统的编程语言&#xff0c;因此从基础概念开…

尤雨溪推荐的拖拽插件,支持Vue2/Vue3 VueDraggablePlus

大家好,我是「前端实验室」爱分享的了不起~ 今天在网上看到尤雨溪推荐的这款拖拽组件,试了一下非常不错,这里推荐给大家。 说到拖拽工具库,非大名鼎鼎的的 Sortablejs 莫属。它是前端领域比较知名的,且功能强大的工具。但我们直接使用Sortablejs的情况很少,一般都是使用…

【Redis】之Geo

概述 Geo就是Geolocation的简写形式&#xff0c;代表地理坐标。在Redis中&#xff0c;构造了能够存储地址坐标信息的一种数据结构&#xff0c;帮助我们根据经纬度来检索数据。 命令行操作方法 GEOADD 可以用来添加一个或者多个地理坐标。 GEODIST 返回一个key中两个成员之…

【专题】2024跨境出海供应链洞察-更先进供应链报告合集PDF分享(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p37665 当前&#xff0c;全球化商业浪潮促使跨境电商行业飞速发展&#xff0c;产业带与跨境电商接轨、平台半托管模式涌现、社交电商带来红利机会以及海外仓不断扩张&#xff0c;这使得产业带外贸工厂、内贸工厂、传统进出口企业和品…

自制一键杀死端口进程程序# tomcat 如何杀死tomcat进程

直接cmd 窗口执行如下命令即可 netstat -ano | findstr :8080 taskkill /F /PID <PID>简简单单的两个指令,总是记不住,也懒的记, 每次端口冲突的时候, 都是直接查百度,很苦逼, 如果有一个程序,直接输入端口号,点击按钮直接杀死进程,岂不爽歪歪. 跟我一起制作一个屠猫的…

【D3.js in Action 3 精译_022】3.2 使用 D3 完成数据准备工作

当前内容所在位置 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可视化最佳实践&#xff08;下&#xff09;1.4 本章小结 第二章…