javascript
csrf spring_无状态Spring安全性第1部分:无状态CSRF保护
csrf spring
如今,隨著RESTful架構(gòu)變得越來(lái)越標(biāo)準(zhǔn),可能值得花一些時(shí)間重新考慮當(dāng)前的安全方法。 在這一小系列博客文章中,我們將探索以無(wú)狀態(tài)方式解決與Web相關(guān)的安全性問(wèn)題的幾種相對(duì)較新的方法。 這第一篇文章是關(guān)于保護(hù)您的網(wǎng)站免受跨站請(qǐng)求偽造(CSRF)的攻擊。
回顧:什么是跨站請(qǐng)求偽造?
CSRF攻擊基于揮之不去的身份驗(yàn)證Cookie。 在登錄或以其他方式標(biāo)識(shí)為網(wǎng)站上的唯一訪問(wèn)者之后,該網(wǎng)站可能會(huì)在瀏覽器中留下cookie。 如果沒(méi)有顯式注銷或以其他方式刪除此cookie,它可能會(huì)保持一段時(shí)間有效。
另一個(gè)站點(diǎn)可以通過(guò)使瀏覽器向受攻擊的站點(diǎn)發(fā)出(跨站點(diǎn))請(qǐng)求來(lái)濫用此功能。 例如,包括一些用于在“ http://siteunderattack.com/changepassword?pw=hacked”標(biāo)簽上進(jìn)行POST的Javascript,將使瀏覽器發(fā)出該請(qǐng)求,并將對(duì)該域仍然有效的任何(身份驗(yàn)證)cookie附加到該請(qǐng)求!
即使單源策略(SOP)不允許惡意站點(diǎn)訪問(wèn)響應(yīng)的任何部分。 從上面的示例中可以很明顯地看出,如果請(qǐng)求的URL在后臺(tái)觸發(fā)任何副作用(狀態(tài)更改),則損害已經(jīng)完成。
常用方法
常用的解決方案是引入所謂的共享秘密CSRF令牌的要求,并將其作為先前響應(yīng)的一部分讓客戶端知道。
然后,對(duì)于任何有副作用的請(qǐng)求,客戶端都需要將其ping回服務(wù)器。 可以直接在表單中作為隱藏字段或作為自定義HTTP標(biāo)頭完成此操作。 無(wú)論哪種方式,其他站點(diǎn)都無(wú)法成功產(chǎn)生包含正確的CSRF令牌的請(qǐng)求,因?yàn)镾OP阻止跨站點(diǎn)讀取來(lái)自服務(wù)器的響應(yīng)。 這種方法的問(wèn)題在于服務(wù)器需要記住會(huì)話中每個(gè)用戶的每個(gè)CSRF令牌的值。
無(wú)狀態(tài)方法
1.切換到完整且設(shè)計(jì)正確的基于JSON的REST API。
單源策略僅允許跨站點(diǎn)的HEAD / GET和POST。 POST只能是以下啞劇類型之一:application / x-www-form-urlencoded,multipart / form-data或text / plain。 確實(shí)沒(méi)有JSON! 現(xiàn)在考慮到GET永遠(yuǎn)不要在任何經(jīng)過(guò)??適當(dāng)設(shè)計(jì)的基于HTTP的API中觸發(fā)副作用,這讓您可以簡(jiǎn)單地禁止任何非JSON POST / PUT / DELETE,一切都很好。 對(duì)于上傳文件(多部分/表單數(shù)據(jù))的方案,仍然需要明確的CSRF保護(hù)。
2.檢查HTTP Referer標(biāo)頭。
通過(guò)檢查仍然易受攻擊的場(chǎng)景(例如多部分/表單數(shù)據(jù)POST)的Referer標(biāo)頭的存在和內(nèi)容,可以進(jìn)一步完善上述方法。 瀏覽器使用此標(biāo)頭來(lái)指定觸發(fā)請(qǐng)求的確切頁(yè)面(url)。 這可以輕松地用于檢查站點(diǎn)的預(yù)期域。 請(qǐng)注意,如果選擇進(jìn)行此類檢查,則在沒(méi)有標(biāo)題的情況下,切勿允許請(qǐng)求。
3.客戶端生成的CSRF令牌。
讓客戶端在Cookie和自定義HTTP標(biāo)頭中生成并發(fā)送相同的唯一秘密值。 考慮到僅允許網(wǎng)站為其自己的域讀取/寫(xiě)入Cookie,因此只有真實(shí)網(wǎng)站才能在兩個(gè)標(biāo)頭中發(fā)送相同的值。 使用這種方法,您的服務(wù)器要做的就是在每個(gè)請(qǐng)求無(wú)狀態(tài)的基礎(chǔ)上檢查兩個(gè)值是否相等!
實(shí)作
著眼于第三種方法,用于基于顯式但無(wú)狀態(tài)CSRF令牌的安全性,讓我們看看使用Spring Boot和Spring Security在代碼中的樣子。
在Spring Boot中,您會(huì)獲得一些不錯(cuò)的默認(rèn)安全設(shè)置,您可以使用自己的配置適配器對(duì)其進(jìn)行微調(diào)。 在這種情況下,所需要做的就是禁用默認(rèn)的csrf行為并添加自己的StatelessCSRFFilter:
自定義CSRF保護(hù)
@EnableWebSecurity @Order(1) public class StatelessCSRFSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().addFilterBefore(new StatelessCSRFFilter(), CsrfFilter.class);} }這是StatelessCSRFFilter的實(shí)現(xiàn):
自定義CSRF過(guò)濾器
public class StatelessCSRFFilter extends OncePerRequestFilter {private static final String CSRF_TOKEN = "CSRF-TOKEN";private static final String X_CSRF_TOKEN = "X-CSRF-TOKEN";private final RequestMatcher requireCsrfProtectionMatcher = new DefaultRequiresCsrfMatcher();private final AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {if (requireCsrfProtectionMatcher.matches(request)) {final String csrfTokenValue = request.getHeader(X_CSRF_TOKEN);final Cookie[] cookies = request.getCookies();String csrfCookieValue = null;if (cookies != null) {for (Cookie cookie : cookies) {if (cookie.getName().equals(CSRF_TOKEN)) {csrfCookieValue = cookie.getValue();}}}if (csrfTokenValue == null || !csrfTokenValue.equals(csrfCookieValue)) {accessDeniedHandler.handle(request, response, new AccessDeniedException("Missing or non-matching CSRF-token"));return;}}filterChain.doFilter(request, response);}public static final class DefaultRequiresCsrfMatcher implements RequestMatcher {private final Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");@Overridepublic boolean matches(HttpServletRequest request) {return !allowedMethods.matcher(request.getMethod()).matches();}} }不出所料,無(wú)狀態(tài)版本在兩個(gè)標(biāo)頭值上只做一個(gè)簡(jiǎn)單的equals()。
客戶端實(shí)施
客戶端實(shí)現(xiàn)也很簡(jiǎn)單,尤其是在使用AngularJS時(shí)。 AngularJS已經(jīng)提供了內(nèi)置的CSRF令牌支持。 如果您告訴它要讀取的Cookie,它將自動(dòng)將其值發(fā)送到您選擇的自定義標(biāo)頭中。 (瀏覽器負(fù)責(zé)發(fā)送cookie標(biāo)頭本身。)
您可以按以下方式覆蓋AngularJS的默認(rèn)名稱(XSRF而不是CSRF):
設(shè)置適當(dāng)?shù)牧钆泼Q
$http.defaults.xsrfHeaderName = 'X-CSRF-TOKEN'; $http.defaults.xsrfCookieName = 'CSRF-TOKEN';此外,如果您想為每個(gè)請(qǐng)求生成一個(gè)新的令牌值,則可以向$ httpProvider添加自定義攔截器,如下所示:
攔截器生成cookie
app.config(['$httpProvider', function($httpProvider) {//fancy random token, losely after https://gist.github.com/jed/982883function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e16]+1e16).replace(/[01]/g,b)};$httpProvider.interceptors.push(function() {return {'request': function(response) {// put a new random secret into our CSRF-TOKEN Cookie before each requestdocument.cookie = 'CSRF-TOKEN=' + b();return response;}};}); }]); 您可以在github上找到一個(gè)完整的可用示例。
確保已安裝gradle 2.0,并使用“ gradle build”和“ gradle run”簡(jiǎn)單地運(yùn)行它。 如果要像eclipse一樣在IDE中使用它,請(qǐng)使用“ gradle eclipse”,然后從IDE內(nèi)導(dǎo)入并運(yùn)行它(不需要服務(wù)器)。
免責(zé)聲明
有時(shí),經(jīng)典的CSRF令牌被錯(cuò)誤地視為針對(duì)重播或暴力攻擊的解決方案。 此處列出的無(wú)狀態(tài)方法未涵蓋此類攻擊。 我個(gè)人認(rèn)為這兩種類型的攻擊都應(yīng)該在另一個(gè)層次上進(jìn)行處理,例如使用https和速率限制。 對(duì)于公開(kāi)網(wǎng)站上的任何數(shù)據(jù)輸入,我倆都認(rèn)為這是必須的!
翻譯自: https://www.javacodegeeks.com/2014/10/stateless-spring-security-part-1-stateless-csrf-protection.html
csrf spring
總結(jié)
以上是生活随笔為你收集整理的csrf spring_无状态Spring安全性第1部分:无状态CSRF保护的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: FF任命新全球CEO 贾跃亭等高管宣布增
- 下一篇: spring期刊状态_无状态Spring