• Django CSRF认证解决方法


    什么是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错误呢?

    解决方法

    1. 去掉项目的CSRF验证

    注释掉此段代码即可,但是不推荐此方式,将导致我们的网站完全无法防止CSRF攻击。

    2. 前端表单中增加csrf信息

    <form enctype="multipart/form-data" method="post" action="{% url 'add_data' %}">
        {% csrf_token %}
    </form>
    

    一定要注意后端使用render而不要使用render_to_response进行渲染,这样前端就会有csrf_token变量,前端cookies中也会出现csrftoken数据,然后在HTML中使用即可。这种方式只限制在form表单中使用,ajax请求不支持。

    3. 指定请求去掉CSRF校验

    可以只针对指定的路由去掉CSRF校验,这也分为两种情况:

    • FBV:以函数实现路由处理
    # 导入,可以使此次请求忽略csrf校验
    from django.views.decorators.csrf import csrf_exempt
    
    # 在处理函数加此装饰器即可
    @csrf_exempt
    def add_data(request):
        result = {}
        # TODO
    
        return HttpResponse(result)
    • 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>

    4. 在ajax请求中在beforeSend中加入csrf校验数据

    $('#get_tencentcloud_instances').click(function () {
                    $.ajax({
                        url: "{{ request_url }}",
                        type: "POST",
                        data: $('#form_data_edit').serialize(),
                        beforeSend: function (xhr,settings) {
                            xhr.setRequestHeader("X-CSRFtoken",$.cookie("csrftoken"));
                            layer.msg('同步数据中,请等待......', {icon: 4,time:600000});
                        },
                        success: function (data) {
                            if (data.code == 0){
                                layer.alert(data.msg, {icon: 6});
                            } else {
                                layer.alert(data.msg, {icon: 7});
                            }
                        },
                        complete: function () {
                            console.info("同步数据执行完成......");
                        },
                        error: function (data) {
                            console.info("同步数据执行出错......");
                        }
                    });
                })

    转自: https://www.cnblogs.com/small-bud/p/12381813.html

    不论你在什么时候开始,重要的是开始之后就不要停止。 不论你在什么时候结束,重要的是结束之后就不要悔恨。
  • 相关阅读:
    洛谷 P4707 重返现世
    多项式总结&多项式板子
    线性常系数齐次递推
    洛谷 P2791 幼儿园篮球题
    CF Gym 102028G Shortest Paths on Random Forests
    洛谷 P4705 玩游戏
    [NOI2018]冒泡排序
    PKUSC2019 改题记录
    PKUSC2019 没约记
    Codeforces Round #557 题解【更完了】
  • 原文地址:https://www.cnblogs.com/yunhgu/p/14107078.html
Copyright © 2020-2023  润新知