本文目录导读:

在防御CSRF攻击时,优先推荐使用 CSRF Token,而不是单纯依赖 Referer。
简要结论:Token 是主流、标准、可靠的防御方式;Referer 仅能作为辅助或降级方案,不可单独依赖。
以下是两者的详细对比分析:
为什么推荐使用 Token?
- 安全性与唯一性:Token 是服务器生成的、与用户会话绑定的随机字符串,攻击者无法提前获知,每次提交请求时,服务器验证 Token 是否匹配,从根本上防止了第三方网站构造合法请求。
- 不受环境限制:Token 在浏览器中通过隐藏字段或自定义 HTTP 头传递,不依赖请求头信息。
- 标准实践:主流框架(如 Spring Security、Django、Express.js)都内置了 CSRF Token 支持,实现简单且经过安全验证。
为什么不推荐单独依赖 Referer?
- Referer 可被篡改或缺失:
- 用户访问 HTTPS 页面后,再通过 HTTP 发起请求时,某些浏览器会不发送 Referer 头。
- 部分浏览器插件或安全软件可能会移除 Referer 头。
- 攻击者可以通过修改
<form>的enctype或使用 Flash 等插件方式,在某些情况下伪造 Referer。 - iframe、跨域请求、直接输入地址或书签访问也可能导致 Referer 为空。
- 绕过风险:虽然 Referer 可以用于同源检查,但一旦攻击者发现某个你信任的域名存在 XSS 漏洞,可以利用该域名发起 CSRF 攻击,Referer 检测会被绕过。
- 可靠性不足:Referer 是客户端携带的信息,不应该作为唯一的安全决策依据。
实际中如何选择?
| 场景 | 推荐方案 |
|---|---|
| 高安全性需求(如支付、登录、后台管理) | 仅使用 Token,并可配合 SameSite Cookie |
| API 接口或非浏览器客户端 | Token(通过请求头传递) |
| 传统、低风险场景(如简单表单) | Token 为主,可额外加入 Referer 检查作为纵深防御 |
| 无法实现 Token 的旧系统 | 必须加上 Referer 检查,并配合其他措施(如二次验证) |
最佳实践:组合防御
现代 Web 安全推荐深度防御:
- 首选:CSRF Token(在表单中加入隐藏字段或通过
X-CSRF-Token请求头发送)。 - 辅助:SameSite Cookie 属性(设置为
Strict或Lax,能有效阻止第三方网站发起的跨站请求)。 - 环境补充:Referer/Origin 头检查(作为第二层验证,拦截明显异常的请求)。
- 重要操作:二次确认(如支付时要求输入密码或验证码)。
总结对比表
| 特性 | CSRF Token | Referer/Origin |
|---|---|---|
| 安全性 | 高(与用户会话绑定,不可预测) | 低(可缺失、可伪造) |
| 实现复杂度 | 中等(需服务器存储和验证) | 低(仅校验请求头) |
| 适用场景 | 所有类型请求(GET/POST/API) | 仅限依赖浏览器环境的表单请求 |
| 兼容性 | 所有浏览器正常 | HTTPS 到 HTTP 可能丢失 |
| 推荐程度 | ★★☆☆☆(仅作辅助) |
实际代码示例(伪代码)
Token 防御:
# 服务端生成 Token 并注入表单
<form method="POST" action="/transfer">
<input type="hidden" name="csrf_token" value="{{ token }}">
<input type="text" name="amount">
</form>
# 服务端接收时验证
if request.form['csrf_token'] != session['csrf_token']:
raise "CSRF Token 无效"
Referer 辅助检查:
def check_referer(request):
referer = request.headers.get('Referer', '')
# 检查是否以合法域名开头(只能作为辅助,不能百分百依赖)
if not referer.startswith('https://yourdomain.com/'):
# 可以记录日志或要求用户二次验证,但不应直接拒绝所有流量
pass
最终建议: 只要你的项目条件允许,请一定使用 CSRF Token,如果旧的代码或框架无法支持 Token 生成,再考虑使用 Referer 作为临时应急方案,同时尽快升级替换。