在Python项目中处理CSRF token,最直接的方式是依赖框架内置保护,若需手动操作,则通过Cookie获取token并携带在请求中。
理解CSRF Token在Python中的角色
CSRF攻击利用用户已认证的身份发起非预期请求,Python Web框架通过生成随机token并验证其一致性来防御,据OWASP安全指南,CSRF依然位列常见Web风险,开发者需重视,Django将token存储在Cookie中,同时要求表单或请求包含该token;Flask通过Session存储,依赖扩展验证,理解这一底层逻辑,是正确使用token的前提。
Python中获取csrftoken的三种方法
从Django内置方法获取
在Django中,获取CSRF token最简便的方式是利用模板标签{% csrf_token %},它会渲染一个隐藏输入字段,若需在AJAX或API中获取,可通过django.middleware.csrf.get_token(request),该函数返回当前请求的token,如果不存在则生成新token并设置Cookie,示例代码:
from django.http import JsonResponse
from django.middleware.csrf import get_token
def csrf_token_view(request):
return JsonResponse({'csrf_token': get_token(request)})
前端在后续请求中,从Cookie读取csrftoken,或通过响应对象获取,然后设置到请求头X-CSRFToken,Django官方文档建议,每次页面加载都应刷新token,但实际中token可复用直到会话结束,对于需要排除CSRF保护的视图,可使用@csrf_exempt装饰器;若希望确保Cookie被设置,使用@ensure_csrf_cookie,在AJAX场景中,若Cookie的HttpOnly属性被开启,JavaScript将无法读取,此时需从响应体或模板变量中传递token。
从Flask扩展获取
Flask本身不内置CSRF保护,但Flask-WTF提供了便捷的集成,在模板中,{{ csrf_token() }}生成一个隐藏字段,其值来自Session,若需手动获取,可访问session['csrf_token'],但需注意Flask-WTF在生成表单时自动设置,对于API,Flask-WTF支持通过X-CSRFToken头验证,配置示例:
from flask_wtf.csrf import CSRFProtect csrf = CSRFProtect(app)
之后,所有POST请求都需要携带CSRF token,否则返回400错误,开发者可以自定义错误处理,但建议保持默认行为,对于前后端分离项目,可手动调用generate_csrf()生成token,并通过接口返回,前端在后续请求中携带该token,Flask-WTF还支持通过exempt列表忽略特定路由,适用于Webhook等场景。
通过requests库手动获取
在爬虫或测试中,需要手动模拟CSRF token,流程如下:
- 使用
requests.Session发起GET请求,获取目标页面。 - 从响应Cookie中提取
csrftoken(Django)或csrf_token(Flask)。 - 在后续POST请求中,将token作为表单字段
csrfmiddlewaretoken或请求头X-CSRFToken携带。 - 注意部分网站会更新token,每次请求后需重新获取。
示例:
import requests
s = requests.Session()
# 步骤1:获取token
login_page = s.get('http://example.com/login/')
token = s.cookies.get('csrftoken')
# 步骤2:登录
login_data = {'username': 'user', 'password': 'pass', 'csrfmiddlewaretoken': token}
response = s.post('http://example.com/login/', data=login_data)
# 后续请求自动携带Cookie,但token若变化需更新
业内专家指出,手动处理token时,务必重用登录后的Session对象,否则Cookie不连续导致验证失败,对于多步表单,token可能随步骤变化,需跟踪每一步的响应Cookie,若目标网站使用Flask-WTF,token存储在Session中,爬虫需维持Session对象,否则验证失败,部分网站会检查Referer头,确保模拟合法来源。
csrftoken在Django与Flask中的验证原理
Django的验证流程
Django的CSRF中间件在每次请求到达时执行以下步骤:
- 检查请求方法是否为POST、PUT、DELETE等不安全方法。
- 如果是不安全方法,从Cookie中获取
csrftoken值。 - 从请求体(字段
csrfmiddlewaretoken)或请求头X-CSRFToken中获取相同值。 - 比较两者是否一致,如果一致,请求通过;否则返回403。
- 对于GET、HEAD、OPTIONS等安全方法,不验证。
这种机制确保只有持有正确Cookie的用户才能发起写操作,Django之所以同时使用Cookie和请求体双重验证,是为了防止攻击者通过Cookie注入或泄漏绕过防护,Django的CSRF token基于用户会话生成,不同用户拥有不同token,有效防止跨用户伪造。
Flask-WTF的验证流程
Flask-WTF的CSRFProtect扩展同样拦截POST请求,但验证逻辑不同:
- 从Session中读取
csrf_token。 - 从请求体字段
csrf_token或请求头X-CSRFToken中获取提交的值。 - 比较两者,匹配则通过。
- 如果Session中没有token,则自动生成并存储。
由于Session对用户是透明的,Flask-WTF不需要依赖Cookie,而是通过服务端Session存储token,适合无Cookie的API场景,但这也意味着在高并发或分布式部署中,Session存储可能成为瓶颈,推荐使用Redis等外部存储。
对比表格
行业共识认为,Django的Cookie+请求体双重验证更适合传统表单,而Flask的Session存储适用于API和前后端分离项目,下表细化对比:
| 特性 | Django | Flask-WTF |
|---|---|---|
| token存储位置 | Cookie(客户端) | Session(服务端) |
| 验证来源 | Cookie值与请求值比较 | Session值与请求值比较 |
| 默认启用 | 是,所有不安全请求 | 需手动初始化CSRFProtect |
| 性能影响 | 每次请求读取Cookie,开销小 | 每次请求读取Session,可能依赖外部存储(如Redis) |
| 适用场景 | 传统MVC应用 | 微服务或API优先项目 |
| 跨域支持 | 需额外配置,否则可能被同源策略阻断 | 通过Session支持,跨域兼容性更好 |
常见错误与调试技巧
403 Forbidden错误
这是最常见的CSRF问题,可能原因:
- 请求未携带token,检查表单是否包含
{% csrf_token %},或AJAX请求头是否设置了X-CSRFToken。 - Cookie被禁用,Django依赖Cookie,如果浏览器禁用Cookie,需要启用,或改用Session存储。
- Token过期,Django默认token永不过期,但Flask-WTF可设置过期时间,如果遇到,重新加载页面获取新token。
- 在Django中,如果使用了
@csrf_exempt但视图仍要求token,检查中间件顺序。
Token不匹配
在Django中,如果Cookie中的token与请求中的值不一致,会触发该错误,常见于跨域请求或手动构造请求时,解决方案:确保从同一请求的Cookie中获取token,并原样携带,不要从不同页面或不同请求中复制,在Flask-WTF中,如果Session中的token与提交值不匹配,通常是因为Session被清空或跨请求丢失,检查Session配置是否持久化。
在AJAX中如何正确携带token
对于Django,前端需要从Cookie读取csrftoken,并设置到请求头,示例(使用jQuery):
var csrftoken = Cookies.get('csrftoken');
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS)$/.test(settings.type)) {
xhr.setRequestHeader('X-CSRFToken', csrftoken);
}
}
});
对于Flask-WTF,类似,但token从模板变量或响应中获取,注意:如果Cookie的HttpOnly
属性被设置为True,JavaScript无法读取,此时需从后端接口单独返回token,或通过模板变量直接输出到页面中。
性能与安全考虑
使用HTTPS
CSRF token本身不加密,但必须通过HTTPS传输,防止中间人窃取,据OWASP,HTTPS是CSRF防御的基础,在HTTP环境下,token可能被截获,攻击者可利用其发起伪造请求。
Token刷新策略
Django默认不刷新token,但安全敏感应用可以每次请求后刷新,不过这会影响用户体验和缓存,行业实践:对于关键操作(如支付、修改密码),每次生成新token,在Flask-WTF中,可通过设置WTF_CSRF_TIME_LIMIT控制token有效期,过期后需重新获取。
避免Token泄露
CSRF token不应暴露在URL参数中,因为URL可能被记录在日志或Referer头中,始终通过表单字段或自定义请求头传递,在Django中,若使用{% csrf_token %},它自动生成隐藏字段,不会泄露到URL,在Flask-WTF中,同样遵循该原则。
csrftoken python 常见问题解答
如何在Django中获取csrftoken并用于AJAX POST?
在Django中,可以通过get_token(request)获取token,然后通过JSON响应返回给前端,前端在AJAX请求中读取Cookie中的csrftoken,并设置X-CSRFToken头,Django的CsrfViewMiddleware会自动验证此头,如果Cookie被设置为HttpOnly,则需从后端单独获取token,例如在模板中通过{{ csrf_token }}输出,或通过专门接口返回。
Flask-WTF中如何手动生成csrftoken?
在Flask-WTF中,手动生成token可以使用generate_csrf()函数,该函数返回一个随机字符串,并自动存储到Session中,示例:
from flask_wtf.csrf import generate_csrf token = generate_csrf() # 然后在模板或响应中返回
注意,如果使用generate_csrf(),需要确保Session已启用,且后续验证时调用validate_csrf(token),对于API场景,可生成token后返回给前端,前端在后续请求中携带该token。
Python爬虫中csrftoken处理有哪些注意事项?
爬虫处理csrftoken时,需使用统一的Session保存Cookie,并在每次POST前从Cookie中获取最新token,如果目标网站使用Django,注意token可能随着表单生成而更新,但通常登录后token不变,对于Flask-WTF,token存储在Session中,爬虫需要维持Session对象,否则验证失败,部分网站会检查Referer头,确保模拟合法来源,若遇到403错误,可尝试在请求头中添加Referer字段,值与当前页面URL一致。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/506601.html



