CSRF 和 CORS 有什么区别?Spring Security 如何防护?
简化版
CSRF(跨站请求伪造)是一种攻击——利用「浏览器会自动带上目标站点的 Cookie」的特性,诱导已登录用户的浏览器向目标站点发起恶意写请求(如转账),服务端如果只认 Cookie 就会当成用户本人操作。CORS(跨源资源共享)是浏览器的一种安全策略——控制「一个源的脚本能不能读取另一个源的响应」。两者不是一回事:CSRF 是攻击、要防;CORS 是访问控制机制、不是鉴权、也不能替代 CSRF 防护。Spring Security:基于 Cookie/Session 的写请求要开 CSRF token 防护;跨域 API 要配明确的 CORS(限定来源/方法/头/凭证)。
详细版
CSRF vs CORS 对比:
| 维度 | CSRF | CORS |
|---|---|---|
| 是什么 | 一种攻击 | 浏览器安全策略 |
| 关注点 | 浏览器自动带 Cookie → 伪造请求 | 脚本能否跨域读响应 |
| 防的方向 | 防伪造写请求 | 管跨域读访问权限 |
| 是不是鉴权 | —— | 不是鉴权,别当访问控制 |
| Spring 防护 | CSRF Token | CorsConfigurationSource |
Spring Security 配置:
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// Cookie/Session 场景:开启 CSRF(默认开启)
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
// 跨域 API:配明确的 CORS
.cors(cors -> cors.configurationSource(corsSource()));
return http.build();
}
CorsConfigurationSource corsSource() {
CorsConfiguration c = new CorsConfiguration();
c.setAllowedOrigins(List.of("https://app.example.com")); // 明确来源,不是 *
c.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
c.setAllowedHeaders(List.of("Authorization", "Content-Type"));
c.setAllowCredentials(true); // 带凭证时,allowedOrigins 不能是 *
var src = new UrlBasedCorsConfigurationSource();
src.registerCorsConfiguration("/**", c);
return src;
}
完整版教学
一、两个问题的出发点完全不同
CSRF 和 CORS 名字都带「跨」,但性质完全不同:
- CSRF 是「攻击类型」:它利用的是浏览器自动携带 Cookie的特性,让用户在不知情下发出恶意请求。它是需要防御的坏东西。
- CORS 是「浏览器安全策略」:它决定一个源的 JavaScript 能不能读取另一个源的响应。它是浏览器的一种访问控制机制。
一个是「防伪造请求」,一个是「管跨域读取权限」——出发点根本不同,不能混为一谈,更不能用一个替代另一个。
| 维度 | CSRF | CORS |
|---|---|---|
| 性质 | 攻击方式 | 浏览器安全策略 |
| 核心原因 | 浏览器自动带 Cookie | 浏览器限制跨源脚本读响应 |
| 防护目标 | 防伪造写请求 | 管控哪些源能读响应 |
| 是否鉴权 | 不是 | 也不是 |
CSRF 看“请求是不是被伪造”,CORS 看“浏览器脚本能不能读响应”,这两个问题不在同一层。
二、CSRF 为什么危险
经典场景:
- 用户登录了银行网站
bank.com,浏览器保存了bank.com的登录 Cookie。 - 用户没退出,又访问了攻击者的恶意页面
evil.com。 evil.com的页面里藏了一个自动提交的表单/请求,指向bank.com/transfer?to=攻击者&amount=10000。- 浏览器发这个请求时,会自动带上
bank.com的 Cookie(浏览器对同一域名的请求都自动带 Cookie)。 - 如果
bank.com只认 Cookie(看到有效登录 Cookie 就执行),就会把这个伪造请求当成用户本人的转账操作执行。
危险的根源:浏览器自动带 Cookie + 服务端只凭 Cookie 认定身份。用户「被代表」发起了他不知道的操作。
用户已登录 bank.com
-> 访问 evil.com
-> evil.com 诱导浏览器请求 bank.com/transfer
-> 浏览器自动带 bank.com Cookie
-> 服务端若只认 Cookie,就可能执行伪造写操作
三、CSRF Token 的原理
CSRF Token 防护的思路:让写操作除了 Cookie,还必须带一个「攻击者拿不到的随机 token」。
- 服务端给合法页面/客户端一个随机的、攻击者猜不到的 CSRF token。
- 写操作(POST/PUT/DELETE)必须带上这个 token,服务端校验 token 才执行。
- 恶意站点虽然能让浏览器自动带 Cookie 发请求,但它读不到目标站点页面里的 token(受同源策略限制,
evil.com无法读取bank.com页面内容),所以构造不出合法的写请求。
关键在于:Cookie 是自动带的(攻击者能利用),但 token 需要主动读取页面才能拿到(攻击者拿不到)——用「必须主动获取的凭证」来区分「真实用户」和「伪造请求」。
四、CORS 控制的是什么
CORS 管的是「跨域读响应」:浏览器在跨域请求时,会根据服务端返回的 CORS 响应头(Access-Control-Allow-Origin 等)决定要不要把响应交给发起请求的前端脚本。
- 对非简单请求(带自定义头、PUT/DELETE 等),浏览器会先发 OPTIONS 预检,检查服务端允许的方法和头。
- 服务端允许 → 浏览器放行;不允许 → 浏览器拦截响应(请求可能已到达服务端,但脚本读不到响应)。
CORS 配错的两种后果:配太严 → 合法前端调不通(跨域被拦);配太松(如允许所有来源还带凭证)→ 把敏感 API 暴露给不该信任的来源。
五、带凭证跨域要格外谨慎
如果要允许跨域请求携带 Cookie(allowCredentials=true),有一条硬规则:
allowedOrigins不能是*(通配所有来源)——必须明确列出可信的具体域名。
原因:如果既允许所有来源、又允许带凭证,那么任何网站都能带着用户的 Cookie 跨域访问你的 API 并读取响应——等于把用户的登录态暴露给全世界。所以带凭证时,来源必须是白名单。CORS 配置应尽量窄,按环境区分开发域名和生产域名(别图省事全放开)。
六、Spring Security 中怎么配
根据认证机制选择 CSRF 策略:
- 浏览器表单 + Session(Cookie) → 应保留 CSRF 防护(Spring Security 默认开启)。因为 Cookie 会被浏览器自动带,存在 CSRF 风险。
- 纯 Bearer Token API(JWT 放在 Authorization 头) → 可以按风险关闭 CSRF。因为 Token 不是浏览器自动携带的(要 JS 主动放进 Authorization 头),攻击者的恶意页面无法自动带上你的 Token,CSRF 风险形态大大降低。但仍要防 XSS 和 Token 泄露(XSS 能偷 Token)。
CORS 应在 Security 链中启用(http.cors(...)),并提供统一的 CorsConfigurationSource,明确配置允许的来源、方法、头、凭证。
七、常见误区与追问
- 误区:CORS 是认证授权。 CORS 只管浏览器是否把跨域响应交给脚本,允许跨域不代表业务上允许访问。
- 误区:配置了 CORS 就能防 CSRF。 CORS 解决跨域读响应问题,CSRF 利用的是浏览器自动带 Cookie 发写请求,二者不能互相替代。
- 误区:关闭 CSRF 一定不安全。 如果是纯 Bearer Token 且 Token 不会被浏览器自动携带,CSRF 风险形态会降低,但仍要防 XSS 和 Token 泄露。
- 追问:为什么 CSRF Token 能挡住伪造请求? 恶意站点能诱导浏览器带 Cookie 发请求,但读不到目标站点页面里的随机 token。
- 追问:带凭证 CORS 为什么不能用
*? 允许所有来源且允许 Cookie 会让任意站点读取带用户凭证的响应,来源必须收窄到可信白名单。 - 追问:Postman 不受 CORS 限制说明什么? CORS 是浏览器执行的策略,不是服务端鉴权边界,后端 API 仍必须做认证授权。
面试时要能把威胁模型说清楚:CSRF 风险来自「浏览器自动带凭证」,所以看你的认证是否依赖这种自动携带的凭证。
八、加强记忆
CSRF(跨站请求伪造)是一种攻击——利用浏览器自动带 Cookie,诱导已登录用户的浏览器发伪造写请求(服务端只认 Cookie 就中招);防护用 CSRF Token(写操作必须带攻击者拿不到的随机 token——恶意站能带 Cookie 但读不到目标页面的 token)。CORS(跨源资源共享)是浏览器安全策略——控制脚本能否跨域读响应(靠 Access-Control-Allow-* 响应头 + OPTIONS 预检),不是鉴权、别当访问控制(非浏览器客户端不受它约束)。Spring Security:Cookie/Session 写请求保留 CSRF;纯 Bearer Token API 可关 CSRF(Token 非自动携带,但仍防 XSS);CORS 用 CorsConfigurationSource 配明确来源,带凭证(allowCredentials)时 allowedOrigins 不能是 *。核心:CSRF 防伪造写、CORS 管跨域读,威胁模型看认证是否依赖浏览器自动带的凭证。