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

二、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')

猜你喜欢

转载自www.cnblogs.com/xvchengqi/p/10009244.html