跨站请求伪造怎么防护?深度解析与实操指南
📖 目录导读
- 什么是跨站请求伪造(CSRF)?
核心原理与攻击流程

- CSRF攻击的危害有多大?
真实场景案例
- 为什么你的网站容易被CSRF攻击?
技术漏洞分析
- 主流防护方案详解
Token机制、Referer验证、SameSite Cookie、自定义请求头
- 代码实战:如何实现CSRF防护?
前后端合作示例(Python/JavaScript)
- 常见问题问答
开发者最关心的10个问题
- 总结与最佳实践
什么是跨站请求伪造(CSRF)?
跨站请求伪造(Cross-Site Request Forgery,简称CSRF)是一种利用用户已登录身份,在用户不知情的情况下,向目标网站发起恶意请求的攻击方式。
攻击流程图解
- 用户登录A银行网站,获得有效Cookie
- 用户未退出,访问了攻击者精心构造的恶意页面B
- 页面B自动请求A银行的转账接口(如
bank.com/transfer?to=hacker&amount=10000) - 由于浏览器自动携带Cookie,服务器认为该请求合法,转账成功
核心要点:CSRF攻击不窃取数据,而是伪造操作(如修改密码、转账、发帖等)。
CSRF攻击的危害有多大?
根据2024年OWASP Top 10报告,CSRF仍属于关键安全风险,常见危害包括:
- 金融损失:自动转账、修改支付账户
- 账户劫持:修改邮箱、密码重置
- 权限滥用:管理员账户被CURD操作
- 数据污染:伪造评论、投票、点赞
真实案例
2018年某国内社交平台因CSRF漏洞,导致数百万用户被强迫自动关注黑产账号,造成大量虚假流量。
为什么你的网站容易被CSRF攻击?
根本原因在于 HTTP协议的无状态性和浏览器的同源策略不完善:
- 浏览器在发送请求时会自动携带Cookie(包括Session Cookie)
- 服务器无法区分请求是用户主动发起,还是恶意页面自动触发
- 许多开发者过度依赖Cookie验证,忽略了请求来源校验
主流防护方案详解
1 同步Token模式(最推荐)
原理:服务器生成一个随机Token,嵌入到表单或请求头中,提交时校验。
实现方式:
# 服务端生成Token
csrf_token = secrets.token_hex(16)
session['csrf_token'] = csrf_token
# 返回给前端
return render_template('form.html', token=csrf_token)
前端提交:
<form action="/submit" method="POST">
<input type="hidden" name="_csrf_token" value="{{ token }}">
<button>提交</button>
</form>
优点:防御效果好,几乎所有框架都支持
缺点:需要维护Token状态,对多页/单页应用略有差异
2 同源检测(Referer/Origin检查)
原理:检查HTTP请求头中的Referer或Origin字段是否来自可信来源。
def check_referer(request):
allowed_domains = ['example.com', 'sub.example.com']
ref = request.headers.get('Referer', '')
if not ref:
return False
from urllib.parse import urlparse
domain = urlparse(ref).netloc
return domain in allowed_domains
注意:Referer可能被浏览器禁用或伪造,因此建议作为辅助方案。
3 SameSite Cookie属性
原理:设置Cookie的SameSite属性,限制第三方站点发送Cookie。
Set-Cookie: session=abc; SameSite=Strict; HttpOnly; Secure
三种模式:
Strict:完全禁止第三方请求携带CookieLax:允许GET等安全请求携带(默认值)None:允许所有第三方请求(需配合Secure)
适用场景:现代浏览器(Chrome 80+、Firefox 60+)默认Lax模式,可有效防御大部分CSRF攻击。
4 自定义请求头验证
原理:要求所有敏感请求都必须包含自定义HTTP头(如X-Requested-With: XMLHttpRequest)。
实现:
- 前端使用AJAX/ fetch必须设置该头
- 服务端校验该头是否存在
优点:避免Token存储问题
缺点:仅适用于API请求,且需要确保前端规范
代码实战:前后端协作防护
Flask + Vue 完整示例
后端(Python/Flask):
from flask import Flask, session, request, jsonify
import secrets
app = Flask(__name__)
app.secret_key = 'your-secret-key'
# 生成Token
@app.route('/api/get-csrf-token')
def get_csrf_token():
token = secrets.token_hex(32)
session['csrf_token'] = token
return jsonify({'csrf_token': token})
# 受保护接口
@app.route('/api/transfer', methods=['POST'])
def transfer():
# 获取请求头的Token
received_token = request.headers.get('X-CSRF-Token')
if not received_token or received_token != session.get('csrf_token'):
return jsonify({'error': 'CSRF验证失败'}), 403
# 业务逻辑...
return jsonify({'success': True})
前端(Vue.js + axios):
// 初始化时获取Token
const csrfToken = await axios.get('/api/get-csrf-token')
// 每次请求携带Token
axios.defaults.headers.common['X-CSRF-Token'] = csrfToken.data.csrf_token
// 发送敏感请求
axios.post('/api/transfer', { amount: 1000, to: 'friend' })
常见问题问答
Q1:CSRF和XSS有什么区别?
A:CSRF是伪造请求,利用用户的身份;XSS是注入脚本,直接窃取数据或执行操作,两者经常组合出现,但防护方法不同。
Q2:RESTful API如何防护CSRF?
A:推荐使用 Token放在请求头 + SameSite Cookie 组合方案,对于移动端,还需加入idempotency key(幂等键)。
Q3:SameSite=Strict会不会影响用户体验?
A:会,例如用户从博客点击链接跳转至支付页面,Strict模式下不会携带Cookie,导致需要重新登录,建议使用 Lax模式 平衡安全与体验。
Q4:WebSocket需要防CSRF吗?
A:需要!WebSocket握手时会发送Cookie,建议在握手请求中携带Token验证,或者使用Origin检查。
Q5:Token放在URL参数里安全吗?
A:不安全,URL会记录在浏览器历史、服务器日志中,容易泄露,务必放在请求体或请求头中。
Q6:如何防止Token被暴力破解?
A:使用cryptographically secure伪随机数生成器(如Python的secrets模块),长度建议32字节以上,并定期更换。
Q7:多服务架构下如何共享CSRF Token?
A:使用集中式Token存储(如Redis),所有微服务统一从Token服务验证。
Q8:CSRF和CORS有关系吗?
A:CORS控制跨域读取,CSRF攻击的是跨域写操作,即便CORS配置严格,依然可能遭受CSRF攻击。
Q9:GraphQL如何防护CSRF?
A:在GraphQL请求头中加入Token,同时使用查询白名单和成本分析限制恶意操作。
Q10:移动端APP需要防CSRF吗?
A:如果APP使用的是原生的HTTP客户端(如OkHttp),盗用Cookie的概率较低,但如使用WebView内嵌网页,则需完全遵循Web防护方案。
总结与最佳实践
核心防护层次
- 第一层:使用 SameSite=Lax 或 Strict 的Cookie策略(强制启用)
- 第二层:对所有写操作(POST、PUT、DELETE)添加 CSRF Token
- 第三层:验证 Referer/Origin 作为补充
- 终极保险:关键操作(转账、修改密码)要求输入 二次验证码(如短信验证码)
开发者自检清单
- [ ] 是否所有状态改变请求都使用Token或自定义头?
- [ ] Cookie是否设置了
HttpOnly、SameSite、Secure? - [ ] 是否对第三方引用资源(图片、付费API)进行了来源限制?
- [ ] 是否记录了CSRF攻击日志并触发告警?
技术选型建议
| 框架 | 推荐防护方式 |
|---|---|
| Django | 内置{% csrf_token %}模板标签 |
| Laravel | @csrf 指令与 VerifyCsrfToken 中间件 |
| Spring Boot | csrf().enable() 与 RequestDataValueProcessor |
| Express | csurf 中间件 |
| Nuxt/Next.js | Nuxt Security Module / csurf 插件 |
最后提醒:CSRF防护不是一次性配置,而是一个持续改进的过程,建议定期进行安全审计,使用自动化工具(如OWASP ZAP)扫描漏洞,并结合业务场景动态调整防护策略。
一句话总结:让每个敏感请求都带上服务器知晓的“暗号”,而不是仅依靠浏览器自动带来的“身份证”。
综合OWASP官方文档、MDN Web文档及主流框架安全实践,经去伪存精整理而成。*