spring-security中的csrf防御机制

目录(?)[+]

什么是csrf?

csrf又称跨域请求伪造,攻击方通过伪造用户请求访问受信任站点。 CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如:NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI......而现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为“沉睡的巨人”。
举个例子,用户通过表单发送请求到银行网站,银行网站获取请求参数后对用户账户做出更改。在用户没有退出银行网站情况下,访问了攻击网站,攻击网站中有一段跨域访问的代码,可能自动触发也可能点击提交按钮,访问的url正是银行网站接受表单的url。因为都来自于用户的浏览器端,银行将请求看作是用户发起的,所以对请求进行了处理,造成的结果就是用户的银行账户被攻击网站修改。
解决方法基本上都是增加攻击网站无法获取到的一些表单信息,比如增加图片验证码,可以杜绝csrf攻击,但是除了登陆注册之外,其他的地方都不适合放验证码,因为降低了网站易用性
相关介绍:
http://baike.baidu.com/view/1609487.htm?fr=aladdin
http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html

spring-security中csrf防御原理

在web应用中增加前置过滤器,对需要验证的请求验证是否包含csrf的token信息,如果不包含,则报错。这样攻击网站无法获取到token信息,则跨域提交的信息都无法通过过滤器的校验。
看一下CsrfFilter的源码就很好理解了
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // 先从tokenRepository中加载token  
  2.    CsrfToken csrfToken = tokenRepository.loadToken(request);  
  3.    final boolean missingToken = csrfToken == null;  
  4.    // 如果为空,则tokenRepository生成新的token,并保存到tokenRepository中  
  5.    if(missingToken) {  
  6.        CsrfToken generatedToken = tokenRepository.generateToken(request);  
  7.        // 默认的SaveOnAccessCsrfToken方法,记录tokenRepository,  
  8.        // tokenRepository,response,获取token时先将token同步保存到tokenRepository中  
  9.        csrfToken = new SaveOnAccessCsrfToken(tokenRepository, request, response, generatedToken);  
  10.    }  
  11.    // 将token写入request的attribute中,方便页面上使用  
  12.    request.setAttribute(CsrfToken.class.getName(), csrfToken);  
  13.    request.setAttribute(csrfToken.getParameterName(), csrfToken);  
  14.   
  15.    // 如果不需要csrf验证的请求,则直接下传请求(requireCsrfProtectionMatcher是默认的对象,对符合^(GET|HEAD|TRACE|OPTIONS)$的请求不验证)  
  16.    if(!requireCsrfProtectionMatcher.matches(request)) {  
  17.        filterChain.doFilter(request, response);  
  18.        return;  
  19.    }  
  20.   
  21.    // 从用户请求中获取token信息  
  22.    String actualToken = request.getHeader(csrfToken.getHeaderName());  
  23.    if(actualToken == null) {  
  24.        actualToken = request.getParameter(csrfToken.getParameterName());  
  25.    }  
  26.    // 验证,如果相同,则下传请求,如果不同,则抛出异常  
  27.    if(!csrfToken.getToken().equals(actualToken)) {  
  28.        if(logger.isDebugEnabled()) {  
  29.            logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request));  
  30.        }  
  31.        if(missingToken) {  
  32.            accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken));  
  33.        } else {  
  34.            accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken));  
  35.        }  
  36.        return;  
  37.    }  
  38.   
  39.    filterChain.doFilter(request, response);  

使用样例

在web.xml中增加spring的过滤器代理
在spring的配置文件中增加过滤器
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <bean id="csrfFilter" class="org.springframework.security.web.csrf.CsrfFilter">  
  2.     <constructor-arg>  
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-family: Arial, Helvetica, sans-serif;"><!--</span><pre name="code" class="html">HttpSessionCsrfTokenRepository是把token放到session中来存取  
-->
 
   
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.         <bean class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository"/>  
  2.     </constructor-arg>  
  3. </bean>  
  4. <!-- 
  5.     如果用的是spring mvc 的form标签,则配置此项时自动将crsf的token放入到一个hidden的input中,而不需要开发人员显式的写入form 
  6. -->  
  7. <bean id="requestDataValueProcessor" class="org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor"/>  
如果配置了CsrfRequestDataValueProcessor,并且使用了spring的form标签来写表单代码,则这样就可以了。否则需要在页面上书写相关代码
首先获取token
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <meta name="_csrf" content="${_csrf.token}"/>  
  2. <meta name="_csrf_header" content="${_csrf.headerName}"/>  
然后在发送请求之前将token放入header中(或者form表单中)
[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. var token = $("meta[name='_csrf']").attr("content");  
  2. var header = $("meta[name='_csrf_header']").attr("content");  
  3. $(document).ajaxSend(function(e, xhr, options) {  
  4.     xhr.setRequestHeader(header, token);  
  5. });  





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值