• DAY85-Django框架(十五) 中间件和CSRF跨站伪装请求


    一、中间件

    1.定义

    ​ 中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。

    ​ 每次请求到视图之前,或者响应到浏览器之前都会经过中间件的筛选

    2.基本使用

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    #这是Django内部已经封装好的中间件
    

    3.自定义中间件

    #中间件的方法
    process_request(self,request)
    
    process_view(self, request, callback, callback_args, callback_kwargs)
    
    process_template_response(self,request,response)
    
    process_exception(self, request, exception)
    
    process_response(self, request, response)
    
    #第一步:创建一个PY文件
    #第二步:导入from django.utils.deprecation import MiddlewareMixin
    from django.utils.deprecation import MiddlewareMixin
    
    #第三步:新建自定义中间件process_request和process_response是必须的
    class Mymiddle1(MiddlewareMixin):
        def process_request(self, request):
            print('Mymiddle1   request')
    
        def process_response(self, request, response):
            print('Mymiddle1   response')
            return response#process_response一定要返回HttpResponse对象
    
    
    class Mymiddle2(MiddlewareMixin):
        def process_request(self, request):
            print('Mymiddle2   request')
    
        def process_response(self, request, response):
            print('Mymiddle2   response')
            return response
    
    
    #第四步:写一个视图函数
    from django.shortcuts import render,HttpResponse
    def index(requset):
        print('index')
        return HttpResponse('ok')
    
    
    #第五步:在setting里设置
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'app01.mymiddleware.Mymiddle1',
        'app01.mymiddleware.Mymiddle2'
    ]
    #请求是从上而下一层层的通过中间件的,之后才是执行视图层,而响应是从下而上一层层的返回去。
    
    #结果
    Mymiddle1   request
    Mymiddle2   request
    index
    Mymiddle2   response
    Mymiddle1   response
    

    结论

    ​ 如果请求再通过中间件时,在process_request遇到了return返回,那么这个请求就不会再往下走进入视图了,而是从当前的process_response往上的返回

    from django.shortcuts import render,HttpResponse
    class Mymiddle1(MiddlewareMixin):
        def process_request(self, request):
            print('Mymiddle1   request')
            print('Mymiddle1   请求中断')
            return HttpResponse('Mymiddle1 请求中断')
    
        def process_response(self, request, response):
            print('Mymiddle1   response')
            return response#process_response一定要返回HttpResponse对象
    
    #结果
    Mymiddle1   request
    Mymiddle1   请求中断
    Mymiddle1   response
    

    4.process_view

    #语法
    process_view(self, 
    request, 			#request是HttpRequest对象。
    callback, 			#view_func是Django即将使用的视图函数。
    callback_args, 		#view_args是将传递给视图的位置参数的列表.
    callback_kwargs		#view_kwargs是将传递给视图的关键字参数的字典。
    )
    

    ​ 它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import render,HttpResponse
    
    class Mymiddle1(MiddlewareMixin):
        def process_request(self, request):
            print('Mymiddle1   request')
    
        def process_response(self, request, response):
            print('Mymiddle1   response')
            return response
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("Mymiddle1   view")
    
    
    class Mymiddle2(MiddlewareMixin):
        def process_request(self, request):
            print('Mymiddle2   request')
    
        def process_response(self, request, response):
            print('Mymiddle2   response')
            return response
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("Mymiddle2   view")
    
    Mymiddle1   request
    Mymiddle2   request
    Mymiddle1   view
    Mymiddle2   view
    index
    Mymiddle2   response
    Mymiddle1   response
    

    结论

    如果中间件1中process_view在return,那么就会跳过之后的process_view和视图函数,直接执行process_response

    def process_view(self, 	request, callback, callback_args, callback_kwargs):        
        print('Mymiddle1   view中断')
        return HttpResponse('Mymiddle1 view中断')
    
    #结果
    Mymiddle1   request
    Mymiddle2   request
    Mymiddle1   view中断
    Mymiddle2   response
    Mymiddle1   response
    

    可以在process_view中调用视图函数,也会跳过之后的process_view和视图函数,直接执行process_response

    def process_view(self, request, callback, callback_args, callback_kwargs):
            res = callback(request)
            print('Mymiddle1   view中断')
            return res
    
    #结果
    Mymiddle1   request
    Mymiddle2   request
    index
    Mymiddle1   view中断
    Mymiddle2   response
    Mymiddle1   response
    

    5.process_exception

    process_exception(self, 
    request, 			#HttpRequest对象
    exception			#视图函数异常产生的Exception对象
    )
    

    ​ 这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import render,HttpResponse
    
    class Mymiddle1(MiddlewareMixin):
        def process_request(self, request):
            print('Mymiddle1   request')
            # print('Mymiddle1 请求中断')
            # return HttpResponse('Mymiddle1 请求中断')
    
        def process_response(self, request, response):
            print('Mymiddle1   response')
            return response
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("Mymiddle1   view")
            # res = callback(request)
            # print('Mymiddle1   view中断')
            # return res
    
        def process_exception(self,request,exception):
            print("Mymiddle1   exception")
    
    
    class Mymiddle2(MiddlewareMixin):
        def process_request(self, request):
            print('Mymiddle2   request')
    
        def process_response(self, request, response):
            print('Mymiddle2   response')
            return response
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("Mymiddle2   view")
    
        def process_exception(self,request,exception):
            print("Mymiddle2   exception")
    
    #结果
    #当视图函数没有出现异常时,触发process_exception方法
    Mymiddle1   request
    Mymiddle2   request
    Mymiddle1   view
    Mymiddle2   view
    index
    Mymiddle2   response
    Mymiddle1   response
    

    结论

    process_exception是在视图函数执行完毕之后出发的,和process_response一样是从下往上的执行

    如果此时视图函数出现了异常

    def index(requset):
        print('index')
        asd#未定义的变量
        return HttpResponse('ok')
    

    1.自定义的process_exception中没有return,那么会内置的来抛出异常

    Mymiddle1   request
    Mymiddle2   request
    Mymiddle1   view
    Mymiddle2   view
    index
    Mymiddle2   exception
    Mymiddle1   exception
    ...
    异常信息
    ...
    Mymiddle2   response
    Mymiddle1   response
    

    2.自定义的process_exception中写了return,那么就不会在执行之后的process_exception,而是执行process_response

    def process_exception(self,request,exception):
        print('Mymiddle2   抛出异常')
        return HttpResponse('Mymiddle2   抛出异常')
    
    #结果
    Mymiddle1   request
    Mymiddle2   request
    Mymiddle1   view
    Mymiddle2   view
    index
    Mymiddle2   抛出异常
    Mymiddle2   response
    Mymiddle1   response
    

    ​ 如果Mymiddle2的 process_exception没有返回HttpResponse对象,那么就会由从下往上的顺序,去找有没有返回HttpResponse对象的process_exception,直到找到。

    #结果
    Mymiddle1   request
    Mymiddle2   request
    Mymiddle1   view
    Mymiddle2   view
    index
    Mymiddle2   exception
    Mymiddle1   抛出异常
    Mymiddle2   response
    Mymiddle1   response
    

    6.中间件的实际应用

    # 登陆装饰器
    lis = ['/shopping/', '/order/']
    class Login(MiddlewareMixin):
        def process_request(self, request):
            url = request.get_full_path()
            if request.path in lis:
                if not request.session.get('is_login'):
                    return redirect('/login/?next=%s' % url)
    
        def process_response(self, request, response):
            return response
    
    
    # 限制请求次数
    import time
    class Overtimr(MiddlewareMixin):
        def process_request(self, request):
            IP = request.META['REMOTE_ADDR']
            id_time = request.session.get(IP, [])
            t = time.time()
            if len(id_time) < 3:
                id_time.append(t)
                request.session[IP] = id_time
            else:
                if time.time() - id_time[0] < 60:
                    print(time.time() - id_time[0])
                    return HttpResponse('操作频繁')
                else:
                    print(time.time() - id_time[0])
                    id_time.pop(0)
                    id_time.append(time.time())
                    request.session[IP] = id_time
    
        def process_response(self, request, response):
            return response
    

    二、CSRF跨站请求伪造

    1.CSRF

    ​ CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性

    可以这样来理解:
    *攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的*,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。

    2.攻击原理

    要完成一次CSRF攻击,受害者必须依次完成两个步骤:

      1.登录受信任网站A,并在本地生成Cookie。

      2.在不登出A的情况下,访问危险网站B。

    看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:

      1.你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。

      2.你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了......)

      3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。

    3.防御攻击

    ​ 目前防御 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求中添加 token 并验证;在 HTTP 头中自定义属性并验证

    ​ 对于CSRF的防御,Django内部做了一个中间件来处理CSRF的攻击

    MIDDLEWARE = [
        'django.middleware.csrf.CsrfViewMiddleware',
    ]
    

    ​ 它设置了一个随机字符串,通过响应发给受信任的用户,当受信任用户发送请求时必须携带一模一样的随机字符串,才可以进入服务器。而且每次响应服务器给浏览器都不一样的,有效的做到了对CSRF的防御

    方式一:请求中添加 token 并验证

    放在表单中请求

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="" method="post">
        {% csrf_token %}
        <!--渲染之后,获得随机字符串
    	<input type="hidden" name="csrfmiddlewaretoken" value="RaxCabhuZdpvWxQ67whrdmImNPYuFVQ5Ies9X52TVrIbt1AdVfxPNbMUFVKgLWp4">
    -->
        <input type="text" name="name" placeholder="用户名">
        <input type="text" name="pwd" placeholder="密码">
        <input type="submit">
    </form>
    </body>
    </html>
    
    from django.shortcuts import render, HttpResponse
    def csrf_test(request):
        if request.method == 'GET':
            return render(request, 'csrf_test.html')
        else:
            name = request.POST.get('name')
            pwd = request.POST.get('pwd')
            token = request.POST.get('csrfmiddlewaretoken')
            print(token)
            if name == 'xcq' and pwd == '123':
                msg = '登陆成功'
            else:
                msg = '登录失败'
            return HttpResponse(msg)
    

    通过AJAX

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        {% load static %}
        <script src="{% static 'jquery-3.3.1.js' %}"></script>
    </head>
    <body>
    <form action="" method="post">
        {% csrf_token %}
        <input type="text" name="name" placeholder="用户名">
        <input type="text" name="pwd" placeholder="密码">
    </form>
    <button id="btn">登录</button>
    </body>
    <script>
        //post请求
        $('#btn').click(function () {
            $.ajax({
                url:'/csrf_test/',
                type:'post',
                data:{'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()},
                //或者 data:{'csrfmiddlewaretoken':'{{ csrf_token }}'},
                success:function (data) {
                    alert(data)
                }
            })
        })
        
        //get请求
            $('#btn').click(function () {
            $.ajax({
                url:'/csrf_test/?id={{ csrf_token }}' ,
                type:'get',
                success:function (data) {
                    alert(data)
                }
            })
        })
    </script>
    </html>
    

    方式二:在 HTTP 头中自定义属性并验证

    //获取cookie里的csrftoken
    $("#btn").click(function () {
        var token=$.cookie('csrftoken')
        $.ajax({
            url: '/csrf_test/',
            headers:{'X-CSRFToken':token},
            type: 'post',
            data: {
                'name': $('[name="name"]').val(),
                'password': $("#pwd").val(),
            },
            success: function (data) {
                alert(data)
            }
        })
    })
    

    局部禁用和局部使用

    FBV模式

    from django.views.decorators.csrf import csrf_exempt,csrf_protect
    #局部禁用,全局得使用
    @csrf_exempt
    def csrf_disable(request):
        print(request.POST)
        return HttpResponse('ok')
    #局部使用,全局得禁用
    @csrf_protect
    def csrf_disable(request):
        print(request.POST)
        return HttpResponse('ok')
    

    CBV模式

    from django.views import View
    from django.utils.decorators import method_decorator
    from django.views.decorators.csrf import csrf_exempt,csrf_protect
    
    @method_decorator(csrf_protect,name='dispatch')
    #CBV的csrf装饰器,只能加载类上且指定方法为dispatch,或dispatch方法上
    class Csrf_disable(View):
        # @method_decorator(csrf_protect)
        def dispatch(self, request, *args, **kwargs):
            ret=super().dispatch(request, *args, **kwargs)
            return ret
        def get(self,request):
            return HttpResponse('ok')
    
        def post(self,request):
            return HttpResponse('post---ok')
    
  • 相关阅读:
    window7环境下VMWare自定义安装Linux虚拟机完全教程
    Extjs的GridPanel分页前后台完整代码实例
    python数据持久存储:pickle模块的基本使用
    一个 11 行 Python 代码实现的神经网络
    Hadoop入门实例——WordCount统计单词
    Linux环境搭建Hadoop伪分布模式
    函数
    循环
    docker环境下gitlab配置ci
    docker部署mysql配置过程记录
  • 原文地址:https://www.cnblogs.com/xvchengqi/p/10009244.html
Copyright © 2020-2023  润新知