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

news2024/9/20 11:06:17

一、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中
	@Override
	public 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
	@Override
	public CsrfToken loadToken(HttpServletRequest request) {
		HttpSession session = request.getSession(false);
		if (session == null) {
			return null;
		}
		return (CsrfToken) session.getAttribute(this.sessionAttributeName);
	}
  // 生成Token 
	@Override
	public 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,如下所示:

@Override
	protected 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.coloradmin.cn/o/2148748.html

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

相关文章

关于广告投放平台的设计

文章目录 广告投放平台的作用广告平台的核心功能 最近在看关于广告投放平台相关的设计&#xff0c;倒是没看到完整的案例介绍。整理一下收集的各种信息&#xff0c;假如需要设计一个广告投放系统&#xff0c;该怎么做&#xff1f; 广告投放平台的作用 要体现广告投放平台的作用…

如何实现LLM的通用function-calling能力?

众所周知&#xff0c;LLM的函数function-calling能力很强悍&#xff0c;解决了大模型与实际业务系统的交互问题。其本质就是函数调用。 从openai官网摘图&#xff1a; 简而言之&#xff1a; LLM起到决策的作用&#xff0c;告知业务系统应该调用什么函数&#xff0c;以及入参是…

动物识别系统Python+卷积神经网络算法+TensorFlow+人工智能+图像识别+计算机毕业设计项目

一、介绍 动物识别系统。本项目以Python作为主要编程语言&#xff0c;并基于TensorFlow搭建ResNet50卷积神经网络算法模型&#xff0c;通过收集4种常见的动物图像数据集&#xff08;猫、狗、鸡、马&#xff09;然后进行模型训练&#xff0c;得到一个识别精度较高的模型文件&am…

Android ImageView支持每个角的不同半径

Android ImageView支持每个角的不同半径 import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import an…

css 控制虚线刻度尺寸

文章目录 css效果 css <div style"width: 100%; height: 1px;background-image: linear-gradient(to right, #545454 0%, #545454 80%, transparent 5%);background-size: 15px 10px;background-repeat: repeat-x; margin: 0 auto;"></div>效果

W外链如何实现长链接转短链接教程

要实现微信外链的长链接转短连接&#xff0c;可以借助专门的工具来简化流程并增加链接的安全性和稳定性。 以下是一个具体方案&#xff1a; 使用W外链工具 W外链是一款集成了多种功能的微信外链生成器&#xff0c;包括但不限于短链制作、活码生成、微信外链制作等。以下是使用…

设置PDF打开密码

为PDF文件设置打开密码是一种有效的保护措施&#xff0c;它能防止未经授权的用户访问文件内容。以下是一份专业指南&#xff0c;详细介绍如何为PDF文件设置打开密码。 打开pdf编辑器&#xff0c;我们点击工具栏中的【文件】功能&#xff0c;选择里面的【属性】 然后在属性设置…

英飞凌PSoC4000T的GPIO中断示例工程

关于PSoC4000T的初步介绍见:英飞凌MCU第五代高性能CAPSENSE技术PSoC4000T_psoc 4000t-CSDN博客 下面这个工程,在modustoolbox中可编译、下载到开发板、debug调试。 编译时会用到mtb_shared这个库: 已经pdl这个periperal driver library库:

SMS over IP原理

