淺析Spring?Security如何防止CSRF攻擊
一、CSRF 攻擊簡介
CSRF(Cross-Site Request Forgery)攻擊,即跨站請(qǐng)求偽造攻擊,是一種利用用戶已登錄的身份,在用戶不知情的情況下,強(qiáng)制其執(zhí)行非預(yù)期操作的攻擊方式。攻擊者通常會(huì)通過偽造的請(qǐng)求,誘使用戶在已登錄的應(yīng)用程序中執(zhí)行惡意操作,例如轉(zhuǎn)賬、修改個(gè)人信息等。
二、Spring Security 防止 CSRF 攻擊的機(jī)制
1. 默認(rèn)啟用 CSRF 保護(hù)
從 Spring Security 4.0 開始,默認(rèn)情況下會(huì)啟用 CSRF 保護(hù)。這意味著對(duì)于 PATCH、POST、PUT 和 DELETE 方法的請(qǐng)求,Spring Security 會(huì)自動(dòng)進(jìn)行 CSRF 驗(yàn)證。
2. CSRF 令牌的生成與驗(yàn)證
Spring Security 通過生成唯一的 CSRF 令牌(Token)來防止 CSRF 攻擊。具體流程如下:
- 生成 CSRF 令牌:服務(wù)器在處理請(qǐng)求時(shí),會(huì)生成一個(gè)唯一的 CSRF 令牌,并將其存儲(chǔ)在用戶的會(huì)話(HttpSession)或 Cookie 中。
- 客戶端提交 CSRF 令牌:客戶端在提交表單或發(fā)送 AJAX 請(qǐng)求時(shí),需要將 CSRF 令牌包含在請(qǐng)求中。通常,這個(gè)令牌會(huì)作為表單的一個(gè)隱藏字段或請(qǐng)求頭的一部分發(fā)送。
- 服務(wù)器驗(yàn)證 CSRF 令牌:服務(wù)器在接收到請(qǐng)求后,會(huì)從請(qǐng)求中提取 CSRF 令牌,并與存儲(chǔ)在會(huì)話或 Cookie 中的令牌進(jìn)行比較。如果兩者一致,則認(rèn)為請(qǐng)求是合法的;如果不一致,則認(rèn)為是 CSRF 攻擊,服務(wù)器會(huì)拒絕該請(qǐng)求。
3. 配置與自定義
開啟或關(guān)閉 CSRF 保護(hù):可以通過配置來開啟或關(guān)閉 CSRF 保護(hù)。例如,在基于 Java 配置的項(xiàng)目中,可以通過以下代碼關(guān)閉 CSRF 保護(hù):
http.csrf().disable();
或者在基于 XML 配置的項(xiàng)目中,使用以下代碼:
<security:csrf disabled="true"/>
自定義 CSRF 令牌存儲(chǔ)方式:Spring Security 提供了 CsrfTokenRepository
接口,開發(fā)者可以實(shí)現(xiàn)該接口來自定義 CSRF 令牌的存儲(chǔ)和獲取方式。默認(rèn)實(shí)現(xiàn)是 HttpSessionCsrfTokenRepository
,它將 CSRF 令牌存儲(chǔ)在 HttpSession 中。
4. 在請(qǐng)求中包含 CSRF 令牌
表單提交:在 HTML 表單中,可以通過 Thymeleaf 等模板引擎自動(dòng)包含 CSRF 令牌。例如:
<form action="/submit" method="POST"> <input type="hidden" name="_csrf" value="${_csrf.token}" /> <button type="submit">Submit</button> </form>
AJAX 請(qǐng)求:在使用 AJAX 提交請(qǐng)求時(shí),需要手動(dòng)將 CSRF 令牌添加到請(qǐng)求頭中。例如,使用 jQuery 時(shí)可以這樣操作:
$.ajax({ url: '/submit', type: 'POST', headers: { 'X-CSRF-TOKEN': $('meta[name="_csrf"]').attr('content') }, data: { // Your data here }, success: function(response) { console.log(response); }, error: function(xhr, status, error) { console.error('Error:', error); } });
在 HTML 中,需要包含 CSRF 令牌的 meta 標(biāo)簽:
<meta name="_csrf" content="${_csrf.token}" />
三、最佳實(shí)踐
- 始終啟用 CSRF 保護(hù):除非有充分的理由,否則應(yīng)始終啟用 CSRF 保護(hù),以確保應(yīng)用程序的安全性。
- 使用 HTTPS:通過使用 HTTPS,可以防止攻擊者攔截和篡改請(qǐng)求,從而提高應(yīng)用程序的安全性。
- 限制 CSRF 令牌的作用域:可以配置 CSRF 令牌僅對(duì)特定的端點(diǎn)有效,從而減少潛在的安全風(fēng)險(xiǎn)。
- 定期更新依賴:及時(shí)更新 Spring Security 和其他相關(guān)依賴,以修復(fù)已知的安全漏洞。
四、方法補(bǔ)充
Spring Security 防止 CSRF 攻擊
使用 security 是 3.3.2 版本
1、 啟用 CSRF ,security 自帶功能
@Bean public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { // 禁用默認(rèn)的登錄和退出 httpSecurity.formLogin(AbstractHttpConfigurer::disable); httpSecurity.logout(AbstractHttpConfigurer::disable); //開啟 CSRF httpSecurity.csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())); httpSecurity.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // 配置攔截規(guī)則 httpSecurity.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests // 放行白名單 .requestMatchers(CollUtil.isEmpty(ignoreWhiteProperties.getWhites()) ? new String[0] : ignoreWhiteProperties.getWhites().toArray(new String[0])).permitAll() // 需要鑒權(quán)認(rèn)證 .anyRequest().authenticated()); // 添加過濾器 httpSecurity.addFilterBefore(authTokenFilter(), UsernamePasswordAuthenticationFilter.class); // 異常時(shí)認(rèn)證處理流程 httpSecurity.exceptionHandling( exceptionHandlingConfigurer -> exceptionHandlingConfigurer.authenticationEntryPoint( authenticationEntryPoint()).accessDeniedHandler(accessDeniedHandler())); return httpSecurity.build(); }
2、 spring gateway 中添加過濾器先進(jìn)行添加36位 空字符,再進(jìn)行 base64 編碼,重新寫入請(qǐng)求頭中
package com.changan.gateway.filter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.nio.charset.StandardCharsets; import java.util.Base64; /** * CSRF 在頭信息中額外生成36個(gè)空字符,使其校驗(yàn)通過 */ @Component public class CsrfGlobalFilter implements GlobalFilter { private final StringBuffer preHead = new StringBuffer(); public CsrfGlobalFilter(){ // 生成36位隨機(jī)數(shù) preHead.append("\u0000".repeat(36)); } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //獲取前端傳遞的 X-Xsrf-Token 頭信息 String headerValue = exchange.getRequest().getHeaders().getFirst("X-Xsrf-Token"); //在 X-Xsrf-Token 頭信息之前添加 36 位空字符 headerValue = preHead + headerValue; //base64 加密 String encodedValue = Base64.getEncoder().encodeToString(headerValue.getBytes(StandardCharsets.UTF_8)); exchange.getRequest().mutate() .header("X-Xsrf-Token", encodedValue) .build(); return chain.filter(exchange); } }
總結(jié):
Security 自帶 CSRF 功能,他會(huì)在第一次 POST 請(qǐng)求后會(huì)在 cookie 中存儲(chǔ)一個(gè) csrf_token 值,前端有的框架會(huì)自動(dòng)識(shí)別,下次請(qǐng)求會(huì)自動(dòng)攜帶上,就可以防止 CSRF 攻擊
注意:
按常理是這樣的,可是 3.3.2 版本跟進(jìn)源碼發(fā)現(xiàn),需要有一個(gè) 36 位 前綴隨機(jī)數(shù),但是后續(xù)需要異或運(yùn)算= csrf_token 本身才放行。這里應(yīng)該是要通過計(jì)算得出。
上述為了能實(shí)現(xiàn)功能,暫時(shí)直接用 “空字符” 也能成功
五、總結(jié)
Spring Security 提供了強(qiáng)大的 CSRF 防護(hù)機(jī)制,通過生成和驗(yàn)證 CSRF 令牌,有效防止了 CSRF 攻擊。開發(fā)者可以通過配置和自定義來滿足不同的安全需求,同時(shí)遵循最佳實(shí)踐,確保應(yīng)用程序的安全性。
到此這篇關(guān)于淺析Spring Security如何防止CSRF攻擊的文章就介紹到這了,更多相關(guān)Spring Security防CSRF攻擊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
教你如何精準(zhǔn)統(tǒng)計(jì)出你的接口"QPS"
今天小編就為大家分享一篇關(guān)于QPS的精準(zhǔn)計(jì)算方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2021-08-08Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(60)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你2021-08-08java阿拉伯?dāng)?shù)字轉(zhuǎn)中文數(shù)字
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)阿拉伯?dāng)?shù)字轉(zhuǎn)換為中文數(shù)字,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04帶你用Java方法輕松實(shí)現(xiàn)樹的同構(gòu)
給定兩棵樹T1和T2。如果T1可以通過若干次左右孩子互換就變成T2,則我們稱兩棵樹是“同構(gòu)”的。例如圖1給出的兩棵樹就是同構(gòu)的,因?yàn)槲覀儼哑渲幸豢脴涞慕Y(jié)點(diǎn)A、B、G的左右孩子互換后,就得到另外一棵樹2021-06-06Spring?Boot實(shí)現(xiàn)JWT?token自動(dòng)續(xù)期的實(shí)現(xiàn)
本文主要介紹了Spring?Boot實(shí)現(xiàn)JWT?token自動(dòng)續(xù)期,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Java枚舉_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
enum 的全稱為 enumeration, 是 JDK 5 中引入的新特性,存放在 java.lang 包中。這篇文章給大家介紹Java枚舉相關(guān)知識(shí),需要的的朋友參考下2017-04-04