Django CSRF认证的几种解决方案
浏览器在发送请求的时候,会自动带上当前域名对应的cookie内容,发送给服务端,不管这个请求是来源A网站还是其它网站,只要请求的是A网站的链接,就会带上A网站的cookie。浏览器的同源策略并不能阻止CSRF攻击,因为浏览器不会停止js发送请求到服务端,只是在必要的时候拦截了响应的内容。或者说浏览器收到响应之前它不知道该不该拒绝。
用户登陆A网站后,攻击者自己开发一个B网站,这个网站会通过js请求A网站,比如用户点击了某个按钮,就触发了js的执行。
防止攻击
-
Double Submit Cookie
攻击者是利用cookie随着http请求发送的特性来攻击。但攻击者不知道 cookie里面是什么。
Django中是在表单中加一个隐藏的 csrfmiddlewaretoken,在提交表单的时候,会有 cookie 中的内容做比对,一致则认为正常,不一致则认为是攻击。由于每个用户的 token 不一样,B网站上的js代码无法猜出token内容,对比必然失败,所以可以起到防范作用。
-
Synchronizer Token
和上面的类似,但不使用 cookie,服务端的数据库中保存一个 session_csrftoken,表单提交后,将表单中的 token 和 session 中的对比,如果不一致则是攻击。
这个方法实施起来并不困难,但它更安全一些,因为网站即使有 xss 攻击,也不会有泄露token的问题。
Django使用CsrfViewMiddleware中间件进行CSRF校验,默认开启防止csrf(跨站点请求伪造)攻击,在post请求时,没有携带csrf字段,导致校验失败,报403错误。那么我们如何解决这种403错误呢?
<form enctype="multipart/form-data" method="post" action="{% url 'add_data' %}"> {% csrf_token %} </form>
可以只针对指定的路由去掉CSRF校验,这也分为两种情况:
# 导入,可以使此次请求忽略csrf校验 from django.views.decorators.csrf import csrf_exempt # 在处理函数加此装饰器即可 @csrf_exempt def add_data(request): result = {} # TODO return HttpResponse(result)
2.CBV:以类实现路由处理
from django.views import View from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator class IndexView(View): @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs): return render(request, 'home.html') def post(self, request, *args, **kwargs): data = request.POST.get('data') qr_path = gen_qrcode(data) return HttpResponse(qr_path)
或者用下面的方式,把装饰器放在类外面
from django.views import View from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator @method_decorator(csrf_exempt, name='dispatch') class IndexView(View): def get(self, request, *args, **kwargs): return render(request, 'home.html') def post(self, request, *args, **kwargs): data = request.POST.get('data') qr_path = gen_qrcode(data) return HttpResponse(qr_path)
4. 为所有请求添加csrf校验数据(推荐)
以上方式都有限制,适用范围比较窄,我们需要一种可以一劳永逸的方式:让所有请求都携带csrf数据。因为我们是使用Django模板渲染前端页面的,所以一般会先定义一个base.html,其他页面通过{% extends "base.html" %}来引入使用,那么在base.html中添加ajax的全局钩子,在请求时添加csrf数据即可。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{% block title %}首页{% endblock %}</title> <link rel="stylesheet" href="{% static 'css/base.css'%}"> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <script> $.ajaxSetup({ data: { csrfmiddlewaretoken: '{{ csrf_token }}' } }) </script> {% block css %} {% endblock %} </head> <body>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现