Spring Boot应用CSRF防护实战与Spring Security解决方案
📅 2026/7/3 2:37:35
👁️ 阅读次数
📝 编程学习
1. CSRF漏洞的本质与危害
CSRF(Cross-Site Request Forgery)是一种利用用户已登录状态发起的恶意请求攻击。想象这样一个场景:你在咖啡厅用笔记本登录了银行网站,此时浏览器保存了登录凭证。如果这时你点开了黑客发来的钓鱼邮件里的链接,这个页面可能偷偷向银行发送转账请求——因为浏览器会自动带上你的登录凭证,银行系统会认为这是你本人操作。
这种攻击之所以危险,是因为:
- 攻击者无需获取用户凭证
- 用户完全无感知
- 可执行任意权限范围内的操作
- 攻击成本极低
典型攻击流程:
- 用户登录受信任网站A
- 网站A在用户浏览器设置认证Cookie
- 用户未登出A的情况下访问恶意网站B
- B的页面包含自动向A发送请求的代码
- 浏览器自动携带A的Cookie发送请求
- A服务器无法区分该请求是否来自用户真实意愿
2. Spring Boot应用为何容易"裸奔"
很多Spring Boot开发者存在以下误区:
误区一:GET请求不需要防护
@GetMapping("/transfer") public String transferMoney(@RequestParam String toAccount, @RequestParam BigDecimal amount) { // 转账逻辑 }攻击者只需构造一个图片链接:
<img src="http://bank.com/transfer?toAccount=hacker&amount=10000">误区二:POST请求天然安全
<form action="http://bank.com/transfer" method="POST"> <input type="hidden" name="toAccount" value="hacker"> <input type="hidden" name="amount" value="10000"> </form> <script>document.forms[0].submit();</script>误区三:前后端分离无需考虑即使使用AJAX,浏览器仍会自动携带Cookie:
fetch('/api/transfer', { method: 'POST', body: JSON.stringify({toAccount: 'hacker', amount: 10000}), headers: {'Content-Type': 'application/json'} })3. Spring Security防御方案全解析
3.1 基础防护配置
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .and() // 其他配置... } }3.2 深度防御策略
策略一:Cookie+Header双重验证
.csrfTokenRepository(new CookieCsrfTokenRepository() {{ setCookieHttpOnly(false); // 允许JS读取 setCookieName("XSRF-TOKEN"); setHeaderName("X-XSRF-TOKEN"); }})策略二:自定义防护路径
.requireCsrfProtectionMatcher(request -> { // 只保护/api开头的POST/PUT/DELETE请求 return request.getMethod().matches("POST|PUT|DELETE") && request.getRequestURI().startsWith("/api"); })策略三:动态Token刷新
.csrfTokenRepository(new CsrfTokenRepository() { @Override public CsrfToken generateToken(HttpServletRequest request) { return new DefaultCsrfToken( "X-CSRF-TOKEN", "_csrf", UUID.randomUUID().toString() ); } // 其他实现方法... })4. 实战中的坑与解决方案
坑一:文件上传失效
// 错误配置 http.csrf().disable(); // 正确方案 http.csrf().ignoringAntMatchers("/upload");坑二:前后端分离场景前端需要:
- 从Cookie读取XSRF-TOKEN
- 在每次请求Header中添加X-XSRF-TOKEN
// Axios拦截器示例 axios.interceptors.request.use(config => { const token = document.cookie .split('; ') .find(row => row.startsWith('XSRF-TOKEN=')) ?.split('=')[1]; config.headers['X-XSRF-TOKEN'] = token; return config; });坑三:多Tab操作冲突解决方案:每个表单生成独立Token
<form> <input type="hidden" name="_csrf" th:value="${csrfToken}"> <!-- 表单内容 --> </form>5. 高级防护技巧
技巧一:二次验证
@PostMapping("/transfer") public ResponseEntity<?> transfer( @RequestParam String toAccount, @RequestParam BigDecimal amount, @CookieValue(value = "XSRF-TOKEN") String csrfToken, @RequestHeader("X-XSRF-TOKEN") String headerToken) { if(!csrfToken.equals(headerToken)) { throw new InvalidCsrfTokenException(); } // 业务逻辑... }技巧二:操作指纹绑定
String userFingerprint = request.getHeader("User-Agent") + request.getHeader("Accept-Language"); String storedToken = redis.get(userFingerprint); if(!storedToken.equals(request.getHeader("X-CSRF-TOKEN"))) { throw new InvalidCsrfTokenException(); }技巧三:敏感操作限流
@RateLimiter(value = 1, timeUnit = TimeUnit.MINUTES) @PostMapping("/transfer") public ResponseEntity<?> transferMoney() { // 业务逻辑 }6. 渗透测试常见问题
问题一:CSRF Token泄露
通过子域名或XSS漏洞可能窃取Token
解决方案:
.setCookieDomain("maindomain.com") // 限制Cookie域 .setSecure(true) // 仅HTTPS传输问题二:Token未绑定会话
// 错误做法:所有用户使用相同Token String staticToken = "fixed_token_value"; // 正确做法:会话级Token String sessionToken = UUID.randomUUID().toString(); request.getSession().setAttribute("CSRF_TOKEN", sessionToken);问题三:防护覆盖不全检查清单:
- [ ] 所有状态修改操作(POST/PUT/DELETE/PATCH)
- [ ] 文件上传接口
- [ ] GraphQL端点
- [ ] SOAP WebService
- [ ] 第三方回调接口
7. Spring Security 6.x新特性
特性一:CSRF Token自动旋转
.csrfTokenRepository(new RotatingCsrfTokenRepository())特性二:同站点Cookie属性
@Bean public CookieSerializer cookieSerializer() { DefaultCookieSerializer serializer = new DefaultCookieSerializer(); serializer.setSameSite("Strict"); return serializer; }特性三:安全头增强
http.headers() .contentSecurityPolicy("script-src 'self'") .xssProtection() .and() .referrerPolicy(ReferrerPolicy.SAME_ORIGIN);在实际项目中,我建议采用分层防御策略:基础CSRF防护+敏感操作二次验证+关键接口限流。曾经有个电商项目因为忽略CSRF防护,导致攻击者利用图片外链批量修改用户收货地址,这个教训让我在后续所有项目中都严格执行CSRF防护规范。
编程学习
技术分享
实战经验