浅析Spring Security如何防止CSRF攻击
目录
- 一、CSRF 攻击简介
- 二、Spring Security 防止 CSRF 攻击的机制
- 1. 默认启用 CSRF 保护
- 2. CSRF 令牌的生成与验证
- 3. 配置与自定义
- 4. 在请求中包含 CSRF 令牌
- 三、最佳实践
- 四、方法补充
- 五、总结
一、CSRF 攻击简介
CSRF(Cross-Site Request Forgery)攻击,即跨站请求伪造攻击,是一种利用用户已登录的身份,在用户不知情的情况下,强制其执行非预期操作的攻击方式。攻击者通常会通过伪造的请求,诱使用户在已登录js的应用程序中执行恶意操作,例如转账、修改个人信息等。
二、Spring Security 防止 CSRF 攻击的机制
1. 默认启用 CSRF 保护
从 Spring Security 4.0 开始,默认情况下会启用 CSRF 保护。这意味着对于 PATCH、POST、PUT 和 DELETE 方法的请求,Spring Security 会自动进行 CSRF 验证。
2. CSRF 令牌的生成与验证
Spring Security 通过生成唯一的 CSRF 令牌(Token)来防止 CSRF 攻击。具体流程如下:
- 生成 CSRF 令牌:服务器在处理请求时,会生成一个唯一的 CSRF 令牌,并将其存储在用户的会话(HttpSession)或 Cookie 中。
- 客户端提交 CSRF 令牌:客户端在提交表单或发送 AJAX 请求时,需要将 CSRF 令牌包含在请求中。通常,这个令牌会作为表单的一个隐藏字段或请求头的一部分发送。
- 服务器验证 CSRF 令牌:服务器在接收到请求后,会从请求中提取 CSRF 令牌,并与存储在会话或 Cookie 中的令牌进行比较。如果两者一致,则认为请求是合法的;如果不一致,则认为是 CSRF 攻击,服务器会拒绝该请求。
3. 配置与自定义
开启或关闭 CSRF 保护:可以通过配置来开启或关闭 CSRF 保护。例如,在基于 Java 配置的项目中,可以通过以下代码关闭 CSRF 保护:
http.csrf().disable();
或者在基于 XML 配置的项目中,使用以下代码:
<security:csrf disabled="true"/>
自定义 CSRF 令牌存储方式:Spring Security 提供了 CsrfTokenRepository
接口,开发者可以实现该接口来自定义 CSRF 令牌的存储和获取方式。默认实现是 HttpSessionCsrfTokenRepository
,它将 CSRF 令牌存储在 HttpSession 中。
4. 在请求中包含 CSRF 令牌
表单提交:在 html 表单中,可以通过 Thymeleaf 等模板引擎自动包含 CSRF 令牌。例如:
<form action="/submit" method="POST"> <input type="hidden" name="_csrf" value="${_csrf.token}" /> <button type="submit">Submit</button> </form>
AJAX 请求:在使用 AJAX 提交请求时,需要手动将 CSRF 令牌添加到请求头中。例如,使用 jquery 时可以这样操作:
$.ajax({ url: '/suhttp://www.devze.combmit', 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 标签:
<meta name="_csrf" content="${_csrf.token}" />
三、最佳实践
- 始终启用 CSRF 保护:除非有充分的理由,否则应始终启用 CSRF 保护,以确保应用程序的安全性。
- 使用 HTTPS:通过使用 HTTPS,可以防止攻击者拦截和篡改请求,从而提高应用程序的安全性。
- 限制 CSRF 令牌的作用域:可以配置 CSRF 令牌仅对特定的端点有效,从而减少潜在的安全风险。
- 定期更新依赖:及时更新 Spring Security 和其他相关依赖,以修复已知的安全漏洞。
四、方法补充
Spring Security 防止 CSRF 攻击
使用 security 是 3.3.2 版本
1、 启用 CSRF ,security 自带功能
@Bean public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { // 禁用默认的登录和退出 httpSecurity.formLogin(AbstractHttpConfigurer::disable); httpSecurity.logout(AbstractHttpConfigurer::disable); //开启 CSRF httpSecurity.csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())); httpSecurity.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // 配置拦截规则 httpSecurity.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests // 放行白名单 .requestMatchers(CollUtil.isEmpty(ignoreWhiteProperties.getWhites()) ? new String[0] : ignoreWhiteProperties.getWhites().toArray(new String[0])).permitAll() // 需android要鉴权认证 .anyRequest().authenticated()); // 添加过滤器 httpSecurity.addFilterBefore(authTokenFilter(), UsernamePasswordAuthenticationFilter.class); // 异常时认证处理流程 httpSecurity.exceptionHandling( exceptionHandlingConfigurer -> exceptionHandlingConfigurer.authenticationEntryPoint( authenticationEntryPoint()).AccessDeniedHandler(accessDeniedHandler())); return httpSecurity.build(); }
2、 spring gateway 中添加过滤器先进行添加36位 空字符,再进行 base64 编码,重新写入请求头中
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个空字符,使其校验通过 */ @Component public class CsrfGlobalFilter implements GlobalFilter { private final StringBuffer preHead = new StringBuffer(); public CsrfGlobalFilter(){ // 生成36位随机数 preHead.append("\u0000".repeat(36)); } @OverrideGRTLXyl public Mono<Void> filter(ServerWebEpythonxchange 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); } }
总结:
Security 自带 CSRF 功能,他会在第一次 POST 请求后会在 cookie 中存储一个 csrf_token 值,前端有的框架会自动识别,下次请求会自动携带上,就可以防止 CSRF 攻击
注意:
按常理是这样的,可是 3.3.2 版本跟进源码发现,需要有一个 36 位 前缀随机数,但是后续需要异或运算= csrf_token 本身才放行。这里应该是要通过计算得出。
上述为了能实现功能,暂时直接用 “空字符” 也能成功
五、总结
Spring Security 提供了强大的 CSRF 防护机制,通过生成和验证 CSRF 令牌,有效防止了 CSRF 攻击。开发者可以通过配置和自定义来满足不同的安全需求,同时遵循最佳实践,确保应用程序的安全性。
到此这篇关于浅析Spring Security如何防止CSRF攻击的文章就介绍到这了,更多相关Spring Security防CSRF攻击内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论