django的crsf機制防御詳解及在前后端分離中post數據到django-vue


django的crsf機制防御詳解及在前后端分離中post數據到django

CSRF(Cross Site Request Forgery) 跨站點偽造請求

某個用戶已經登陸了你的網站,另外有一個惡意的網站有一個指向你網站的鏈接,那么當用戶點擊這個鏈接時,就會請求你的網站,但是你的網站以為是用戶發來的請求,這時惡意網站就得逞了。

django的應對措施

用戶在post請求時,發送給用戶一個token,然后在django內部實現了一個校驗這個token的函數,當正確時,服務器就會返回正確的內容,如果不正確就是返回403。

django自帶的表單模版中可以直接使{% csrf_token %}來通過django的檢驗。

如何工作

引用django官網 點此查看

CSRF保護基於以下內容:

  1. CSRF cookie,基於隨機密鑰值,其他站點無權訪問。

這個cookie設置為CsrfViewMiddleware。django.middleware.csrf.get_token()如果尚未在請求中設置,則與每個已調用的響應(內部用於檢索CSRF令牌的函數)一起發送。
為了防止BREACH攻擊,令牌不僅僅是秘密; 隨機鹽被置於秘密之前並用於加擾它。
出於安全原因,每次用戶登錄時都會更改密鑰的值。

  1. 所有傳出POST表單中都有一個名為“csrfmiddlewaretoken”的隱藏表單字段。此字段的值同樣是秘密的值,鹽添加到它並用於加擾它。每次調用時都會重新生成salt,get_token()以便在每個此類響應中更改表單字段值。
    這部分由模板標簽完成。

  2. 對於未使用HTTP GET,HEAD,OPTIONS或TRACE的所有傳入請求,必須存在CSRF cookie,並且’csrfmiddlewaretoken’字段必須存在且正確。如果不是,用戶將收到403錯誤。
    驗證’csrfmiddlewaretoken’字段值時,只將秘密而不是完整令牌與cookie值中的秘密進行比較。這允許使用不斷變化的令牌。雖然每個請求都可以使用自己的令牌,但秘密仍然是所有人共同的。
    這項檢查是通過CsrfViewMiddleware。

  3. 此外,對於HTTPS請求,嚴格的引用檢查由 CsrfViewMiddleware。這意味着即使子域可以在您的域上設置或修改cookie,它也不能強制用戶發布到您的應用程序,因為該請求不會來自您自己的確切域。
    這也解決了在使用會話獨立秘密時在HTTPS下可能發生的中間人攻擊,因為Set-Cookie即使用戶在HTTPS下與某個站點通信時,HTTP 頭(不幸)也被接受了。(對HTTP請求不進行引用檢查,因為在HTTP下,Referer標頭的存在不夠可靠。)
    如果CSRF_COOKIE_DOMAIN設置了該設置,則將引用者與其進行比較。此設置支持子域。例如, 將允許來自和的POST請求 。如果未設置該設置,則引用必須與HTTP 標頭匹配。CSRF_COOKIE_DOMAIN = ‘.example.com’www.example.comapi.example.comHost
    可以使用該CSRF_TRUSTED_ORIGINS設置將已接受的引用擴展到當前主機或cookie域之外。

請求分析
在django第一次渲染渲染模版的時候,{% csrf_token %}

會調用django.middleware.csrf.get_token生成一個64位的無序字符csrfmiddlewaretoken放在html里,並且會在瀏覽器的cookie里面設置64位的csrftoken=,然后在用戶點擊提交按鈕的時候會在后台通過調用某種算法來計算這兩個值生成的token是否相等來方法csrf攻擊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="container">
<form class="form-signin" method="POST">
{% csrf_token %}
<h2 class="form-signin-heading">Please login in</h2>
<label for="inputEmail" class="sr-only">Email address</label>
<input name="loginEmail" type="email" id="inputEmail" class="form-control" placeholder="Email address" value = "{{myLogin.loginEmail}}" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input name="loginPassword" type="password" id="inputPassword" class="form-control" placeholder="Password" required>
<!-- <div class="checkbox">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div> -->
<button class="btn btn-lg btn-primary btn-block" type="submit">Login in</button>
</form>
</div> <!-- /container -->

 

下面來是生成token的算法分析
django source

主要的一些算法代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def get_token(request):
"""
Return the CSRF token required for a POST form. The token is an
alphanumeric value. A new token is created if one is not already set.
A side effect of calling this function is to make the csrf_protect
decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie'
header to the outgoing response. For this reason, you may need to use this
function lazily, as is done by the csrf context processor.
"""
if "CSRF_COOKIE" not in request.META:
csrf_secret = _get_new_csrf_string()
request.META["CSRF_COOKIE"] = _salt_cipher_secret(csrf_secret)
else:
csrf_secret = _unsalt_cipher_token(request.META["CSRF_COOKIE"])
request.META["CSRF_COOKIE_USED"] = True
return _salt_cipher_secret(csrf_secret)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from django.utils.crypto import get_random_string
import string

CHAR = string.ascii_lowercase
SECRET_LEHGTH = 3


def get_new_sting():
return get_random_string(SECRET_LEHGTH, allowed_chars=CHAR)

def salt_cipher_secret(secret):
# 鹽的長度是固定的
salt = get_new_sting()
pairs = zip((CHAR.index(x) for x in secret), (CHAR.index(x) for x in salt))
cipher = ''.join(CHAR[(x+y) % len(CHAR)] for x, y in pairs)
return salt + cipher
token = salt_cipher_secret(get_new_sting())
# print(salt_cipher_secret('abc'))

