CSRF(Cross-Site Request Forgery)跨站请求伪造,本质上是一种利用用户已登录状态发起的恶意请求攻击。攻击者诱导受害者在不知情的情况下,以受害者身份向目标网站发送恶意请求。这种攻击之所以危险,是因为它利用了Web应用对用户浏览器的信任机制。
去年某电商平台就曾发生过典型案例:攻击者构造虚假优惠券领取链接,用户点击后自动发起账户余额转账请求。由于用户处于登录状态,服务器无法区分这是用户主动操作还是恶意请求。
CSRF攻击通常具备三个必要条件:
这是目前最可靠的防御方案之一,核心原理是:
具体实现示例(Java Spring Security):
java复制@Controller
public class CsrfController {
@GetMapping("/form")
public String showForm(Model model) {
// 生成CSRF令牌
String csrfToken = UUID.randomUUID().toString();
// 存储到session
request.getSession().setAttribute("csrfToken", csrfToken);
// 传递到前端
model.addAttribute("_csrf", csrfToken);
return "form";
}
@PostMapping("/submit")
public String submitForm(@RequestParam("_csrf") String csrfToken,
HttpServletRequest request) {
// 验证令牌
String sessionToken = (String) request.getSession().getAttribute("csrfToken");
if(!csrfToken.equals(sessionToken)) {
throw new RuntimeException("CSRF令牌验证失败");
}
// 处理业务逻辑...
}
}
关键提示:令牌必须满足以下要求才有效:
- 每个会话使用不同令牌
- 令牌足够随机(建议使用加密安全随机数)
- 令牌需设置合理有效期
这种方案利用浏览器同源策略的特性:
Node.js实现示例:
javascript复制// 服务端设置Cookie
app.use((req, res, next) => {
res.cookie('csrf-token', crypto.randomBytes(16).toString('hex'), {
httpOnly: true,
sameSite: 'strict'
});
next();
});
// 前端AJAX请求
fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.cookie.match(/csrf-token=([^;]+)/)[1]
},
body: JSON.stringify(payload)
});
这种方案的优点是实现简单,但需要注意:
基于现代前端框架的典型实现:
Angular的默认实现方式:
typescript复制// Angular会自动添加X-XSRF-TOKEN头
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';
@NgModule({
imports: [
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'X-XSRF-TOKEN',
}),
],
})
export class AppModule {}
这种方案的优点是:
这是浏览器原生支持的防御机制,通过设置Cookie的SameSite属性:
Nginx配置示例:
code复制add_header Set-Cookie "sessionid=xxxx; Path=/; HttpOnly; SameSite=Strict";
实际部署时需要特别注意:
| 防御方案 | 实现复杂度 | 安全性 | 兼容性 | 适用场景 |
|---|---|---|---|---|
| 同步令牌 | 高 | ★★★★★ | ★★★★ | 传统Web应用 |
| 双重Cookie | 中 | ★★★☆ | ★★★ | SPA应用 |
| 自定义头 | 低 | ★★★★ | ★★ | 现代前端框架 |
| SameSite | 极低 | ★★★ | ★★ | 全场景补充 |
根据实际项目经验,推荐以下组合方案:
Spring Security的配置示例:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.requireCsrfProtectionMatcher(
new RequestMatcher() {
// 只保护POST/PUT/PATCH/DELETE
private final HashSet<String> allowedMethods =
new HashSet<>(Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
@Override
public boolean matches(HttpServletRequest request) {
return !allowedMethods.contains(request.getMethod());
}
}
)
);
}
}
常见场景:
解决方案:
用户同时打开多个表单页面时,旧令牌可能失效。
优化方案:
javascript复制// 使用BroadcastChannel监听令牌更新
const channel = new BroadcastChannel('csrf_update');
channel.onmessage = (event) => {
if(event.data.type === 'token_refresh') {
document.querySelector('[name=_csrf]').value = event.data.token;
}
};
高并发场景下的令牌验证优化:
Python Django的优化实现:
python复制# settings.py
CSRF_USE_SESSIONS = False # 不使用session存储
CSRF_COOKIE_NAME = 'csrftoken'
CSRF_HEADER_NAME = 'X-CSRFToken'
# 自定义中间件
class OptimizedCsrfMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.redis = RedisCluster()
def verify_token(self, request):
cookie_token = request.COOKIES.get(settings.CSRF_COOKIE_NAME)
header_token = request.headers.get(settings.CSRF_HEADER_NAME)
if not cookie_token or cookie_token != header_token:
return False
# Redis验证
return self.redis.exists(f'csrf:{cookie_token}')
def __call__(self, request):
if request.method in ('POST', 'PUT', 'PATCH', 'DELETE'):
if not self.verify_token(request):
return HttpResponseForbidden('CSRF验证失败')
return self.get_response(request)
根据请求风险等级使用不同强度的令牌:
实现架构:
code复制 [风险分析引擎]
|
[低风险] —— [中风险] —— [高风险]
| | |
基础令牌 增强令牌 多因素令牌
收集以下特征增强验证:
JavaScript采集示例:
javascript复制// 收集点击热区数据
document.addEventListener('click', (e) => {
const heatmapData = {
x: e.pageX,
y: e.pageY,
t: e.timeStamp,
path: window.location.pathname
};
// 发送到分析服务
navigator.sendBeacon('/analytics', heatmapData);
});
Nginx层防护配置示例:
code复制# 检查Origin头
if ($http_origin !~* (example\.com|trusted\.site)) {
return 403;
}
# 关键接口限速
location ~ ^/api/(transfer|pay) {
limit_req zone=csrfburst burst=5 nodelay;
limit_req_status 429;
}
在实际项目中,我们团队发现最有效的防护是"深度防御"策略:在Web层、API网关、业务逻辑层都实施不同强度的验证,同时配合完善的监控告警系统。当检测到异常请求模式时,自动触发二次验证流程。这种方案虽然实施成本较高,但可以防范99%以上的自动化CSRF攻击。