SpringSecurity跨域请求伪造(CSRF)的防护实现

目录
  • 一、CSRF
  • 二、攻击过程
  • 三、防御手段
    • 1.HTTP Referer
    • 2.CsrfToken认证
  • 三、使用SpringSecurity防御CSRF
    • 1.SpringSecurity防御CSRF过程
    • 2.SpringSecurity配置CSRF
    • 3.启动项目测试

一、CSRF

CSRF的全称是(Cross Site Request Forgery),可译为跨域请求伪造,是一种利用用户带登录 态的cookie进行安全操作的攻击方式。CSRF实际上并不难防,但常常被系统开发者忽略,从而埋下巨 大的安全隐患。

二、攻击过程

举个例子,假设你登录了邮箱,正常情况下可以通过某个链接http:xx.mail.com/send可以发送邮件。此时你又访问了别的网站,网站中有黄色广告,点击后广告会请求http:xx.mail.com/send。此时相当于在盗版网站中调用了发送邮件的链接,访问时会使用你邮箱网站的cookie信息。虽然盗版网站会提示跨域,但服务端任然进行了相应处理。

三、防御手段

在任何情况下,都应当尽可能地避免以GET方式提供涉及数据修改的API。并不是说其他请求方式可以避免CSRF,只是GET请求更容易被攻击。

在此基础上,防御 CSRF攻击的方式主要有以下两种。

1.HTTP Referer

Http referer是由浏览器添加的一个请求头字段,用于标识请求来源,浏览器端无法轻易篡改该值。

比如攻击者在第三方页面构造了POST请求,htttp referer不是我们网站的地址(有的老版IE浏览器可以修改该值,如果用户的浏览器比较新,就能避免这个问题),当服务端收到请求,发现请求来自其他站点,就能拒绝该请求。

这种方式简单便捷,但不是完全可靠,比如老的浏览器就能修改该值。用户在浏览器设置了不被跟踪,就不会有该字段,服务端加了校验后就会拦截掉用户的正常请求。

2.CsrfToken认证

CSRF是利用用户的登录态进行攻击的,而用户的登录态记录在cookie中。其实攻击者并不知道用 户的cookie存放了哪些数据,于是想方设法让用户自身发起请求,这样浏览器便会自行将cookie传送到 服务器完成身份校验。

CsrfToken 的防范思路是,添加一些并不存放于 cookie 的验证值,并在每个请求中都进行校验, 便可以阻止CSRF攻击。

具体做法是在用户登录时,由系统发放一个CsrfToken值,用户携带该CsrfToken值与用户名、密码 等参数完成登录。服务端记录该会话的CsrfToken值,之后在用户的任何请求中,都必须带上该 CsrfToken值,并由系统进行校验。

该方案需要前端配合,包括存储CsrfToken的值,在每次的请求中,不管是form表单还是ajax,都需要携带该token。虽然比HTTP Referer安全很多,但也有弊端,如果在已有系统进行改造,就需要修改每一个请求,所以建议在系统开发之初就考虑防御CSRF攻击。

三、使用SpringSecurity防御CSRF

csrf攻击完全是基于浏览器的,如果前端没有浏览器,也就不会有CSRF攻击了,所以我们需要关闭SpringSecurity自动配置的csrf。

1.SpringSecurity防御CSRF过程

CsrfFilter:

SpringSecurity通过注册一个CsrfFilter来专门处理CSRF攻击。

CsrfToken:

用该接口来定义csrftoekn所需的一些必要方法。

public interface CsrfToken extends Serializable {
    //从哪个头字段获取token值
	String getHeaderName();
    //从哪个参数获取token值
	String getParameterName();

	String getToken();
}

CsrfTokenRepository

定义了如何生成,保存、以及加载token.

public interface CsrfTokenRepository {

	CsrfToken generateToken(HttpServletRequest request);

	void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response);

	CsrfToken loadToken(HttpServletRequest request);

}

HttpSessionCsrfTokenRepository

