1. 程式人生 > 其它 >django 中介軟體和csrf

django 中介軟體和csrf

django 中介軟體和csrf

django操作cookie

# cookie 常見引數
set_signed_cookie(key,value,salt='加密鹽')

set_signed_cookie(key,value,max_age=超時時間:預設是秒數)

expires:專門針對IE瀏覽器設定超時時間
    
# 刪除cookie
HttpResponse物件.delete_cookie(key) 

django操作session

# session
session資料是儲存在服務端的(存?),給客戶端返回的是一個隨機字串
sessionid	: 隨機字串

# 在預設情況下操作session的時候需要django預設的一張django_session表
當我們執行資料庫遷移命令
django會自己建立很多表 django_session就是其中的一張

# django預設session過期時間
django預設的session的過期時間是14天
但是你也可以人為的修改它

# 操作session格式
設定
request.session['key'] = value  # 可以設定多組
獲取
request.session.get('key')  # 可以獲取多組


設定session內部工作原理

# 設定session
def set_session(request):
    request.session['name'] = 'jason'
    return HttpResponse('設定成功')

# 分析
1.django內部會自動幫你生成一個隨機字串

2.django內部自動將隨機字串和對應的資料儲存到django_session表中
	中介軟體第二個執行:
	2.1 先在記憶體中產生操作資料的快取
	2.2 在響應經過django中介軟體的時候才真正的操作資料庫
    
3.將產生的隨機字串返回給客戶端瀏覽器儲存

獲取session內部工作原理

# 獲取session
def get_session(request):
    print(request.session.get('name'))
    return HttpResponse('獲取成功')

# 分析
1.自動從瀏覽器請求中獲取session對應的隨機字串

2.拿著該隨機字串去django_session表中查詢對應的資料

3.如果比對上了自動獲取並'解密處理' 將對應的資料取出並以字典的形式封裝到request.session中

4.如果比對不上 則request.session.get()返回是None

django操作session引數

# 獲取產生的隨機字串
request.session.session_key  

# 只刪客戶端
request.session.delete() 

# 服務端 客戶端都刪
request.session.flush()  

# 刪除session
def del_session(request):
    request.session.delete()
    return HttpResponse('刪除')

session 設定過期時間

'request.session.set_expiry() 設定時間'
# set_expiry() 括號內可以放四種類型的引數:
1.整數						 多少秒
2.日期物件			   			到指定日期就失效
3.0							   一旦當前瀏覽器視窗關閉立刻失效
4.不寫						 失效時間就取決於django內部全域性session預設的失效時間

# 設定過期時間的使用

'設定session'
def set_session(request):
    # 設定session
    request.session['name'] = 'jason'
    # 設定過期時間
    request.session.set_expiry(3)
    return HttpResponse('設定了')


'獲取session'
def get_session(request):
    # 判斷session
    if request.session.get('name'):
        print(request.session)
        print(request.session.get('name'))
        return HttpResponse('獲取了')
    return HttpResponse('失效,未獲取')

session資料的儲存位置

1.資料庫儲存
2.快取儲存
3.檔案儲存
4.快取+資料庫儲存
5.動態加密

CBV新增裝飾器

'''
CBV中django不建議直接給類的方法加裝飾器
CBC新增裝飾器的三種方法
'''
# 需要藉助於一個專門的裝飾器模組
from django.utils.decorators import method_decorator

# 方式1:直接在類中的某個方法上新增
class MyLoginView(views.View):
    @method_decorator(login_auth)
    def get(self, request):
        return HttpResponse("from CBV get view")
    
# 方式2:直接在類名上新增並指定
'可以新增多個針對不同的方法加不同的裝飾器'
@method_decorator(login_auth, name='get')
@method_decorator(login_auth, name='post')
class MyLoginView(views.View):
    def get(self, request):
        return HttpResponse("from CBV get view")
    
# 方式3:重寫dispatch方法並新增作用於類中所有的方法
class MyLoginView(views.View):
    @method_decorator(login_auth)
    def dispatch(self, request, *args, **kwargs):
        super().dispatch(request,*args,**kwargs)

django中介軟體

# 中介軟體的簡介
django自帶7箇中間件,每個中介軟體都有各自的功能
django還支援程式設計師自定義中介軟體
'''
在用django開發專案的專案的時候
只要涉及到全域性相關功能都可以使用中介軟體方便完成
'''
# 中介軟體是django的門戶
1請求來的時候需要先經過中介軟體才能到達真正的django後端

2響應走的時候最後也需要經過中介軟體才能傳送出去

# 中介軟體使用常見場景
'只要涉及到專案全域性的功能,一定要想到中介軟體'
使用者黑名單校驗
使用者訪問頻率校驗
網站全域性使用者身份校驗

# 自定義中介軟體
process_request
process_response
process_view
process_template_response
process_excepton

自定義中介軟體

# 建立自定義中介軟體
1.建立一個任意名稱的資料夾
2.在該資料夾內建立一個任意名稱的py檔案
3.在該py檔案內編寫中介軟體類(這個類必須要繼承MiddlewareMixin)
4.配置檔案中註冊

# 必須掌握的方法
1.process_request
2.process_response

# 需要了解的方法
1.process_view
2.process_template_response
3.process_exception

process_request

1.請求來的時候需要經過每一箇中間件裡面的process_request方法
結果的順序是按照配置檔案中註冊的中介軟體從上往下的順序依次執行

2.如果中介軟體裡面沒有定義該方法,那麼直接跳過執行下一個中介軟體

3.如果該方法返回HttpResponse物件,那麼請求不再繼續往後直接返回相應的資料(校驗失敗不允許訪問)

process_response

1.響應走的時候需要經過每一箇中間件裡面的process_response方法
'該方法有兩個額外引數request,response'
2.該方法必須返回一個HttpResponse物件
	2.1預設返回的就是形參response
	2.2你也可以自己返回自己的
3.順序是按照配置檔案中註冊了中間價從下往上依次執行經過所有的中間裡面的process_response
如果你沒有定義的話,直接跳過執行下一個
'''
如果第一個process_response方法已經返回了一個HttpResponse物件,那麼響應會替換成該HttpResponse物件資料 而不再是檢視函式想要返回給客戶端的資料
'''

其他方法

# process_view
  	路由匹配成功之後執行檢視之前從上往下執行配置檔案中註冊了的中介軟體裡面的process_view方法
    
# process_template_response
  	檢視函式執行完畢之後返回的物件中含有render屬性對應一個render方法
    則會從下往上執行配置檔案中註冊了的中介軟體裡面的process_template_response方法
    
# process_exception
  	檢視函式執行過程中報錯並在返回響應的時候會從下往上執行配置檔案中註冊了的中介軟體裡面的process_exception

csrf跨站請求偽造

# csrf的簡介
CSRF(Cross Site Request Forgery, 跨站域請求偽造)是一種網路的攻擊方式,它在 2007 年曾被列為網際網路 20 大安全隱患之一。其他安全隱患,比如 SQL 指令碼注入,跨站域指令碼攻擊等在近年來已經逐漸為眾人熟知,很多網站也都針對他們進行了防禦
'''
攻擊者(黑客,釣魚網站)盜用了你的身份,以你的名義傳送惡意請求,這些請求包括髮送郵件,傳送訊息,盜取賬號,購買商品,銀行轉賬,從而使你的個人隱私洩露和財產損失
'''

csrf簡單模擬

# csrf模擬
釣魚網站:一個模仿正規網站的網址 誘騙使用者在該網站上做本應該在正規網站上做的操作,從而獲取到該使用者在正規網站上的資料甚至是財產
  eg:假設我們需要登入網頁完成轉賬操作
    我們不小心登入到了釣魚網站 填寫了賬戶 密碼 對方賬戶等資訊
    點選轉賬之後我們賬戶的錢確實減少了 但是對方賬戶卻變成了一個你從來不認識的人
  原理:將收款人的賬號 提前寫成犯罪分子的然後隱藏 暴露給使用者一個沒有name屬性的標籤寫著玩
