javascript
无状态Spring安全性第1部分:无状态CSRF保护
如今,隨著RESTful架構(gòu)變得越來越標準,可能值得花一些時間重新考慮當前的安全方法。 在這個小系列的博客文章中,我們將探索一些以無狀態(tài)方式解決與Web相關(guān)的安全問題的相對較新的方法。 這第一篇文章是關(guān)于保護您的網(wǎng)站免受跨站請求偽造(CSRF)的攻擊。
總結(jié):什么是跨站點偽造?
CSRF攻擊基于揮之不去的身份驗證Cookie。 在登錄或以其他方式標識為網(wǎng)站上的唯一訪問者之后,該網(wǎng)站可能會在瀏覽器中留下cookie。 如果不顯式注銷或以其他方式刪除此cookie,它可能會保持一段時間有效。
另一個站點可以通過使瀏覽器向受攻擊的站點發(fā)出(跨站點)請求來濫用此功能。 例如,包括一些用于在“ http://siteunderattack.com/changepassword?pw=hacked”標簽上進行POST的Javascript,將使瀏覽器發(fā)出該請求,并將對該域仍然有效的任何(身份驗證)cookie附加到該請求!
即使單源策略(SOP)不允許惡意站點訪問響應(yīng)的任何部分。 從上面的示例中可以很明顯地看出,如果請求的URL在后臺觸發(fā)任何副作用(狀態(tài)更改),則損害已經(jīng)完成。
常用方法
常用的解決方案是引入所謂的共享秘密CSRF令牌的要求,并將其作為先前響應(yīng)的一部分讓客戶端知道。
然后,對于任何有副作用的請求,客戶端都必須將其ping回服務(wù)器。 可以直接在表單中作為隱藏字段或作為自定義HTTP標頭完成此操作。 無論哪種方式,其他站點都無法成功產(chǎn)生包含正確CSRF令牌的請求,因為SOP阻止跨站點讀取來自服務(wù)器的響應(yīng)。 這種方法的問題在于服務(wù)器需要記住會話中每個用戶的每個CSRF令牌的值。
無狀態(tài)方法
1.切換到完整且設(shè)計正確的基于JSON的REST API。
單源策略僅允許跨站點的HEAD / GET和POST。 POST只能是以下啞劇類型之一:application / x-www-form-urlencoded,multipart / form-data或text / plain。 確實沒有JSON! 現(xiàn)在考慮到GET永遠不要在任何經(jīng)過??適當設(shè)計的基于HTTP的API中觸發(fā)副作用,這讓您可以簡單地禁止任何非JSON POST / PUT / DELETE,一切都很好。 對于上傳文件(多部分/表單數(shù)據(jù))的方案,仍然需要明確的CSRF保護。
2.檢查HTTP Referer標頭。
通過檢查仍然易受攻擊的場景(例如多部分/表單數(shù)據(jù)POST)的Referer標頭的存在和內(nèi)容,可以進一步完善上述方法。 瀏覽器使用此標頭來指定觸發(fā)請求的確切頁面(url)。 這可以輕松地用于檢查站點的預(yù)期域。 請注意,如果選擇進行此類檢查,則在沒有標題的情況下,切勿允許請求。
3.客戶端生成的CSRF令牌。
讓客戶端在Cookie和自定義HTTP標頭中生成并發(fā)送相同的唯一秘密值。 考慮到僅允許網(wǎng)站為其自己的域讀取/寫入Cookie,因此只有真實網(wǎng)站才能在兩個標頭中發(fā)送相同的值。 使用這種方法,您的服務(wù)器要做的就是在每個請求無狀態(tài)的基礎(chǔ)上檢查兩個值是否相等!
實作
著眼于第三種基于顯式但基于無狀態(tài)CSRF令牌的安全性的方法,讓我們看看使用Spring Boot和Spring Security的代碼的外觀。
在Spring Boot中,您會獲得一些不錯的默認安全設(shè)置,您可以使用自己的配置適配器對其進行微調(diào)。 在這種情況下,所需要做的就是禁用默認的csrf行為并添加自己的StatelessCSRFFilter:
自定義CSRF保護
@EnableWebSecurity @Order(1) public class StatelessCSRFSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().addFilterBefore(new StatelessCSRFFilter(), CsrfFilter.class);} }這是StatelessCSRFFilter的實現(xiàn):
自定義CSRF過濾器
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();}} }不出所料,無狀態(tài)版本在兩個標頭值上只做一個簡單的equals()。
客戶端實施
客戶端實現(xiàn)也很簡單,尤其是在使用AngularJS時。 AngularJS已經(jīng)提供了內(nèi)置的CSRF令牌支持。 如果您告訴它要讀取的cookie,它將自動將其值發(fā)送到您選擇的自定義標頭中。 (瀏覽器負責(zé)發(fā)送cookie標頭本身。)
您可以按以下方式覆蓋AngularJS的默認名稱(XSRF而不是CSRF):
設(shè)置適當?shù)牧钆泼Q
$http.defaults.xsrfHeaderName = 'X-CSRF-TOKEN'; $http.defaults.xsrfCookieName = 'CSRF-TOKEN';此外,如果您想為每個請求生成一個新的令牌值,則可以將自定義攔截器添加到$ 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上找到一個完整的可用示例。
確保已安裝gradle 2.0,并使用“ gradle build”和“ gradle run”簡單地運行它。 如果要像eclipse一樣在IDE中使用它,請使用“ gradle eclipse”,只需從IDE內(nèi)導(dǎo)入并運行它即可(無需服務(wù)器)。
免責(zé)聲明
有時,經(jīng)典的CSRF令牌被錯誤地視為針對重播或暴力攻擊的解決方案。 此處列出的無狀態(tài)方法未涵蓋此類攻擊。 我個人認為這兩種類型的攻擊都應(yīng)從另一個角度進行考慮,例如使用https和速率限制。 對于公開網(wǎng)站上的任何數(shù)據(jù)輸入,我倆都認為這是必須的!
翻譯自: https://www.javacodegeeks.com/2014/10/stateless-spring-security-part-1-stateless-csrf-protection.html
總結(jié)
以上是生活随笔為你收集整理的无状态Spring安全性第1部分:无状态CSRF保护的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 飞智ios怎么设置(ios飞智开启飞鼠功
- 下一篇: 带有Swagger的Spring Res