springboot~传统WEB应用开启CSRF

CSRF 是什么?

CSRF(Cross-Site Request Forgery,跨站请求伪造) 是一种常见的Web安全漏洞。攻击者利用受害者已经登录的合法会话,诱使受害者执行非本意的操作。

简单比喻:

想象你在咖啡店会员卡里有钱,你每次消费只需要说“用会员卡支付”。攻击者伪装成服务员,在你面前说“用会员卡转账100元到XXX账户”。因为你已经在咖啡店的系统中“登录”了(身份已认证),系统就会执行这个操作。

CSRF攻击原理:

攻击流程:

  1. 用户登录:用户登录正常的网站A(如银行网站),获得登录凭证(Cookie/Session)
  2. 用户访问恶意网站:用户在同一个浏览器中访问了攻击者的网站B
  3. 恶意请求:网站B通过隐藏表单、图片src、AJAX等方式,向网站A发送请求
  4. 自动携带凭证:浏览器会自动携带网站A的Cookie
  5. 网站A执行请求:网站A看到合法Cookie,误以为是用户的自愿操作

攻击示例:

<!-- 恶意网站上的代码 -->
<img src="https://your-bank.com/transfer?to=hacker&amount=10000" width="0" height="0" />

<!-- 或隐藏表单 -->
<form action="https://your-bank.com/change-email" method="POST">
  <input type="hidden" name="email" value="hacker@evil.com">
</form>
<script>document.forms[0].submit();</script>

为什么有时要禁用CSRF保护?

适用禁用场景:

  1. 纯API服务(无浏览器交互)

    # 移动App通过API访问后端
    # 使用Token认证(JWT/OAuth),而不是Session
    Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
    
  2. 仅提供非状态改变的操作

    GET /api/users          # 只读操作,通常不需要CSRF
    POST /api/transfer      # 写操作,需要CSRF
    
  3. 微服务内部通信

    # 服务间调用使用服务凭证,而不是用户会话
    service-to-service: true
    
  4. 某些特殊框架/场景

    // GraphQL通常使用token而不是session
    // 或某些实时通信应用
    

具体框架中的禁用示例:

Spring Security:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()  // 禁用CSRF
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .httpBasic();  // 使用HTTP Basic认证
    }
}

Django:

# settings.py
CSRF_COOKIE_SECURE = False  # 禁用CSRF
# 或针对特定视图
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def api_view(request):
    pass

CSRF保护机制对比:

保护机制 工作原理 适用场景
CSRF Token 服务器生成Token,表单/请求必须携带 传统Web应用
SameSite Cookie Cookie仅在同站请求中发送 现代浏览器支持
双重Cookie验证 客户端读取Cookie并附加到请求 兼容性较好
Referer检查 检查请求来源 简单但不可靠
无(禁用) 不验证 纯API、内部服务

何时应该启用/禁用CSRF?

应该启用CSRF的场景:

  • ✅ 传统的基于Session的Web应用
  • ✅ 用户通过浏览器访问的表单提交
  • ✅ 需要用户交互的操作(转账、修改数据)
  • ✅ 使用Cookie/Session进行身份认证

可以禁用CSRF的场景:

  • ✅ 纯REST API,使用JWT/OAuth Token认证
  • ✅ 仅限移动App访问的后端服务
  • ✅ 微服务间的内部通信
  • ✅ 只读的公共API
  • ✅ 使用其他认证方式(API Key、HMAC签名)

禁用CSRF后的替代安全方案:

# 替代方案示例:

1. JWT Token认证:
   每次请求携带: Authorization: Bearer <token>
   
2. API Key + Secret:
   请求签名: X-Signature: sha256(api_secret + request_data)
   
3. OAuth 2.0:
   使用Access Token进行授权
   
4. CORS限制:
   Access-Control-Allow-Origin: https://trusted-domain.com
   
5. Rate Limiting:
   限制请求频率防止滥用

最佳实践建议:

// 在Spring中,可以针对不同端点配置不同的CSRF策略
public class SecurityConfig {
    protected void configure(HttpSecurity http) {
        http
            // 对Web页面启用CSRF
            .csrf(csrf -> csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
            )
                .requireCsrfProtectionMatcher(
                    new RequestMatcher() {
                        public boolean matches(HttpServletRequest request) {
                            // 仅对特定路径启用CSRF
                            return request.getRequestURI().startsWith("/web/");
                        }
                    })
            .and()
            // API端点使用无状态认证
            .authorizeRequests()
                .antMatchers("/api/**").authenticated()
                .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS); // API无状态
    }
}

验证 CSRF 是否生效

  1. 检查 Cookie:登录后查看浏览器是否有 XSRF-TOKEN Cookie
  2. 测试请求
    • 直接发送 POST 请求应该被拒绝(403 错误)
    • 携带正确的 CSRF Token 的请求应该成功
  3. 检查响应头:某些配置下,响应头会包含 CSRF Token
# 使用 curl 测试
# 1. 先获取 CSRF Token(从登录后的 Cookie 或响应头)
# 2. 发送带 CSRF Token 的请求
curl -X POST http://localhost:8080/api/test \
  -H "Content-Type: application/json" \
  -H "X-XSRF-TOKEN: YOUR_CSRF_TOKEN" \
  -H "Cookie: XSRF-TOKEN=YOUR_CSRF_TOKEN" \
  -d '{"data": "test"}'

总结:

禁用CSRF的前提条件:

  1. 应用不使用Cookie/Session进行身份认证
  2. 请求来源可控(如仅限移动App、内部服务)
  3. 已实施同等或更强的安全措施替代
  4. 确认攻击面不会因此扩大

黄金法则:

如果用户通过浏览器访问你的网站,并且网站使用了Cookie/Session,那么永远不要禁用CSRF保护。只有在完全理解风险并有替代方案时,才考虑为API服务禁用CSRF。

posted @ 2025-12-31 11:22  张占岭  阅读(0)  评论(0)    收藏  举报