在Java Web应用程序中阻止CSRF
生活随笔
收集整理的這篇文章主要介紹了
在Java Web应用程序中阻止CSRF
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
跨站點(diǎn)請(qǐng)求偽造攻擊(CSRF)在Web應(yīng)用程序中非常常見(jiàn),如果允許,可能會(huì)造成重大危害。 如果您從未聽(tīng)說(shuō)過(guò)CSRF,建議您查看有關(guān)它的OWASP頁(yè)面 。 幸運(yùn)的是,阻止CSRF攻擊非常簡(jiǎn)單,我將向您展示它們的工作方式,以及如何在基于Java的Web應(yīng)用程序中以盡可能不干擾的方式防御它們。 想象一下,您即將在銀行的安全網(wǎng)頁(yè)上進(jìn)行匯款,當(dāng)您單擊轉(zhuǎn)帳選項(xiàng)時(shí),將加載一個(gè)表格頁(yè)面,您可以選擇借方和貸方帳戶,并輸入要轉(zhuǎn)移的金額。 當(dāng)您對(duì)選擇感到滿意時(shí),請(qǐng)按“提交”,然后將表單信息發(fā)送到銀行的Web服務(wù)器,該服務(wù)器依次執(zhí)行交易。 現(xiàn)在,將以下內(nèi)容添加到圖片中,一個(gè)惡意網(wǎng)站(您認(rèn)為當(dāng)然沒(méi)有害處)在瀏覽器的另一個(gè)窗口/選項(xiàng)卡上打開(kāi),而您無(wú)辜地在銀行站點(diǎn)中移動(dòng)了數(shù)百萬(wàn)美元。 這個(gè)邪惡的網(wǎng)站了解銀行的網(wǎng)絡(luò)表單結(jié)構(gòu),當(dāng)您瀏覽該網(wǎng)站時(shí),它會(huì)嘗試發(fā)布從您的帳戶中提取資金并將其存入邪惡的霸主賬戶的交易,之所以能夠這樣做,是因?yàn)槟c銀行之間存在公開(kāi)且有效的會(huì)話銀行網(wǎng)站使用同一瀏覽器! 這是CSRF攻擊的基礎(chǔ)。 一種簡(jiǎn)單有效的預(yù)防方法是在加載初始傳輸表單時(shí)生成一個(gè)隨機(jī)(即,不可預(yù)測(cè)的)字符串并將其發(fā)送給瀏覽器。 然后,瀏覽器將這些數(shù)據(jù)與傳輸選項(xiàng)一起發(fā)送,并且服務(wù)器會(huì)在批準(zhǔn)交易進(jìn)行處理之前對(duì)其進(jìn)行驗(yàn)證。 這樣,惡意網(wǎng)站即使可以訪問(wèn)瀏覽器中的有效會(huì)話也無(wú)法發(fā)布交易。 為了在Java中實(shí)現(xiàn)此機(jī)制,我選擇使用兩個(gè)過(guò)濾器,一個(gè)過(guò)濾器為每個(gè)請(qǐng)求創(chuàng)建鹽,另一個(gè)過(guò)濾器進(jìn)行驗(yàn)證。 由于用戶請(qǐng)求以及隨后應(yīng)驗(yàn)證的POST或GET不一定按順序執(zhí)行,因此我決定使用基于時(shí)間的緩存來(lái)存儲(chǔ)有效鹽字符串列表。 用于為請(qǐng)求生成新的鹽并將其存儲(chǔ)在緩存中的第一個(gè)過(guò)濾器可以編碼如下: package com.ricardozuasti.csrf;import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.RandomStringUtils;public class LoadSalt implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {// Assume its HTTPHttpServletRequest httpReq = (HttpServletRequest) request;// Check the user session for the salt cache, if none is present we create oneCache<String, Boolean> csrfPreventionSaltCache = (Cache<String, Boolean>)httpReq.getSession().getAttribute("csrfPreventionSaltCache");if (csrfPreventionSaltCache == null){csrfPreventionSaltCache = CacheBuilder.newBuilder().maximumSize(5000).expireAfterWrite(20, TimeUnit.MINUTES).build();httpReq.getSession().setAttribute("csrfPreventionSaltCache", csrfPreventionSaltCache);}// Generate the salt and store it in the users cacheString salt = RandomStringUtils.random(20, 0, 0, true, true, null, new SecureRandom());csrfPreventionSaltCache.put(salt, Boolean.TRUE);// Add the salt to the current request so it can be used// by the page rendered in this requesthttpReq.setAttribute("csrfPreventionSalt", salt);chain.doFilter(request, response);}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void destroy() {}
} 我使用Guava CacheBuilder創(chuàng)建鹽緩存,因?yàn)樗扔写笮∠拗?#xff0c;又有每個(gè)條目的過(guò)期超時(shí)。 為了生成實(shí)際的鹽,我使用了由Java 6 SecureRandom支持的Apache Commons RandomStringUtils ,以確保生成強(qiáng)大的種子。 在以AJAX鏈接,發(fā)布或調(diào)用安全交易的頁(yè)面結(jié)尾的所有請(qǐng)求中均應(yīng)使用此過(guò)濾器,因此在大多數(shù)情況下,最好將其映射到每個(gè)請(qǐng)求(也許除了靜態(tài)內(nèi)容(例如圖像) ,CSS等)。 您的web.xml中的映射應(yīng)類似于: ...<filter><filter-name>loadSalt</filter-name><filter-class>com.ricardozuasti.csrf.LoadSalt</filter-class></filter>...<filter-mapping><filter-name>loadSalt</filter-name><url-pattern>*</url-pattern></filter-mapping>...
就像我說(shuō)的,要在執(zhí)行安全交易之前驗(yàn)證鹽,我們可以編寫(xiě)另一個(gè)過(guò)濾器:
package com.ricardozuasti.csrf;import com.google.common.cache.Cache; import java.io.IOException; import javax.servlet.*; import javax.servlet.http.HttpServletRequest;public class ValidateSalt implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {// Assume its HTTPHttpServletRequest httpReq = (HttpServletRequest) request;// Get the salt sent with the requestString salt = (String) httpReq.getParameter("csrfPreventionSalt");// Validate that the salt is in the cacheCache<String, Boolean> csrfPreventionSaltCache = (Cache<String, Boolean>)httpReq.getSession().getAttribute("csrfPreventionSaltCache");if (csrfPreventionSaltCache != null &&salt != null &&csrfPreventionSaltCache.getIfPresent(salt) != null){// If the salt is in the cache, we move onchain.doFilter(request, response);} else {// Otherwise we throw an exception aborting the request flowthrow new ServletException("Potential CSRF detected!! Inform a scary sysadmin ASAP.");}}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void destroy() {} } 您應(yīng)該為每個(gè)需要確保安全的請(qǐng)求(例如,檢索或修改敏感信息,轉(zhuǎn)移資金等)配置此過(guò)濾器,例如: ...<filter><filter-name>validateSalt</filter-name><filter-class>com.ricardozuasti.csrf.ValidateSalt</filter-class></filter>...<filter-mapping><filter-name>validateSalt</filter-name><url-pattern>/transferMoneyServlet</url-pattern></filter-mapping>... 配置兩個(gè)servlet后,所有受保護(hù)的請(qǐng)求都將失敗:)。 要解決此問(wèn)題,您必須在每個(gè)以安全URL結(jié)尾的鏈接和表單帖子中添加csrfPreventionSalt參數(shù),該參數(shù)包含具有相同名稱的request參數(shù)的值。 例如,在JSP頁(yè)面內(nèi)以HTML形式: ... <form action="/transferMoneyServlet" method="get"><input type="hidden" name="csrfPreventionSalt" value="<c:out value='${csrfPreventionSalt}'/>"/>... </form> ...當(dāng)然,您可以編寫(xiě)一個(gè)自定義標(biāo)簽,一個(gè)不錯(cuò)的Javascript代碼,或在每個(gè)所需的鏈接/表單中添加新參數(shù)的方法。
參考: Ricardo Zuasti博客博客上的JCG合作伙伴 Ricardo Zuasti 阻止了Java Web應(yīng)用程序中的CSRF 。
翻譯自: https://www.javacodegeeks.com/2012/06/preventing-csrf-in-java-web-apps.html
總結(jié)
以上是生活随笔為你收集整理的在Java Web应用程序中阻止CSRF的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 花呗快充额度什么时候解冻?
- 下一篇: 花呗额度快充可以充几次?