# 釣魚網站 原理
1.我們在釣魚網站的頁面,針對對方賬戶,只給使用者提供一個沒有name屬性的普通input框
2.然後我們在內部隱藏一個已經寫好name和value和input框


# 正規網站
# html
<h1>我是正兒八經的網站</h1>
<form action="" method="post">
    <p>username: <input type="text" name="username"></p>
    <p>target_user: <input type="text" name="target_user"></p>
    <p>money: <input type="text" name="money"></p>
    <input type="submit">
</form>
# views
def transfer(request):
    if request.method == 'POST'
        username = request.POST.:get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print('%s給%s轉了%s元' % (username, target_user, money))
    return render(request, 'transfer.html')


# 不正規釣魚網站	

<h1>我是釣魚網站</h1>
<form action="http://127.0.0.1:8000/transfer/" method="post">
    <p>username: <input type="text" name="username"></p>
    <p>target_user: <input type="text"></p>
    {# 隱藏 #}
    <input type="text" name="target_user" value="jason" style="display: none">

    <p>money: <input type="text" name="money"></p>
    <input type="submit">
</form>
        
def transfer(request):
    return render(request, 'transfer.html')

csrf攻擊原理

網站是通過cookie來實現登入功能的。而cookie只要存在瀏覽器中,那麼瀏覽器在訪問這個cookie的伺服器的時候,就會自動的攜帶cookie資訊到伺服器上去。那麼這時候就存在一個漏洞了,如果你訪問了一個別有用心或病毒網站,這個網站可以在網頁原始碼中插入js程式碼,使用js程式碼給其他伺服器傳送請求(比如ICBC的轉賬請求)。那麼因為在傳送請求的時候,瀏覽器會自動的把cookie傳送給對應的伺服器,這時候相應的伺服器(比如ICBC網站),就不知道這個請求是偽造的,就被欺騙過去了。從而達到在使用者不知情的情況下,給某個伺服器傳送了一個請求

csrf解決策略

form表單校驗

# form表單
	<form action="" method="post">
    {% csrf_token %} // 給正規的網站頁面都新增一個唯一標識
    <p>當前賬戶:<input type="text" name="current_user"></p>
    <p>目標賬戶:<input type="text" name="target_user"></p>
    <p>轉賬金額:<input type="text" name="money"></p>
    <input type="submit">
	</form>

ajax校驗

# 方式1:頁面任意位置先寫{% csrf_token %} 之後獲取資料 
'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()
# 方式2:模板語法直接獲取
'csrfmiddlewaretoken':{{ csrf_token }}
<button id="d1">ajax請求</button>


{# 動態解析 靜態檔案#}
{% load static %}
<script src="{% static 'js/mysetup.js' %}"></script>

<script>
    $('#d1').click(function () {
        $.ajax({
            url:'',  // 不寫預設提交地址當前
            type:'post',  // 請求
            // 第一種
            {#data:{"username":"jason", 'csrfmiddlewaretoken':$('[name=csrfmiddlewretoken]').val()},#}
            // 第二種
            {#data:{"username":'jason', 'csrfmiddlewaretoken':'{{ csrf_token }}'},#}
            data:{"username":'jason'},  // 資料
            success:function () {
            }
        })
    })
</script>

通用方式校驗ajax

# 通用解決方案:js指令碼自動處理
	也只能適用於ajax提交  form表單還是需要額外指定
    
將檔案配置到你的Django專案的靜態檔案中,在html頁面上通過匯入該檔案即可自動幫我們解決ajax提交post資料時校驗csrf_token的問題,(匯入該配置檔案之前,需要先匯入jQuery,因為這個配置檔案內的內容是基於jQuery來實現的)
# 參考網址
https://docs.djangoproject.com/en/1.11/ref/csrf/

getCookie方法

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

使用$.ajaxSetup()方法為ajax請求統一設定

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);
    }
  }
});