def unsalt_cipher_secret(token):
salt = token[:SECRET_LEHGTH]
token = token[SECRET_LEHGTH:]
pairs = zip((CHAR.index(x) for x in salt), (CHAR.index(x) for x in token))
secret = ''.join(CHAR[y-x] for x, y in pairs)
return secret
unsalt_cipher_secret(token)

cookie中的值csrftokrn在用戶請求了一次后以后不會再改變,而csrfmiddlewaretoken會每次改變

主要要用的三個函數的作用

  1. get_token是返回csrfmiddlewaretoken的函數,同時第一次請求的話會設置csrftoken到到ccookie中 ,要是重復請求則不會再重新生成。
  2. salt_cipher_secret是生成csrfmiddlewaretokencsrftoken的主要函數,此函數需要一個secret,通過這個secret然后加上salt就可以用相同的secret生成不同的64位token。
  3. unsalt_cipher_secret是通過token來反向生成secret的函數,此方法主要用兩個用途:
    • 重復請求時,通過cookie中的csrftoken然反向生成生成這個值的secret,然后使用這個secret來生成表單中的csrfmiddlewaretoken,
    • 提交表單后通過這個函數, post數據中的csrfmiddlewaretoken和cookie中的csrftoken會通過這個函數計算出生成這兩個值的secret如果相等,就驗證成功,如果不想等就會返回403

算法分析
salt_cipher_secret
生成需要加入的鹽salt,然后分別計算出secret和salt在CHAR中的索引值,然后將對應的索引值相加后除以CHAR的長度后得到的余數,再在CHAR找出對應的值,然后再與salt相加返回,
unsalt_cipher_secret
此函數的通過token反向得到secret與salt_cipher_secret步驟相反,通過切片得到salt和token1,然后分別找出在CHAR中的索引值,然后相減后再在CHAR中找出對應索引上的值,然后拼接成secret。

前后端分離后前端驗證django的token

只要前端在post數據的時候只要把每次通過get_token函數生成的隨機字段發送過去就可以驗證了,可以放在post數據中,也可以放在請求頭中,

放在請求頭中

django的views中設置一個可以獲取csrfmiddlewaretoken的方法就是調用get_token函數返回一個字串

django

1
2
3
4
5
6
# views.py
from django.middleware.csrf import get_token

def token(request):
token = get_token(request=request)
return JsonResponse({'token': token}, json_dumps_params={'indent':3})

 

前端vue
先請求這個返回token的接口,然后將返回的值加入到post放的請求,代碼如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sumbit() {
this.$axios.get("/api/get_token").then(response => {
this.cookie_data = response.data['token'];
this.post(this.cookie_data)
});
},
post(CSRFToken) {
this.dialog_operate = false;
console.log(this.data)
this.$axios
.post("/api/operate",
this.data,
{
headers: {'X-CSRFToken': CSRFToken,'Content-Type':'application/json'}
}

)

 

 

 

 

 

js 使用session 、cookie、angular cookie保存token

 

目錄

1、前言

2、js前端使用session保存token

3、js前端使用cookie保存token

4、js前端使用angular cookie保存token

 

內容

1、前言

  在前端請求后台的API接口的時候,為了安全性,一般需要在用戶登錄成功之后才能發送其他請求。

  所以,在用戶登錄成功之后,后台會返回一個token給前端,這個時候我們就需要把token暫時保存在本地,每次發送請求的時候需要在header里面帶上token,這時候本地的token和后台數據庫中的token進行一個驗證,如果兩者一致,則請求成功,否則,請求失敗。

  如下圖,登錄成功之后返回token:

  

  如下圖所示,進行其他請求的時候在header里面帶上token:

  

 

 2、js前端使用session保存token

  sessionStorage屬性允許你訪問一個Session Storage對象,它與localStorage相似,不同之處在於localStorage里面存儲的數據沒有過期設置,而存儲在sessionStorage里面的數據在頁面會話結束時會被清除。頁面會話在瀏覽器打開期間一直保持,並且重新加載或者恢復頁面仍會保持原來的頁面會話。

  下面是session中儲存token的具體的語法:

復制代碼
// 保存數據到sessionStorage
sessionStorage.setItem('key', 'value');

// 從sessionStorage獲取數據
var data = sessionStorage.getItem('key');

// 從sessionStorage刪除保存的數據
sessionStorage.removeItem('key');

// 從sessionStorage刪除所有保存的數據
sessionStorage.clear();
復制代碼

3、js前端使用cookie保存token

  將token保存在cookie中,一旦瀏覽器關閉,cookie中的token就會被清空。

  下面是cookie中存儲token的語法:

//將token保存在cookie中
document.cookie=token;

//從cookie中取出token
var token = document.cookie.split(";")[0];

4、js前端使用angular cookie保存token

  在angular中引入cookie必須首先引入angular-cookies.js文件。下面是直接能夠在瀏覽器中打開的版本的鏈接:https://cdn.bootcss.com/angular-cookie/4.1.0/angular-cookie.js

  下面是angular cookie中存儲token的語法:

復制代碼
//存儲token
angular
    .module('theme.demos.login_page', ['ngCookies'])
    .controller('LoginController'['$scope','$cookieStore',function($scope,$cookieStore) {
         var valueIndex=encodeURI(data.data.token);            
         $cookieStore.put('tokenIndex',valueIndex);
})

//拿到token    
angular
    .module('theme.demos.login_page', ['ngCookies'])
    .controller('LoginController'['$scope','$cookieStore',function($scope,$cookieStore) {
         var token= $cookieStore.get('tokenIndex');
})
復制代碼

  以上就是三種較為常見的js中存儲token的方法。並且這三種方法在Google瀏覽器、Firefox瀏覽器、IE瀏覽器中均可兼容,但是在Safari瀏覽器中會出現不兼容問題(Apple),主要是無法將token寫入cookie中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM