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.xdnf.cn/news/142324.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

有关C# .NET Core 过滤器的使用

想用一个过滤器实现特定接口的审核日志记录&#xff0c;结果报了错&#xff0c;看了看感觉有些基础要补&#xff0c;所以想记录下来 错误&#xff1a; 在属性过滤器中使用了依赖注入&#xff0c;结果在应用在控制层接口时报了传参的错 //过滤器 public class AuditRecordFil…

数据时代,职场离不开的远程控制工具

中秋了大概率是在正常放假了吧&#xff0c;如果突发遇到需要你处理的文件怎么办呢&#xff1f;其实有远程操作工具你就不用到办公室了。向日葵远程控制软件这些工具就可以帮我们远程实现控制电脑操作。如果你也有这方面需求就继续看吧&#xff0c;这次我将介绍几款我用过效果比…

(c++)字符串相加(真没想到字符串还有相加运算)

#include<iostream> #include<string> using namespace std;int main() {string ch1 "你好";string ch2 "再见";string ch3 ch1 ch2;cout << ch3 << endl;system("pause");return 0; } 运行结果&#xff1a; 学了c…

SpringBoot Kafka发送消息与接收消息实例

前言 Kafka的基本工作原理 我们将消息的发布&#xff08;publish&#xff09;称作 producer(生产者)&#xff0c;将消息的订阅&#xff08;subscribe&#xff09;表述为 consumer&#xff08;消费者&#xff09;&#xff0c;将中间的存储阵列称作 broker(代理)&#xff0c;这…

Qt --- 信号和信号槽

前言 Linux信号Signal&#xff0c;系统内部的通知机制&#xff0c;进程间通信方式。 信号源&#xff1a;谁发的信号。 信号的类型&#xff1a;哪种类别的信号。 信号的处理方式&#xff1a;注册信号处理函数&#xff0c;在信号被触发的时候自动调用执行。 Qt中的信号和Lin…

利士策分享,中秋佳节:月满人团圆的文化传承与演绎

利士策分享&#xff0c;中秋佳节&#xff1a;月满人团圆的文化传承与演绎 在中国丰富多彩的传统节日中&#xff0c;中秋节以其独特的魅力&#xff0c;承载着深厚的文化底蕴和民族情感。 这一节日的起源&#xff0c;宛如一幅缓缓展开的历史画卷&#xff0c;融合了古人对天象的…

栈、队列、树、哈希表

栈 先进后出&#xff0c;添加元素直接memcpy 到对应数组位置就可以&#xff0c;top是栈中存储的元素个数&#xff0c;最后一个元素下标为top-1&#xff1b; 删除元素时直接top--&#xff1b; 后面添加进入的数据会覆盖原来在栈上被删除的数据。 main.c符号匹配 链栈 main.c 队…

为什么说开放式耳机比入耳式的好?学生党必入的蓝牙耳机推荐

因为开放式耳机相比入耳式耳机更具优势&#xff0c;具体如下&#xff1a; 佩戴舒适度更高&#xff1a; 开放式耳机通常不需要插入耳道&#xff0c;不会对耳道产生压迫&#xff0c;长时间佩戴耳朵不易感到闷热、疼痛或不适&#xff0c;减少了对耳部的物理压迫和摩擦&#xff0…

深入浅出Docker

1. Docker引擎 Docker引擎是用来运行和管理容器的核心软件。通常人们会简单的将其指代为Docker或Docker平台。 基于开放容器计划&#xff08;OCI&#xff09;相关的标准要求&#xff0c;Docker引擎采用了模块化的设计原则&#xff0c;其组件是可替换的。 Docker引擎由如下主…

形态学的基本操作在图片中的应用

一、形态学——腐蚀操作 &#xff08;缩小、变细&#xff09; import cv2 import numpy as npimg_pig cv2.imread(pig.png) cv2.imshow(image_pig,img_pig) cv2.waitKey(0) cv2.destroyAllWindows()def cv_show(name, img):cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAl…

element-plus的菜单组件el-menu

菜单是几乎是每个管理系统的软件系统中不可或缺的&#xff0c;element-plus提供的菜单组件可以快速完成大部分的菜单的需求开发&#xff0c; 该组件内置和vue-router的集成&#xff0c;使用起来很方便。 主要组件如下 el-menu 顶级菜单组件 主要属性 mode:决定菜单的展示模式…

MySQL篇(高级字符串函数/正则表达式)(持续更新迭代)

目录 讲点一&#xff1a;高级字符串函数 一、简介 二、常见字符串函数 1. CONCAT() 2. SUBSTRING() 3. LENGTH() 4. REPLACE() 5. TRIM() 6. UPPER() 7. LOWER() 8. LEFT() 9. RIGHT() 10. INSTR() 11. LENTH(str) 讲点二&#xff1a;正则表达式 一、简介 二、…

Defining Additional PhysicalConstraints

步骤3&#xff1a;定义附加物理 约束条件 在此步骤中&#xff0c;您将定义设计的其他物理约束&#xff0c;例如 PACKAGE_PIN和禁止约束。 1.选择布局→I/O规划&#xff0c;从布局选择器打开I/O规划视图布局 在工具栏菜单中。 I/O规划视图布局显示包窗口以及I/O端口和 封装引脚窗…

华为地图服务 - 如何开启和展示“我的位置”? -- HarmonyOS自学10

一. 场景介绍 本章节将向您介绍如何开启和展示“我的位置”功能&#xff0c;“我的位置”指的是进入地图后点击“我的位置”显示当前位置点的功能。效果如下&#xff1a; 二. 接口说明 “我的位置”功能主要由MapComponentController的方法实现&#xff0c;更多接口及使用方法…

学习笔记(一)

前言 一、对象 1、由类建模而成&#xff0c;是消息、数据和行为的组合 2、可以接收和发送消息&#xff0c;并利用消息进行彼此的交互。消息要包含传送给对象接收的信息 3、类的实例化&#xff1a;把类转换为对象的过程叫类的实例化。 4、对象的特性 (1) 对象有状态&#…

QUIC的loss detection学习

PTO backoff backoff 补偿 /ˈbkɒf/PTO backoff 是QUIC&#xff08;Quick UDP Internet Connections&#xff09;协议中的一种机制&#xff0c;用于处理探测超时&#xff08;Probe Timeout, PTO&#xff09;重传策略 它逐步增加探测超时的等待时间&#xff0c;以避免网络拥塞…

【FreeRTOS】任务

1.使用stm32cubemx配置freertos 2.创建任务 我们需要在MX_FREERTOS_Init()里面创建任务 我们根据上面的任务创建方式&#xff0c;实现GPIO_PIN_10的反转 1.任务句柄 2.任务结构体 3.任务执行函数 4.任务函数声明 5.创建线程执行任务 hal_delay和osDelay区别&#xff1f;…

Qt (17)【Qt 文件操作 读写保存】

阅读导航 引言一、Qt文件概述二、输入输出设备类三、文件读写类四、文件和目录信息类五、自定义“记事本” 引言 在上一篇文章中&#xff0c;我们学习了Qt的事件处理机制&#xff0c;知道了如何响应用户的操作。但应用程序常常还需要处理文件&#xff0c;比如读写数据。所以&a…

应用软件系统开发 实操一:任务需求描述

一、实操一&#xff1a;任务需求描述 软件和信息技术服务业数据统计平台是一个为不同级别管理员提供定制化服务的系统。它主要面向数据管理员和系统运维管理员&#xff0c;每一种用户角色各自拥有特定的功能权限。系统运维管理员是专门针对平台基础功能的管理人员&#xff0c;它…

08_Python数据类型_字典

Python的基础数据类型 数值类型&#xff1a;整数、浮点数、复数、布尔字符串容器类型&#xff1a;列表、元祖、字典、集合 字典 字典&#xff08;Dictionary&#xff09;是一种可变容器模型&#xff0c;它可以存储任意类型对象&#xff0c;其中每个对象都存储为一个键值对。…