Spring Security之CSRF攻击

1.CSRF攻击简介

SRF跨站点请求伪造(Cross—Site Request Forgery),跟XSS攻击一样,存在巨大的危害性,其攻击原理如下:

   1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;

   2. 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;

   3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;

   4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;

   5. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息
   
   6. 向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,
   
   7. 来自网站B的恶意代码被执行。 

2.CSRF常见防御手段

2.1 尽量使用POST请求方式

GET接口太容易被拿来做CSRF攻击,看上面示例就知道,只要构造一个img标签,而img标签又是不能过滤的数据。接口最好限制为POST使用,GET则无效,降低攻击风险。
当然POST并不是万无一失,攻击者只要构造一个form就可以,但需要在第三方页面做,这样就增加暴露的可能性。

2.2 通过Referer识别

根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。在通常情况下,访问一个安全受限的页面的请求都来自于同一个网站。比如某银行的转账是通过用户访问http://www.xxx.com/transfer.do页面完成的,用户必须先登录www.xxx.com,然后通过单击页面上的提交按钮来触发转账事件。当用户提交请求时,该转账请求的Referer值就会是
提交按钮所在页面的URL(本例为www.xxx. com/transfer.do)。如果攻击者要对银行网站实施CSRF攻击,他只能在其他网站构造请求,当用户通过其他网站发送请求到银行时,该请求的Referer的值是其他网站的地址,而不是银行转账页面的地址。因此,要防御CSRF攻击,银行网站只需要对于每一个转账请求验证其Referer值即可,如果是以www.xx.om域名开头的地址,则说明该请求是来自银行网站自己的请求,是合法的;如果Referer是其他网站,就有可能是CSRF攻击,则拒绝该请求。
取得HTTP请求Referer:

String referer = request.getHeader("Referer");

2.3 增加token

CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于cookie中,因此攻击者可以在不知道用户验证信息的情况下直接利用用户的cookie来通过安全验证。由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,并且该信总不存在于cookie之中。鉴于此,系统开发人员可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务端进行token校验,如果请求中没有token或者token内容不正确,则认为是CSRF攻击而拒绝该请求。
假设请求通过POST方式提交,则可以在相应的表单中增加一个隐藏域:

token的值通过服务端生成,表单提交后token的值通过POST请求与参数一同带到服务端,每次会话可以使用相同的token,会话过期,则token失效,攻击者因无法获取到token,也就无法伪造请求。
在session中添加token的实现代码:

	HttpSession session = request.getSession();
	Object token = session.getAttribute("_token");
	if(token == null I I "".equals(token)) {
    	session.setAttribute("_token", UUID.randomUUIDO .toString());
	}

2.4 将cookie设置为HttpOnly

CRSF攻击很大程度上是利用了浏览器的cookie,为了防止站内的XSS漏洞盗取cookie,需要在cookie中设置“HttpOnly”属性,这样通过程序(如JavaScript脚本、Applet等)就无法读取到cookie信息,避免了攻击者伪造cookie的情况出现。
在Java的Servlet的API中设置cookie为HttpOnly的代码如下:

response.setHeader( "Set-Cookie", "cookiename=cookievalue;HttpOnly");

3.使用Spring Security防御CSRF攻击

Spring Security 通过注册一个CsrfFilter来专门处理CSRF攻击

在Spring Security中,CsrfToken是一个用于描述Token值,以及验证时应当获取哪个请求参数或请求头字段的接口。

public interface CsrfToken extends Serializable {

    String getHeaderName();

    String getParameterName();

    String getToken();
}

CsrfTokenRepository定义了如何生成、保存和加载token

public interface CsrfTokenRepository {
	// 生成token的规则
    CsrfToken generateToken(HttpServletRequest var1);
	// 保存token
    void saveToken(CsrfToken var1, HttpServletRequest var2, HttpServletResponse var3);
	// 加载token
    CsrfToken loadToken(HttpServletRequest var1);
}

在默认情况下,Spring Security加载的是一个HttpSessionCsrfTokenRepository,HttpSessionCsrfTokenRepository将CsrfToken值存储在HttpSession中,并指定前端把CsrfToken的值放在名为“_csrf”的请求参数或者“X-CSRF-TOKEN”的请求字典里面。校验时,通过对比session和request里面的CsrfToken的值来判断此次是否为csrf攻击请求。

除此之外,SpringSecurity还提供了另外一种方案:CookieCsrfTokenRepository。

这种方式将token存储在cookie中,首先减少了服务器session的内存消耗;其次,前端可以使用JS获取,而不需要服务器注入参数。由于cookie只能被同域情况下读取,所以杜绝了第三方站点跨域获取cookie中的CsrfToken的值。
配置如下:

http...
	.csrf()
	.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

补:查看CsrfFilter逻辑。

你可能感兴趣的:(springSecurity)