CSRF校验及CBV加装装饰器

CSRF校验及CBV加装装饰器

csrf跨站请求伪造

我们的网站在提交表单时,是通过post请求朝一个网站提交信息,这个网站的服务端可以通过request.POST拿到的目标数据,那么就存在这么一种可能,如果有投机分子仿造了一个一模一样的前端钓鱼网站,且表单也通过POST标签提交给服务端,但是将表单标签的关键信息被它在前端更改了,可能会导致我们服务端处理这份被篡改的表单数据,如转账人等关键信息被更改,这样用户造成用户的财产损失。

image

所以我们需要一个措施来判别浏览器发送的POST请求数据是通过我提供的网页发送的,而不是通过钓鱼网站发送的。

csrf校验策略

服务端会先收到浏览器的get请求,返回给用户一个待填写的表单界面,我们在给用户这个界面时,为它打上一个唯一标识,这个标识到时候让用户提交表单时顺带传回,我们再将这个标识进行比对,每个标识用完一次就不能用了,这样没有对应唯一标识的钓鱼网站提交的表单就会被拒之门外了。

image

具体这个唯一标识怎么加,实际上django也为我们封装好了:

  • form表单内部添加(针对form表单的post请求)

    {% csrf_token %}
    
  • ajax请求csrf策略

    // 方式1:自己动手取值 较为繁琐
    data:{'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val(), 其他数据}
    // 方式2:通过获取返回的cookie中的字符串 放置在请求头中发送
    headers: {"X-CSRFToken": $.cookie('csrftoken')}
    // 方式3:直接引入一个js脚本即可(官网提供的)
    function csrfSafeMethod(method) {
      // these HTTP methods do not require CSRF protection
      return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    
    $.ajaxSetup({
      beforeSend: function (xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
          xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
      }
    });
    /*将上述js胆码配置到一个静态文件中,如static/js/csrf.js
    那么在用时只要导入这个文件,那么所有ajax请求都会自动携带csrf标识
    */
    {% load static %}
    <script src="{% static 'js/csrf.js' %}"></script>
    

经过以上的设置后,网页发送的form表单和ajax的post请求就相对很安全了,用到的就是csrftoken的技术。

csrf校验流程

csrf校验中间件

csrf是一个唯一标识,只有在进行第二次请求的时候才会去校验(如get请求就不会校验),它去校验的位置实际上就是我们之前经常注释掉的第四个中间件:'django.middleware.csrf.CsrfViewMiddleware'

它的逻辑是,当进来一个post请求(也有可能是除get外的其他请求)时,会对其进行csrfmiddlewaretoken的校验,如果有这么一个信息属性,那么比对这个信息是否有效,如果没有或者信息不对直接拒绝,这也就是前期在学习提交post表单请求时为什么会被403权限拦截的原因。

所以当我们不需要csrf校验时,只要注释掉这个中间件就可以让它跳过这层校验逻辑。

csrf校验装饰器

有时我们的csrf校验并不想全局设置,那么我们就可以给视图函数加装csrf装饰器让其取消或者添加csrf校验:

from django.views.decorators.csrf import csrf_exempt, csrf_protect

@csrf_exempt  # csrf全局设置时取消这个视图函数的csrf校验
def gogogo(request):pass

@csrf_protect  # csrf全局取消时这个视图函数单独添加csrf校验
def transfer(request):pass

CBV添加装饰器的方法

对于视图函数我们可以直接添加装饰器,但是对于视图类,我们不能直接简单的在类体函数上面直接加装,需要借助模块method_decorator

  1. 方式一:单独生效

    加装到视图类的某个具体的方法上,采取@method_decorator(装饰器名)

    from django.views.decorators.csrf import csrf_protect
    from django.utils.decorators import method_decorator  # 视图类加装饰器必须借助这个模块
    
    class MyView(views.View):
        def get(request):pass
    	
        @method_decorator(csrf_protect)  # post添加csrf校验
    	def post(request):pass    
    
  2. 方式二:也是单独生效

    加装视图类上,指定name属性,对应的是类方法名:@method_decorator(装饰器名, name='post')

    @method_decorator(csrf_protect, name='post')  # post添加csrf校验
    class MyView(views.View):
        def get(request):pass
    	
    	def post(request):pass    
    
  3. 方式三:类整体生效

    将分发函数dispatch继承下来,因为执行视图类的所有函数都会经过这个dispatch,我们只需要在这个函数上加装装饰器就可以让每种请求都加装装饰器。

    class MyView(views.View):
        @method_decorator(login_auth)  # 这个类的所有方法都加登录认证
        def dispatch(request):
            return super().dispatch(request, *args, **kwargs)
        
        def get(request):pass
    	
    	def post(request):pass    
    

这些加装饰器的方式的规则对所有的视图类都有效,但是有一个特例装饰器:csrf_exempt

from django.views.decorators.csrf import csrf_exempt

class MyView(views.View):
    @method_decorator(csrf_exempt)  # csrf_exempt只能通过第三种方式全类取消csrf校验
    def dispatch(request):
        return super().dispatch(request, *args, **kwargs)
posted @ 2022-12-23 16:41  leethon  阅读(50)  评论(0编辑  收藏  举报