《Java实战开发》利用spring-security解决CSRF问题,通过重写CsrfFilter 过滤掉指定方法
最近项目渗透测试检测出一些安全问题其中一项为csrf攻击隐患,然后开始修复
csrf简介
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。——百度百科
详细介绍:浅谈CSRF攻击方式 - hyddd - 博客园
一、maven pom.xml 引入jar包
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId><version>5.0.6.RELEASE</version></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId><version>5.0.6.RELEASE</version></dependency>
二、web.xml配置filter
mgmtCsrfFilter是我重写的csrfFilter名称
<!-- CSRF filter --><filter><filter-name>mgmtCsrfFilter</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><filter-mapping><filter-name>mgmtCsrfFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
三、spring-application.xml配置
<bean id="mgmtCsrfFilter" class="com.haha.sps.mgmt.core.filter.MgmtCsrfFilter"><constructor-arg><bean class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository" /></constructor-arg></bean><bean id="requestDataValueProcessor" class="org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor"></bean>
四、重写csrffilter
重写后的csrfFilter为MgmtCsrfFilter代码如下:
package com.haha.sps.mgmt.core.filter;import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.InvalidCsrfTokenException;
import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;/*** Rewrite org.springframework.security.web.csrf.CsrfFilter * exclude user/doLogion method filter** @author Pente*/
public final class MgmtCsrfFilter extends OncePerRequestFilter {/*** The default {@link RequestMatcher} that indicates if CSRF protection is required or* not. The default is to ignore GET, HEAD, TRACE, OPTIONS and process all other* requests.*/public static final RequestMatcher DEFAULT_CSRF_MATCHER = new CsrfSecurityRequestMatcher();private final Log logger = LogFactory.getLog(getClass());private final CsrfTokenRepository tokenRepository;private RequestMatcher requireCsrfProtectionMatcher = DEFAULT_CSRF_MATCHER;private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();public MgmtCsrfFilter(CsrfTokenRepository csrfTokenRepository) {Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");this.tokenRepository = csrfTokenRepository;}/** (non-Javadoc)** @see* org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(javax.servlet* .http.HttpServletRequest, javax.servlet.http.HttpServletResponse,* javax.servlet.FilterChain)*/@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {request.setAttribute(HttpServletResponse.class.getName(), response);CsrfToken csrfToken = this.tokenRepository.loadToken(request);final boolean missingToken = csrfToken == null;if (missingToken) {csrfToken = this.tokenRepository.generateToken(request);this.tokenRepository.saveToken(csrfToken, request, response);}request.setAttribute(CsrfToken.class.getName(), csrfToken);request.setAttribute(csrfToken.getParameterName(), csrfToken);if (!this.requireCsrfProtectionMatcher.matches(request)) {filterChain.doFilter(request, response);return;}String actualToken = request.getHeader(csrfToken.getHeaderName());if (actualToken == null) {actualToken = request.getParameter(csrfToken.getParameterName());}if (!csrfToken.getToken().equals(actualToken)) {if (this.logger.isDebugEnabled()) {this.logger.debug("Invalid CSRF token found for "+ UrlUtils.buildFullRequestUrl(request));}if (missingToken) {this.accessDeniedHandler.handle(request, response,new MissingCsrfTokenException(actualToken));}else {this.accessDeniedHandler.handle(request, response,new InvalidCsrfTokenException(csrfToken, actualToken));}return;}filterChain.doFilter(request, response);}/*** Specifies a {@link RequestMatcher} that is used to determine if CSRF protection* should be applied. If the {@link RequestMatcher} returns true for a given request,* then CSRF protection is applied.** <p>* The default is to apply CSRF protection for any HTTP method other than GET, HEAD,* TRACE, OPTIONS.* </p>** @param requireCsrfProtectionMatcher the {@link RequestMatcher} used to determine if* CSRF protection should be applied.*/public void setRequireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher) {Assert.notNull(requireCsrfProtectionMatcher,"requireCsrfProtectionMatcher cannot be null");this.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;}/*** Specifies a {@link AccessDeniedHandler} that should be used when CSRF protection* fails.** <p>* The default is to use AccessDeniedHandlerImpl with no arguments.* </p>** @param accessDeniedHandler the {@link AccessDeniedHandler} to use*/public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {Assert.notNull(accessDeniedHandler, "accessDeniedHandler cannot be null");this.accessDeniedHandler = accessDeniedHandler;}/** exclude "user/doLogion" "GET", "HEAD", "TRACE", "OPTIONS"*/private static final class CsrfSecurityRequestMatcher implements RequestMatcher {private RegexRequestMatcher unprotectedMatcher = new RegexRequestMatcher("^/user/doLogin", null);private final HashSet<String> allowedMethods = new HashSet<String>(Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));/** (non-Javadoc)* @see* org.springframework.security.web.util.matcher.RequestMatcher#matches(javax.* servlet.http.HttpServletRequest)*/@Overridepublic boolean matches(HttpServletRequest request) {if(this.allowedMethods.contains(request.getMethod())){return false;}return !unprotectedMatcher.matches(request);}}
}
实现自定义过滤掉登录方法,主要是重写csrfFilter里的DefaultRequiresCsrfMatcher替换成自己写的CsrfSecurityRequestMatcher ,如下:
/** exclude "user/doLogion" "GET", "HEAD", "TRACE", "OPTIONS"*/private static final class CsrfSecurityRequestMatcher implements RequestMatcher {private RegexRequestMatcher unprotectedMatcher = new RegexRequestMatcher("^/user/doLogin", null);private final HashSet<String> allowedMethods = new HashSet<String>(Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));/** (non-Javadoc)* @see* org.springframework.security.web.util.matcher.RequestMatcher#matches(javax.* servlet.http.HttpServletRequest)*/@Overridepublic boolean matches(HttpServletRequest request) {if(this.allowedMethods.contains(request.getMethod())){return false;}return !unprotectedMatcher.matches(request);}}
五、页面配置
在post请求或ajax请求中会遇到问题,在公用jsp文件中加上,例如每个页面都会引用的头文件top.jsp
<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/><script>var token = $("meta[name='_csrf']").attr("content");var header = $("meta[name='_csrf_header']").attr("content");$.ajaxSetup({beforeSend: function (xhr) {if(header && token ){xhr.setRequestHeader(header, token);}}});
// $(document).ajaxSend(function(e,xhr,opt){
// xhr.setRequestHeader(header,token);
// });</script>
$.ajaxSetup的意思就是给我们所有的请求都加上这个header和token,或者放到form表单中。注意,_csrf这个要与spring security的配置文件中的配置相匹配,默认为_csrf。
六、源码解析
看一下CsrfFilter中的doFilterInternal源码解析就知道我们为什么这么修改了
@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {request.setAttribute(HttpServletResponse.class.getName(), response);// 先从tokenRepository中加载tokenCsrfToken csrfToken = this.tokenRepository.loadToken(request);final boolean missingToken = csrfToken == null;// 如果为空,则tokenRepository生成新的token,并保存到tokenRepository中if (missingToken) {csrfToken = this.tokenRepository.generateToken(request);this.tokenRepository.saveToken(csrfToken, request, response);}// 将token写入request的attribute中,方便页面上使用request.setAttribute(CsrfToken.class.getName(), csrfToken);request.setAttribute(csrfToken.getParameterName(), csrfToken);//这个macher就是我们在Spring配置文件中自定义的过滤器,也就是GET,HEAD, TRACE, OPTIONS和我们的rest都不处理// 这个macher就是我们在Spring配置文件中自定义的过滤器,//如果不需要csrf验证的请求,则直接下传请求(requireCsrfProtectionMatcher是默认的对象,对符合^(GET|HEAD|TRACE|OPTIONS)$的请求和我们自定义的请求不验证)if (!this.requireCsrfProtectionMatcher.matches(request)) {filterChain.doFilter(request, response);return;}// 从用户请求中获取token信息String actualToken = request.getHeader(csrfToken.getHeaderName());if (actualToken == null) {actualToken = request.getParameter(csrfToken.getParameterName());}// 验证,如果相同,则下传请求,如果不同,则抛出异常if (!csrfToken.getToken().equals(actualToken)) {if (this.logger.isDebugEnabled()) {this.logger.debug("Invalid CSRF token found for "+ UrlUtils.buildFullRequestUrl(request));}if (missingToken) {this.accessDeniedHandler.handle(request, response,new MissingCsrfTokenException(actualToken));}else {this.accessDeniedHandler.handle(request, response,new InvalidCsrfTokenException(csrfToken, actualToken));}return;}filterChain.doFilter(request, response);}
文章结构部分参考:利用spring-security解决CSRF问题_Frankenstein的博客-CSDN博客
《Java实战开发》利用spring-security解决CSRF问题,通过重写CsrfFilter 过滤掉指定方法相关推荐
- springsecurity sessionregistry session共享_要学就学透彻!Spring Security 中 CSRF 防御源码解析...
今日干货 刚刚发表查看:66666回复:666 公众号后台回复 ssm,免费获取松哥纯手敲的 SSM 框架学习干货. 上篇文章松哥和大家聊了什么是 CSRF 攻击,以及 CSRF 攻击要如何防御.主要 ...
- 要学就学透彻!Spring Security 中 CSRF 防御源码解析
上篇文章松哥和大家聊了什么是 CSRF 攻击,以及 CSRF 攻击要如何防御.主要和大家聊了 Spring Security 中处理该问题的几种办法. 今天松哥来和大家简单的看一下 Spring Se ...
- Spring Security 的 CSRF 的相关资料
近期,因为需要研究 Spring Security 的安全机制,因为 Spring Security 说可以帮助避免 CSRF 攻击. 因此特地考古了相关的内容. 简单点解释就是 CSRF 盗用了你的 ...
- Spring Boot 项目使用Spring Security防护CSRF攻击实战
Spring Boot与Spring Security Spring Boot项目结合Spring Security ,可以实现用户认证和用户授权. Spring Security的用户认证可以是配置 ...
- CSRF攻击的原理和spring security对CSRF攻击的解决方法
对于CSRF攻击的原理,直接上图然后解释一下 一个用户通过浏览器成功登录一个网站,登陆成功后,服务器会返回一个该用户的唯一标识放入浏览器Cookie中,以此作为用户之后操作的唯一凭证.假设此时该用户在 ...
- 千锋教育威哥学Java——爆破专栏丨Spring Security系列教程之解决Spring Security环境中的跨域问题
前言 上一章节中,一一哥 给各位讲解了同源策略和跨域问题,以及跨域问题的解决方案,在本篇文章中,我会带大家进行代码实现,看看在Spring Security环境中如何解决跨域问题. 需要更多教程,微信 ...
- Spring Boot实践 | 利用Spring Security快速搞定权限控制
目录 开始之前 快速开始 使用内存签名服务 使用数据库签名服务 使用自定义签名服务 限制请求 强制使用HTTPS 防止跨站点伪造请求 用户认证功能 在java web工程中,一般使用Servlet过滤 ...
- Java认证授权框架Spring Security介绍
Spring Security 是一个非常强大的身份验证和授权控制框架.为了满足企业项目的不同需求,它提供了很多定制化开发的解决方案,通过简单的调整配置,就能为我们的应用提供一套可靠的安全保障.本节课 ...
- Spring Boot+Vue/前后端分离/高并发/秒杀实战课程之spring Security快速搭建oauth2 内存版身份认证
Springboot快速搭建oauth2 内存版身份认证 环境准备 点击[Create New Project]创建一个新的项目 项目环境配置 配置Thymeleaf 搭建oauth2认证,加入两个依 ...
最新文章
- python中空格字符是什么_关于Python中空格字符串处理的技巧总结
- 使用二进制的方式安装mysql实践纪要
- 0-1语言建模当中会遇到的问题
- 组合总和(可重复使用)Python解法
- python deepcopy报错_python 字典对象赋值之deepcopy遭遇的问题及解决过程(lxml惹的祸)...
- 电脑鸿蒙运行Linux程序,Linux下的Hi3861一站式鸿蒙开发烧录(附工具)-鸿蒙开发烧录工具软件电脑版-东坡下载...
- 导师推荐 | 第 4 期临床基因组家系分析,同时解决科研和临床问题
- 【MyBatis笔记】11-分步查询懒加载
- pycharm pip安装_Python从入门到大师教程 | 一、搭建Python环境和安装Pycharm
- 【翻译】Robust Lane Detection and Tracking in Challenging Scenarios
- 螺旋数字的python实现
- AR引擎vuforia源码分析、中文注释(1)
- 30种EMC标准电路分享,再不收藏就晚了!
- gRPC python封装深度学习算法教程
- 图像加密之灰度加密:基于 密钥 × 解钥 ≡ 1 mod 灰度级 的一轮加密算法例子——lena图
- python3爬取网易云歌曲,利用python3爬取网易云周杰伦所有专辑,歌曲,评论,并完成可视-站长资讯中心...
- 【Python实战项目】做一个 刮刮乐 案例,一不小心....着实惊艳到我了。
- 读书笔记10 《蔡康永的说话之道1》 蔡康永
- 漫画:大公司病了,这也太形象了吧!!!
- [WDS] Disconnected解决方法
热门文章
- 告诉你为什么要懂电机控制
- linux dwm 中文输入法,我最近在dwm上安装中文输入法fcitx。但是遇到了一些问题。...
- 计算机毕业设计ssm电商后台系统c83si系统+程序+源码+lw+远程部署
- Cordova 扫码插件整理-cordova-plugin-qrscanner
- Android类似于桌面360小球加速效果
- 社群运营必备的5大技巧和工具,请收下
- 向浙江股友推荐一匹家门口的黑马...................................
- 航天器动力学建模笔记
- 米拓模板:家电行业网站模板推荐
- 软件测试_BadBoy自动化测试工具2_录制脚本