全面解析CSRF令牌:配置、使用与最佳实践指南
📑 目录导读
- 什么是CSRF攻击与CSRF令牌?
- CSRF令牌的工作原理
- 后端环境下的CSRF令牌配置(以Django/Spring Boot为例)
- 前端如何正确携带CSRF令牌
- 常见配置误区与问答
- 总结与安全建议
什么是CSRF攻击与CSRF令牌?
问题:CSRF攻击是什么?为什么需要CSRF令牌?

跨站请求伪造(CSRF)是一种常见的Web安全漏洞,攻击者利用用户已登录的合法会话,诱导用户点击恶意链接,从而在用户不知情的情况下执行非预期的操作(如转账、修改密码等),CSRF令牌是一种一次性、不可预测的随机字符串,服务端通过校验该令牌来区分合法请求和伪造请求,从而防御CSRF攻击。
核心要点:任何涉及状态变更的操作(表单提交、AJAX请求等)都应校验CSRF令牌;GET请求通常不需要校验,因为它不应改变服务器状态。
CSRF令牌的工作原理
- 生成令牌:用户访问页面时,服务端生成一个随机的、与用户会话绑定的令牌(通常存储在session或cookie中)。
- 传递令牌:服务端将令牌嵌入到页面表单的隐藏字段中,或通过响应头(如
X-CSRF-Token)下发。 - 提交验证:当用户提交请求时,服务端比较请求中的令牌与session中存储的令牌是否一致,若不一致,拒绝请求。
关键机制:
- 令牌在每次会话中生成(或每次请求刷新),攻击者无法通过第三方页面获取。
- 令牌通常与用户身份、时间戳等关联,防止重放攻击。
后端环境下的CSRF令牌配置
1 在Django中的配置
Django内置了CSRF中间件,默认开启,配置步骤:
- 启用中间件:确保
settings.py中的MIDDLEWARE包含django.middleware.csrf.CsrfViewMiddleware。 - 模板中使用:在表单内添加
{% csrf_token %}- AJAX请求支持:从cookie中获取
csrftoken,并设置请求头: - AJAX请求支持:从cookie中获取
// 从cookie中读取CSRF token
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.startsWith(name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
// 为所有AJAX请求添加X-CSRFToken头
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^GET|HEAD|OPTIONS|TRACE$/i.test(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
2 在Spring Boot中的配置
Spring Security 4.0后默认启用CSRF保护。
- 关闭或保留:默认开启,若使用REST API可全局关闭(但Web页面不建议关闭)。
- 前端配合:从cookie中读取
XSRF-TOKEN,并在请求头中设置X-XSRF-TOKEN。
// Spring Security配置示例
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
注意事项:withHttpOnlyFalse()允许前端JS读取cookie中的token。
前端如何正确携带CSRF令牌
1 表单提交场景
对于传统HTML表单,只需在表单内嵌入隐藏字段:
<form method="POST" action="/transfer">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<!-- 其他表单字段 -->
</form>
2 AJAX/Fetch请求场景
对于异步请求,通常采用请求头方式传递:
// Fetch API示例
fetch('/api/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': tokenValue // 注意头名称需与后端一致
},
body: JSON.stringify({ data: 'value' })
});
核心原则:
- 避免将CSRF令牌放在URL参数中(可能被记录或泄露)。
- 确保令牌来源安全,优先从cookie读取,而非从DOM元素获取(防止XSS盗取)。
常见配置误区与问答
❓ 问:我的网站是前后端分离(SPA),还需要CSRF令牌吗?
答:需要,但推荐使用更现代的SameSite Cookie机制,若你仍然使用session-based认证,必须配置CSRF令牌,若使用JWT且token存储在Header中,则无需单独CSRF令牌(因为JWT本身不在cookie中自动携带)。
❓ 问:为什么我的AJAX请求总是403错误?
答案排查步骤:
- 确认后端是否开启CSRF保护。
- 检查请求头名称是否与后端期望一致(如
X-CSRF-TOKENvsX-CSRFToken)。 - 确认cookie中是否包含正确的token(浏览器开发者工具 → Application → Cookies 查看)。
- 确保token未过期(部分框架的token会随会话刷新而失效)。
❓ 问:CSRF令牌能防范XSS攻击吗?
答:不能,CSRF令牌仅防御跨站请求伪造,不能防御跨站脚本攻击,XSS攻击可窃取令牌本身,因此需结合内容安全策略(CSP)和输入输出转义。
总结与安全建议
- 始终校验:所有非GET、HEAD、OPTIONS请求都应校验CSRF令牌。
- 令牌随机性:使用安全的随机数生成器,令牌长度至少128位。
- 绑定用户会话:令牌应与会话ID或用户身份关联,防止复用。
- 结合SameSite属性:设置Cookie的
SameSite=Lax或Strict作为辅助防御层。 - 警惕CORS配置:不要将
Access-Control-Allow-Origin设为,防止跨域读取令牌。 - 定期更新:大型应用可考虑每次请求后刷新令牌(但需注意用户体验)。
最终核心:CSRF令牌不是唯一安全防线,应结合HTTPS、CSP、输入验证等形成纵深防御,配置时遵从框架官方文档,并充分测试异常场景。
延伸阅读:建议查阅OWASP CSRF预防备忘单,以及各类框架(如Laravel、Express)的官方安全指南。