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





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章