目录 1. 短消息业务的实现方式 2. 传统 CS 短消息业务中的发送与送达报告 3. MAP/CAP 信令常见消息 4. SMS over IP 特点概述 5. SMS over IP 中的主要流程 5.1 短消息注册流程(NR 或 LTE 接入) 5.2 短消息发送(MO)流程(NR 或 LTE 接入) 5.3 短消息接收(MT)流程(NR 或…

国际知名度最高的华人改名大师颜廷利:当代最牛的易经姓名学泰斗

国际知名度最高的华人改名大师颜廷利&#xff1a;当代最牛的易经姓名学泰斗 颜廷利教授&#xff0c;一位在姓名学领域享有盛誉的专家&#xff0c;其声誉根植于齐鲁大地&#xff0c;山东济南历城区唐王镇&#xff08;现升级为历城区唐王街道办事处&#xff09;。他的工作基地不仅…

YOLOv8改进系列,YOLOv8的Neck替换成AFPN(CVPR 2023)

摘要 多尺度特征在物体检测任务中对编码具有尺度变化的物体非常重要。多尺度特征提取的常见策略是采用经典的自上而下和自下而上的特征金字塔网络。然而,这些方法存在特征信息丢失或退化的问题,影响了非相邻层次的融合效果。一种渐进式特征金字塔网络(AFPN),以支持非相邻…

【Git原理与使用】版本管理与分支管理(1)

目录 一、基本操作 1、初识Git 2、Git安装[Linux-centos] 3、Git安装[ Linnx-ubuntu] 4、创建git本地仓库 5、配置Git 6、认识工作区、暂存区、版本库 7、添加文件 8、查看历史提交记录 9、查看.git文件目录结构 10、查看版本库对象的内容 11、小结&#xff08;在本地的.git仓库…

计算机毕业设计 服装生产管理系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

什么是安全漏洞?最全的漏洞分类!

01 — “ 什么是漏洞**”** 漏洞是指一个系统存在的弱点或缺陷&#xff0c;系统对特定威胁攻击或危险事件的敏感性&#xff0c;或进行攻击的威胁作用的可能性。漏洞可能来自应用软件或操作系统设计时的缺陷或编码时产生的错误&#xff0c;也可能来自业务在交互处理过程中的设…

想要精益生产管理体系早落地,这些工作不能少!

精益生产管理体系是当代社会将企业管理的各种工具方法和理论、技术以及企业文化融为一体的产物。 推行精益生产管理体系时&#xff0c;企业一定要做好以下3个方面的工作&#xff1a; 1.改变观念&#xff0c;从管理层到员工层都必须深入的贯彻实施精益化生产的思想&#xff0c;…

【分享】“可恶”的运算放大器电容负载

他们说如果使用放大器驱动电容负载(图 1、CLOAD)&#xff0c;一个不错的经验是采用一个 50 或 100 欧的电阻器 (RISO) 将放大器与电容器隔开。这个附加电阻器可能会阻止运算放大器振荡。 图 1.支持电容负载的放大器可能需要在放大器输出与负载电容器之间连接一个电阻器。 使用…

Gitlab runner的使用示例(二):Maven + Docker 自动化构建与部署

Gitlab runner的使用示例&#xff08;二&#xff09;&#xff1a;Maven Docker 自动化构建与部署 在本篇文章中&#xff0c;我们将详细解析一个典型的 GitLab CI/CD 配置文件&#xff08;gitlab-ci.yml&#xff09;&#xff0c;该文件主要用于通过 Maven 构建 Java 应用&…

电脑明明切换到了中文输入法,却无法打字出汉字?

现象&#xff1a; 自己电脑桌面右下角的电脑输入法&#xff0c;已经是中文了 解决办法&#xff1a; 按一下键盘最左边的【Caps Lock】键&#xff0c; 电脑左上角会出现如下弹窗&#xff0c; 调整为&#xff1a;CAPS LOCK OFF 即可&#xff08;OFF时&#xff0c;才能打出汉字…

EW内网穿透详解!

EW EW又叫earthworm&#xff0c;是一套便捷式的网络穿透工具&#xff0c;具有socks5服务架构和端口转发两大核心功能&#xff0c;可以在复杂的网络环境下完成网络穿透。该工具能以"正向"&#xff0c;"反向"&#xff0c;"多级级联"等方式打通一条…

数据结构之线性表——LeetCode:67. 二进制求和,27. 移除元素,26. 删除有序数组中的重复项

67. 二进制求和 题目描述 67. 二进制求和 给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。 运行代码&#xff08;javaC) class Solution {public String addBinary(String a, String b) {StringBuilder ansnew StringBuilder();int ca0;for(i…