默认情况下,SpringSecurity使用的CsrfTokenRepository的实现类是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;

	@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);
		}
	}

	@Override
	public CsrfToken loadToken(HttpServletRequest request) {
		HttpSession session = request.getSession(false);
		if (session == null) {
			return null;
		}
		return (CsrfToken) session.getAttribute(this.sessionAttributeName);
	}

	@Override
	public CsrfToken generateToken(HttpServletRequest request) {
		return new DefaultCsrfToken(this.headerName, this.parameterName, createNewToken());
	}

	public void setParameterName(String parameterName) {
		Assert.hasLength(parameterName, "parameterName cannot be null or empty");
		this.parameterName = parameterName;
	}

	public void setHeaderName(String headerName) {
		Assert.hasLength(headerName, "headerName cannot be null or empty");
		this.headerName = headerName;
	}

	public void setSessionAttributeName(String sessionAttributeName) {
		Assert.hasLength(sessionAttributeName, "sessionAttributename cannot be null or empty");
		this.sessionAttributeName = sessionAttributeName;
	}

	private String createNewToken() {
		return UUID.randomUUID().toString();
	}

}

HttpSessionCsrfTokenRepository将CsrfToken值存储在HttpSession中,并指定前端把CsrfToken 值放在名为“_csrf”的请求参数或名为“X-CSRF-TOKEN”的请求头字段里(可以调用相应的设置方法来重新设定)。校验时,通过对比HttpSession内存储的CsrfToken值与前端携带的CsrfToken值是否一致,便能断定本次请求是否为CSRF攻击。

前端使用Token的时候,必须使用从服务端渲染的方式,比如jsp页面:

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

CookieCsrfTokenRepository

Spring Security还提供了另一种方式,即CookieCsrfTokenRepository。之前是服务端将token存储在了session中。这个是将token存储在浏览器的cookie中,这样可以减少服务端的内存消耗,而且前端可以使用js读取(需要设置该cookie的httpOnly属性为false),更加灵活。

有人可能会有疑问,放在cookie中,不是又可以被攻击了吗?其实不是的。

cookie只有在同域的情况下才能被js获取。正常情况下,服务端从cookie中获取token,前端使用js从cookie中获取token,2者进行校验。攻击者只能在第三方页面伪造请求的时候,利用请求携带cookie,这个时候服务端能拿从携带的cookie中拿到token,但是前端并没有使用js将用于校验的token传给服务端(攻击者没法获取cookie),所以校验没法通过。

CsrfFilter

现在我们重新来看这个类的主要逻辑:

@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		request.setAttribute(HttpServletResponse.class.getName(), response);
		CsrfToken csrfToken = this.tokenRepository.loadToken(request);
		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)) {
			if (this.logger.isTraceEnabled()) {
				this.logger.trace("Did not protect against CSRF since request did not match "
						+ this.requireCsrfProtectionMatcher);
			}
			filterChain.doFilter(request, response);
			return;
		}
		String actualToken = request.getHeader(csrfToken.getHeaderName());
		if (actualToken == null) {
			actualToken = request.getParameter(csrfToken.getParameterName());
		}
		if (!equalsConstantTime(csrfToken.getToken(), actualToken)) {
			this.logger.debug(
					LogMessage.of(() -> "Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request)));
			AccessDeniedException exception = (!missingToken) ? new InvalidCsrfTokenException(csrfToken, actualToken)
					: new MissingCsrfTokenException(actualToken);
			this.accessDeniedHandler.handle(request, response, exception);
			return;
		}
		filterChain.doFilter(request, response);
	}

这段代码的意思就是, 从你指定或者默认的的CsrfTokenRepository中获取token,其实就是获取的服务端存储的token(session中或者cookie中),如果没有,那么就生成并且保存token,然后获取前端传过来的token,然后进行对比。

2.SpringSecurity配置CSRF

1.我们使用cookie的方式存储token.

 2.添加AccessDeniedHandler

用来在token请求不通过的时候,返回数据。

@Component
@Slf4j
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSON.toJSONString(ResultVO.error(10000, "禁止访问")));
    }
}

 3. 前端修改

