DAY85-Django框架(十五) 中間件和CSRF跨站假裝請求

1、中間件

1.定義

​ 中間件顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,而且在全局上改變django的輸入與輸出。由於改變的是全局,因此須要謹慎實用,用很差會影響到性能。javascript

​ 每次請求到視圖以前,或者響應到瀏覽器以前都會通過中間件的篩選html

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

結論java

​ 若是請求再經過中間件時,在process_request遇到了return返回,那麼這個請求就不會再往下走進入視圖了,而是從當前的process_response往上的返回python

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並返回結果。jquery

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

結論ajax

若是中間件1中process_view在return,那麼就會跳過以後的process_view和視圖函數,直接執行process_responsedjango

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

結論cookie

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

2、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')
相關文章
相關標籤/搜索