Java防CSRF攻击实战案例与漏洞修复全解析
📖 文章导读
- 什么是CSRF?它为何危险?
- 真实的Java CSRF攻击案例剖析
- 为什么你的Java Web应用可能“裸奔”?
- 从零搭建:一个带漏洞的Spring Boot案例
- 3种主流防御方案及代码实现
- QA:开发者最常问的5个CSRF问题
- 构建零信任的Java安全防线
什么是CSRF?它为何危险?
CSRF(Cross-Site Request Forgery,跨站请求伪造) 是一种利用用户已登录身份,在用户不知情下发起恶意请求的攻击方式,攻击者通过构造一个看似合法的请求(如转账、改密码、发帖),诱导用户点击或访问,从而以用户的名义执行操作。

核心危险在于:
- 无需窃取用户密码或SessionID
- 利用浏览器自动携带Cookie的机制
- 用户难以察觉,且攻击可批量自动化
根据OWASP统计,CSRF目前仍是企业级Java应用中最常见的高危漏洞之一。
真实的Java CSRF攻击案例剖析
案例:某银行金融系统的“一秒转账”漏洞
某银行Java Web应用采用Spring MVC框架,后台用户管理功能通过/user/transfer?toAccount=XXX&amount=1000实现转账,未添加任何CSRF防护。
攻击过程:
- 用户在银行后台登录,获得有效Session。
- 攻击者在论坛发布帖子,内嵌一个隐藏图片标签:
<img src="http://bank.example.com/user/transfer?toAccount=attacker&amount=5000" width="0" height="0" /> - 用户(仍为登录状态)打开帖子时,浏览器自动加载该图片,发起GET请求。
- 银行服务器验证Session有效,执行转账操作——钱被转走!
关键失败点:
- 未校验请求来源(Origin/Referer)
- 未使用Token验证
- 未区分GET/POST语义(转账不应使用GET)
为什么你的Java Web应用可能“裸奔”?
很多Java开发者存在以下误区:
- ❌ “我的API加了JWT/Token,不需要CSRF防护”
- ❌ “用POST请求就安全了”
- ❌ “只有表单才需要防CSRF”
事实是:
- JWT存储在LocalStorage,但Cookie仍可能被用于身份验证
- 攻击者可构造POST表单(如通过隐藏
<form>+onload提交) - 所有能改变状态的接口(POST/PUT/DELETE)都可能被利用
从零搭建:一个带漏洞的Spring Boot案例
@RestController
public class UserController {
@PostMapping("/updatePassword")
public String updatePassword(@RequestParam String newPwd) {
// 直接更新密码,无任何校验
userService.updatePassword(currentUser(), newPwd);
return "success";
}
}
攻击构造(恶意HTML页面):
<form action="http://victim.com/updatePassword" method="POST">
<input type="hidden" name="newPwd" value="hacked123">
</form>
<script>document.forms[0].submit();</script>
用户只要打开此页面,密码即被篡改。
3种主流防御方案及代码实现
✅ 方案一:CSRF Token(最推荐,兼容性好)
原理: 服务器生成随机Token,嵌入表单/请求头,服务端校验。
Spring Security集成:
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
return http.build();
}
}
前端需在请求头添加X-CSRF-TOKEN,后端自动校验。
✅ 方案二:SameSite Cookie属性
原理: 浏览器限制Cookie跨站发送。
@Configuration
public class CookieConfig {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieCustomizer() {
return factory -> factory.addContextCustomizers(context -> {
context.setSessionCookieName("JSESSIONID");
context.setUseHttpOnly(false);
// 设置SameSite为Strict或Lax
context.setSessionCookieDomain(null);
});
}
}
注意: 需配合Spring Boot 2.6+或使用SameSite过滤器。
✅ 方案三:双重提交Cookie + 自定义请求头(适用于前后端分离)
原理: 在Cookie和自定义请求头中同时携带同一随机值,后端比较是否一致。
// 后端过滤器示例
String cookieValue = extractCookie(request, "X-Session-Id");
String headerValue = request.getHeader("X-Session-Id");
if (!cookieValue.equals(headerValue)) {
throw new CsrfException("CSRF验证失败");
}
❓ QA:开发者最常问的5个CSRF问题
Q1:RESTful API需要防CSRF吗?
A:需要!只要API使用Cookie/Session认证,就必须防护,JWT若放在LocalStorage则无需,但放在Cookie仍需。
Q2:用HTTPS能防CSRF吗?
A:不能,HTTPS仅保证传输加密,不验证请求来源,攻击者仍可发起跨站请求。
Q3:CSRF Token如何保证唯一性?
A:建议使用java.security.SecureRandom生成,或Spring Security自动生成的Token,每次请求后可选刷新。
Q4:多域名场景如何部署CSRF?
A:使用Origin + Referer校验,或采用CORS白名单配合自定义Token,注意不要暴露Token给第三方域名。
Q5:前端Vue/React如何配合CSRF Token?
A:从Cookie读取Token(如XSRF-TOKEN),然后在每次请求时通过拦截器自动附加到请求头。
构建零信任的Java安全防线
CSRF攻击看似“老派”,但在实践中高频出现。防御思路应从“信任来源”转向“验证请求意图”。
- 必须使用的:CSRF Token(Spring Security原生支持)
- 建议配合的:SameSite=Lax(保护GET请求)
- 务必避免的:仅依赖Referer、允许GET请求改变状态、省略Token校验开关
每次代码上线前运行OWASP ZAP或Burp Suite扫描,可自动检测CSRF漏洞。安全无小事,防微杜渐,从每一行代码开始。
📌 本文综述了Java环境下CSRF的攻击原理、实战案例与3种核心防护方案,适用于Spring Boot传统架构与前后端分离架构,下一期将详解“Java文件上传漏洞防护”,敬请关注。