生成的token:

  function getCookie(name){
    var strcookie = document.cookie;//获取cookie字符串
    var arrcookie = strcookie.split("; ");//分割
    //遍历匹配
    for ( var i = 0; i < arrcookie.length; i++) {
      var arr = arrcookie[i].split("=");
      if (arr[0] === name){
        return arr[1];
      }
    }
    return "";
  }

3.启动项目测试

启动项目,登录成功,跳转页面。

文章配套代码:https://gitee.com/lookoutthebush/spring-security-demo

到此这篇关于SpringSecurity跨域请求伪造(CSRF)的防护实现的文章就介绍到这了,更多相关SpringSecurity CSRF防护内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringSecurity框架下实现CSRF跨站攻击防御的方法

    一.什么是CSRF 很多朋友在学习Spring Security的时候,会将CORS(跨站资源共享)和CSRF(跨站请求伪造)弄混,以为二者是一回事.其实不是,先解释一下: CORS(跨站资源共享)是局部打破同源策略的限制,使在一定规则下HTTP请求可以突破浏览器限制,实现跨站访问. CSRF是一种网络攻击方式,也可以说是一种安全漏洞,这种安全漏洞在web开发中广泛存在. 当我们使用Spring Security的时候,这种CSRF漏洞默认的被防御掉了.但是你会发现在跨域请求的情况下,我们的PO

  • SpringSecurity解决POST方式下CSRF问题

    目录 解决方案 ①(后端常用): 解决方案②: 解决方案③: 解决方案④: 问题现象:HTTP Status 403-Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN' 原因:Spring Security为防止CSRF(Cross-site requetst forgery跨站请求伪造)的发生,限制了除了get以外的大多数方法. 解决方案 ①(后端常用): 屏

  • SpringSecurity的防Csrf攻击实现代码解析

    CSRF(Cross-site request forgery)跨站请求伪造,也被称为One Click Attack或者Session Riding,通常缩写为CSRF或XSRF,是一种对网站的恶意利用.尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户的请求来利用受信任的网站.与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性. CSRF是一种依赖web浏览器的

  • 使用SpringSecurity处理CSRF攻击的方法步骤

    CSRF漏洞现状 CSRF(Cross-site request forgery)跨站请求伪造,也被称为One Click Attack或者Session Riding,通常缩写为CSRF或XSRF,是一种对网站的恶意利用.尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户的请求来利用受信任的网站.与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性. CSRF是一种

  • SpringSecurity跨域请求伪造(CSRF)的防护实现

    目录 一.CSRF 二.攻击过程 三.防御手段 1.HTTP Referer 2.CsrfToken认证 三.使用SpringSecurity防御CSRF 1.SpringSecurity防御CSRF过程 2.SpringSecurity配置CSRF 3.启动项目测试 一.CSRF CSRF的全称是(Cross Site Request Forgery),可译为跨域请求伪造,是一种利用用户带登录 态的cookie进行安全操作的攻击方式.CSRF实际上并不难防,但常常被系统开发者忽略,从而埋下巨

  • spring security中的csrf防御原理(跨域请求伪造)

    什么是csrf? csrf又称跨域请求伪造,攻击方通过伪造用户请求访问受信任站点.CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如:NYTimes.com(纽约时报).Metafilter(一个大型的BLOG网站),YouTube和百度HI......而现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为"沉睡的巨人". 举个例子,用户通过表单发送请求到银行网站,银行

  • 浅谈ASP.NET MVC 防止跨站请求伪造(CSRF)攻击的实现方法

    在HTTP POST请求中,我们多次在View和Controller中看下如下代码: 1.View中调用了Html.AntiForgeryToken(). 2.Controller中的方法添加了[ValidateAntiForgeryToken]注解. 这样看似一对的写法其实是为了避免引入跨站请求伪造(CSRF)攻击. 这种攻击形式大概在2001年才为人们所认知,2006年美国在线影片租赁网站Netflix爆出多个CSRF漏洞,2008年流行的视频网址YouTube受到CSRF攻击,同年墨西哥一

  • Django CSRF跨站请求伪造防护过程解析

    前言 CSRF全称Cross-site request forgery(跨站请求伪造),是一种网络的攻击方式,也被称为"One Click Attack"或者Session Riding,通常缩写为CSRF或者XSRF. 攻击原理 1.用户访问正常的网站A,浏览器就会保存网站A的cookies. 2.用户在访问恶意网站B, 网站B上有某个隐藏的链接会自动请求网站A的链接地址,例如表单提交,传指定的参数. 3.恶意网站B的自动化请求,执行就是在用户A的同一个浏览器上,因此在访问网站A的时

  • Django中如何防范CSRF跨站点请求伪造攻击的实现

    CSRF概念 CSRF跨站点请求伪造(Cross-Site Request Forgery). 攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件.发消息,盗取你的账号,添加系统管理员,甚至于购买商品.虚拟货币转账等. CSRF攻击原理以及过程 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A: 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常

  • Django跨域请求CSRF的方法示例

    web跨域请求 1.为什么要有跨域限制 举个例子: 1.用户登录了自己的银行页面 http://mybank.com,http://mybank.com向用户的cookie中添加用户标识. 2.用户浏览了恶意页面 http://evil.com.执行了页面中的恶意AJAX请求代码. 3.http://evil.com向http://mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去. 4.银行页面从发送的cookie中提取

  • CSRF跨站请求伪造漏洞分析与防御

    目录 CSRF 漏洞原理 漏洞危害 防御绕过 漏洞利用 防御措施 总结 CSRF 现在的网站都有利用CSRF令牌来防止CSRF,就是在请求包的字段加一个csrf的值,防止csrf,要想利用该漏洞,要和xss组合起来,利用xss获得该csrf值,在构造的请求中将csrf值加进去,就可以绕过csrf防御,利用该漏洞. 该漏洞与xss的区别:xss是通过执行恶意脚本,获取到用户的cookie等信息,再利用cookie等信息进行绕过登录限制,做一些用户可以做的事. 而csrf是伪造请求,让用户自己执行攻

  • 解析django的csrf跨站请求伪造

    目录 1.什么是跨站请求伪造 2.如何规避跨站请求伪造(csrf校验) 3.如何符合csrf校验 3.1 form表单如何符合校验 3.2 ajax如何符合校验 4.csrf相关的装饰器 4.1 针对FBV的csrf装饰器 4.2 针对CBV的csrf装饰器 1.什么是跨站请求伪造 请看图: 我们自行写了一个网站模仿中国银行,用户不知道是否是真的中国银行,并且提交了转账信息,生成一个form表单,向银行服务器发送转账请求,这个form表单和正规银行网站的form表单一模一样,只不过里面隐藏着改变

  • 基于CORS实现WebApi Ajax 跨域请求解决方法

    概述 ASP.NET Web API 的好用使用过的都知道,没有复杂的配置文件,一个简单的ApiController加上需要的Action就能工作.但是在使用API的时候总会遇到跨域请求的问题,特别各种APP万花齐放的今天,API的跨域请求是不能避免的. 在默认情况下,为了防止CSRF跨站的伪造攻击(或者是 javascript的同源策略(Same-Origin Policy)),一个网页从另外一个域获取数据时就会收到限制.有一些方法可以突破这个限制,那就是大家熟知的JSONP, 当然这只是众多

  • 基于iframe实现ajax跨域请求 获取网页中ajax数据

    大家都知道,在不同域的情况下是不能发送ajax请求的,浏览器会报如下错误: 同时,内嵌的iframe中无法进行跨域通信的,也就是说不同域的iframe是无法互相读取数据的(当然利用hash变化可以从父window传入数据到子iframe,不过并没有什么意义).iframe跨域通信时,浏览器会报如下错误: 其实这两个问题都是由于跨域造成的. 下面就介绍如何解决这个问题. 其实问题的关键就在于,浏览器在解析ajax请求地址时会和当前网页的地址进行比较,如果是跨域的,那就禁止掉并且报错.那么我们如果让

随机推荐