再談Spring MVC中對於CSRF攻擊的防御


在Spring MVC應用中實施CSRF防御, 一般會采用 EYAL LUPU 的方案,該方案的基本思路是在生成表單時在其中插入一個隨機數作為簽名,在表單提交后對其中的簽名進行 驗證 ,根據 驗證 的結果區分該表單是否是經由應用簽署的合法表單。如果簽名不正確或不存在簽名,則說明請求可能已被劫持。


EYAL LUPU方案的巧妙之處在於,通過使用HandlerInterceptorAdapter和Spring3.1中新引入的ReuqestDataValueProcessor這一對組合,使得簽名和驗證的過程無縫地集成到現有應用中。Controller或Model層的對象可以仍然只關心自己的業務邏輯,完全不必考慮CSRF過程的存在;唯一的限制是在View層,必須使用Spring的<form>標簽來渲染表單。

對請求的驗證在攔截器的preHandle方法中,當驗證通過后,方法返回true,請求將沿着處理鏈繼續傳遞;但如果驗證失敗,方法返回false,請求將被截停,並發送一個HTTP 400的狀態代碼作為響應。

如果沒有在web.xml中使用error-page為應用自定義錯誤頁,400狀態碼將直接被發送給客戶端瀏覽器,瀏覽器會顯示一個缺省的錯誤頁面,到目前為之一切都很完美。

但如果使用error-page指定了錯誤頁面,問題來了,Servlet容器會首先根據響應狀態碼把原始的請求轉發(forward)給具體的錯誤頁面,然后該錯誤頁面才被發送到客戶端瀏覽器。需要注意的是,由於我們使用了攔截器,這次forward請求會再次被攔截,preHandle方法中的驗證過程也會再次被觸發,如果不加處理的話,會再次驗證失敗,因為具體的請求仍然是最初的請求。

解決的方案如下:
1. 在Spring的dispatcher-servlet中加入mvc:default-servlet-handler。
這樣做的目的是確保未被Dispatcher實際處理的請求,例如向錯誤頁面的轉發或對靜態資源的訪問等,會被傳回給Servlet容器。

2. 修改preHandle方法,在方法的開始檢測方法的第三個參數的類型。
該參數代表處理鏈中即將處理請求的下一個對象,如果該參數是DefaultServletHttpRequestHandler類的實例,則說明請求即將交由Servlet容器處理,對於此類請求直接放行即可。

3. 最后,確保所有errro-page中聲明的URL不會被任何Controller處理,也不會被ResourceHttpRequestHandler處理。
被Controller處理過就沒機會沿處理鏈將請求交給Servlet容器了。另外,應用 一般 會在dispatcher中用mvc:resources聲明靜態資源,如果把錯誤頁面也包含進來,請求會在被交給DefaultServletHttpRequestHanlder前先被 ResourceHttpRequestHandler匹配到,后者為了優化對靜態資源的處理,默認是只支持GET和HEAD方法的,而此時的請求是從表單POST來的,因此會引發一個Request method 'POST' not supported錯誤,而瀏覽器端只能得到一個405響應,無法顯示預期的錯誤頁頁面。

調整后的preHandle方法類似下面這樣:
public boolean preHandle(HttpServletRequest request,
               HttpServletResponse response, Object handler) throws Exception {

          if (handler instanceof DefaultServletHttpRequestHandler) {
               return true;
          }
          
          if (!request.getMethod().equalsIgnoreCase(
                    WebContentGenerator.METHOD_POST)) {
               // 忽略非POST請求
               return true;
          } else {
               // 驗證CSRF簽名
               //if (passed)
               //     return true;
               //else {
               //     response.sendError(HttpServletResponse.SC_BAD_REQUEST,
               //               "Bad or missing CSRF value");
               //     return false;
               //}
          }
} 



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM