你以为“点了个链接”不算操作?那 CSRF 为什么能把你钱转走?
🏆本文收录于《滚雪球学SpringBoot 3》:
https://blog.csdn.net/weixin_43970743/category_12795608.html,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。
本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】https://blog.csdn.net/weixin_43970743/article/details/151115907,你想学习的都被收集在内,快速投入学习!!两不误。
若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》:https://blog.csdn.net/weixin_43970743/category_11599389.html,涵盖SpringBoot所有版本教学文章。
演示环境说明:
- 开发工具:IDEA 2021.3
- JDK版本: JDK 17(推荐使用 JDK 17 或更高版本,因为 Spring Boot 3.x 系列要求 Java 17,Spring Boot 3.5.4 基于 Spring Framework 6.x 和 Jakarta EE 9,它们都要求至少 JDK 17。)
- Spring Boot版本:3.5.4(于25年7月24日发布)
- Maven版本:3.8.2 (或更高)
- Gradle:(如果使用 Gradle 构建工具的话):推荐使用 Gradle 7.5 或更高版本,确保与 JDK 17 兼容。
- 操作系统:Windows 11
反问一句:**浏览器凭什么替你“自动带上 Cookie”,还不问你一句“你确定吗”?**🙂
1) CSRF 攻击原理演示:它到底怎么“借你身份”?
1.1 核心前提:认证信息存在 Cookie(尤其是 Session Cookie)
CSRF 最常见的成功条件很朴素:
- 你已经登录了目标站点 A(浏览器里有 A 的 Cookie / Session)
- 你又访问了攻击站点 B(不需要登录)
- B 诱导你的浏览器向 A 发起一个“会改变数据的请求”(转账、改邮箱、改密码……)
- 浏览器会自动附带 A 的 Cookie,A 看到 Cookie 就以为“是你本人”在操作
OWASP 的 CSRF 防护指南强调:对依赖 Cookie 的 Web 应用,CSRF token 仍然是必需的,并提醒 XSS 能绕过很多 CSRF 缓解,但并不意味着 CSRF token 不重要。
重点:CSRF 不是“偷 Cookie”,而是“借 Cookie”。
Cookie 没丢,但你的权限被人拿去当扳手拧螺丝了。
1.2 攻击演示 1:一段“看似无害”的自动提交表单
假设你有个银行站点:https://bank.example.com/transfer
并且它居然允许用表单 POST 转账(很多真实系统就是这么干的,别笑😅)。
攻击者在自己的网站放一个页面:
<!-- attacker.example.com/csrf.html -->
<form action="https://bank.example.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker-account" />
<input type="hidden" name="amount" value="10000" />
</form>
<script>
// 用户打开这个页面,表单就自动提交
document.forms[0].submit();
</script>
你一访问这个页面,浏览器就向 bank.example.com 发 POST。
关键点来了:浏览器会自动带上 bank.example.com 的 Cookie。
于是银行后端如果只看 Session,就会执行转账。
1.3 攻击演示 2:GET 也能出事(如果你把“改数据”放在 GET)
OWASP 也明确提醒:不要用 GET 做状态改变操作。
如果你把“注销 / 删除 / 关注 / 点赞”这种动作做成 GET:
<img src="https://app.example.com/account/delete?id=123" />
用户一打开攻击者页面,图片加载就触发请求。
这类 bug 真的不罕见,而且非常“社死”。
1.4 你可能会问:CORS 不是能挡住跨站请求吗?
能挡“读响应”,挡不住“发请求”。
CSRF 并不需要读响应内容,它只需要“请求到达并被当成你处理”。
所以别指望 CORS 替你做 CSRF 防护——它俩解决的不是一个问题。
2) Spring Security 6 默认的 CSRF Token 策略:别被“CookieXsrftokenRepository”这个说法带偏
你在大纲里写了“Spring Security 6 默认策略(CookieXsrftokenRepository)”。这里我得非常小心地纠正一句:
Spring Security 默认把期望的 CSRF token 存在 HttpSession(HttpSessionCsrfTokenRepository)里,不是默认放 Cookie。官方文档写得很明确:默认使用 HttpSessionCsrfTokenRepository,无需额外代码。
那 Cookie 方案是什么?是 Spring Security 提供的 CookieCsrfTokenRepository,主要为了支持 JavaScript/SPA 场景:它把 token 写到名为 XSRF-TOKEN 的 Cookie,并从请求头 X-XSRF-TOKEN(或参数 _csrf)读取。官方文档同样写得很清楚。
所以结论是:
- 默认:Session 存 token(服务端渲染/传统 Web 很顺)
- SPA 常用:Cookie 存 token + Header 传 token(前端更好拿)
2.1 Spring Security 的 CSRF 保护“默认开启”,并且只拦截“会改数据”的方法
Spring Security 文档在 CSRF 章节强调默认启用,并建议在考虑禁用前先评估应用是否合理。
通常它会对 POST/PUT/PATCH/DELETE 等“非安全方法”做 CSRF 校验(GET/HEAD/OPTIONS/TRACE 通常不要求 token)。你真正要记住的是:
只要你的认证是 Cookie,且请求能改变状态,CSRF 就该在场。
3) SPA 前后端分离架构下:CSRF 配置怎么做才不拧巴?
SPA(React/Vue/Angular)最常见的现实是:
- 前端发 AJAX(fetch/axios)
- 后端是 Spring Security
- 认证要么是 Cookie Session,要么是 Cookie 存 JWT(很多团队这样搞)
这时 CSRF 的“拿 token / 传 token / 刷新 token”就容易踩坑。
Spring Security 官方文档专门有“JavaScript 应用 / SPA”小节,明确建议:为了让前端能获取 CSRF token,可以配置把期望 token 存到 Cookie;并且指出在 SPA 集成里要考虑 BREACH 保护与延迟 token(deferred tokens),还给了完整配置示例方向。
3.1 一套“能落地”的 Spring Security 6(Servlet)配置示例
下面这份配置思路是:
- 用
CookieCsrfTokenRepository给前端发XSRF-TOKENcookie - 允许 JS 读取该 cookie(通常用
withHttpOnlyFalse()) - 前端在每个非 GET 请求里带上
X-XSRF-TOKENheader - 配合 Spring Security 对 SPA 的特殊处理(BREACH/延迟 token 的坑)
注意:不同版本的 Spring Security 在“token 的 request handler”上有演进,官方文档对 SPA 的注意事项写得很具体:cookie 场景下 JS 只能拿到“原始 token”,而 Spring Security 默认会对 token 做 BREACH 相关处理,因此需要合适的 request handler 来解析实际 token;并且登录/登出后 token cookie 会被清除,需要刷新 token。
示例(偏通用写法,按你项目版本微调即可):
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
// 1) SPA 常用:把 token 存 Cookie,前端可读
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// 2) 这里通常需要结合官方 SPA 章节的“请求处理器”建议
// 以适配 BREACH 保护与 cookie 场景下 token 解析
// 具体实现建议跟随你所用 Spring Security 小版本的官方示例。
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}
上面我刻意没硬塞一个“看似万能”的 handler 实现,因为这块版本差异很敏感:Spring Security 官方 SPA 小节明确提醒了 BREACH 与 deferred token 的处理复杂度,并给出完整配置思路。最稳妥的做法就是:照官方 SPA 小节的示例配(它就是为这个坑写的)。
3.2 前端(axios / fetch)怎么带 token?
如果你用了 CookieCsrfTokenRepository,默认约定是:
- cookie 名:
XSRF-TOKEN - header 名:
X-XSRF-TOKEN
这些默认值来自 Angular 体系,Spring Security 文档也明确写出。
fetch 示例:
function getCookie(name) {
const m = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
return m ? decodeURIComponent(m[2]) : null;
}
async function postJson(url, body) {
const token = getCookie('XSRF-TOKEN');
return fetch(url, {
method: 'POST',
credentials: 'include', // 关键:让 cookie 一起带上(如果你用 cookie 认证)
headers: {
'Content-Type': 'application/json',
'X-XSRF-TOKEN': token ?? '',
},
body: JSON.stringify(body),
});
}
小提醒(很现实):
- 你如果跨域(前端域名和后端域名不同),还得把
SameSite、CORS、credentials这些一起配对。- CSRF + CORS + SameSite 混在一起时,最容易出现的症状是:明明有 cookie,但后端一直 403。这时候别急着骂后端,先把浏览器 Network 抓包看清楚:cookie 到底发没发?header 到底带没带?
4) 何时可以安全地关闭 CSRF?
这个问题我特别想用一句“人话结论”先顶在前面:
只要你还依赖 Cookie 自动携带认证(Session 或 Cookie-JWT),就别轻易关 CSRF。
如果你是纯 API、纯无状态、认证完全走 Authorization header(Bearer token),通常可以考虑关闭。
Spring Security 文档也明确提醒:CSRF 默认启用;在禁用前要评估是否适用于你的应用。
OWASP 也强调:应根据客户端与认证方式选择合适 CSRF 方案,并指出有状态系统适合同步器 token、无状态系统可考虑双重提交 cookie等策略。
4.1 可以考虑关闭 CSRF 的常见场景(满足越多越安全)
- 后端是纯 REST API,不直接被浏览器“随便访问页面就带 cookie”这种机制驱动
- 认证不使用 Cookie,而是每次请求必须带
Authorization: Bearer <token> - Token 不会被浏览器自动附带(攻击者页面发请求也拿不到你的 token)
- 你对 XSS 有严格防护(因为 XSS 能把很多防护打穿,OWASP 也强调这点)
此时 CSRF 的威胁面会显著缩小——因为攻击者无法让浏览器“自动携带凭证”完成伪造请求。
4.2 不建议关闭 CSRF 的典型场景(很容易出大事)
- 你用 Session Cookie 做登录态(传统 Web / 管理后台很常见)
- 你把 JWT 放 Cookie(也很常见)并且请求自动带 cookie
- 你的网站同时有“浏览器可访问的管理操作”(改密码、绑邮箱、支付确认等)
这时候你关 CSRF,基本等于:
**“我把保险柜门关上了,但把钥匙插在门上。”**🙂
4.3 “我已经用了 SameSite Cookie,还需要 CSRF 吗?”
SameSite 能显著降低某些 CSRF 风险,但不是所有场景都覆盖:
- 业务上可能需要
SameSite=None; Secure(跨站 SSO/第三方集成等) - 子域名/复杂域策略可能引入新坑(OWASP 也提醒过域与子域共享 cookie 的风险点)
所以更稳妥的工程答案通常是:
SameSite 是加固,不是替代。Cookie 认证场景下 CSRF token 仍然是主力。
结语:CSRF 最可怕的不是“技术”,是“你以为它不会发生在你身上”
CSRF 这类漏洞很烦的一点是:它成功时你很难第一时间察觉——请求看起来是“合法用户发的”,日志也像真的一样。等你发现数据被改了,第一反应还可能是“是不是同事手滑了”。
所以我最后还是想用一个不太客气但很实用的反问收尾:
**你愿意把安全建立在“用户不会点奇怪链接”这种祈祷上,还是愿意让框架的 CSRF 机制替你扛住人性的随机性?**😄
🧧福利赠与你🧧
无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。
最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。
同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G PDF编程电子书、简历模板、技术文章Markdown文档等海量资料。
ps:本文涉及所有源代码,均已上传至Gitee:
https://gitee.com/bugjun01/SpringBoot-demo开源,供同学们一对一参考 Gitee传送门https://gitee.com/bugjun01/SpringBoot-demo,同时,原创开源不易,欢迎给个star🌟,想体验下被🌟的感jio,非常感谢❗
🫵 Who am I?
我是 bug菌:
- 热活跃于 CSDN:
https://blog.csdn.net/weixin_43970743| 掘金:https://juejin.cn/user/695333581765240| InfoQ:https://www.infoq.cn/profile/4F581734D60B28/publish| 51CTO:https://blog.51cto.com/u_15700751| 华为云:https://bbs.huaweicloud.com/community/usersnew/id_1582617489455371| 阿里云:https://developer.aliyun.com/profile/uolxikq5k3gke| 腾讯云:https://cloud.tencent.com/developer/user/10216480/articles等技术社区; - CSDN 博客之星 Top30、华为云多年度十佳博主&卓越贡献奖、掘金多年度人气作者 Top40;
- 掘金、InfoQ、51CTO 等平台签约及优质作者;
- 全网粉丝累计 30w+。
更多高质量技术内容及成长资料,可查看这个合集入口 👉 点击查看:https://bbs.csdn.net/topics/612438251 👈️
硬核技术公众号 「猿圈奇妙屋」https://bbs.csdn.net/topics/612438251 期待你的加入,一起进阶、一起打怪升级。
- End -
- 点赞
- 收藏
- 关注作者
评论(0)