Django防御csrf攻擊的實(shí)現(xiàn)方式(包括ajax請求)
csrf攻擊簡要說明
用戶A現(xiàn)在登錄了某銀行網(wǎng)站的官網(wǎng),瀏覽器記錄了該網(wǎng)站生成的記錄用戶信息的cookie,而后用戶A點(diǎn)擊了釣魚網(wǎng)站(該網(wǎng)站和銀行網(wǎng)站長的一樣),該釣魚網(wǎng)站內(nèi)有js代碼向銀行官網(wǎng)發(fā)起轉(zhuǎn)賬請求,此時(shí)該請求會(huì)自動(dòng)攜帶記錄用戶信息的cookie值去訪問銀行官網(wǎng)進(jìn)行轉(zhuǎn)賬操作,銀行網(wǎng)站的后端若沒有設(shè)置csrf防御機(jī)制,僅僅驗(yàn)證用戶是否登錄,就會(huì)認(rèn)為該轉(zhuǎn)賬操作是該用戶的合法操作。如果銀行網(wǎng)站不僅驗(yàn)證cookie,同時(shí)再驗(yàn)證一個(gè)攻擊者無法獲取到的值,就能防御上述漏洞。
CsrfViewMiddleware中間件
Django防御csrf攻擊是通過 django.middleware.csrf.CsrfViewMiddleware
中間件實(shí)現(xiàn)的,當(dāng)我們創(chuàng)建項(xiàng)目后,默認(rèn)在 settings
文件內(nèi)的 MIDDLEWARE
內(nèi)已經(jīng)開啟了該中間件
防御機(jī)理:驗(yàn)證cookie中的 csrftoken
值和表單中 csrfmiddlewaretoken
(或ajax請求頭中的X-CSRFToken參數(shù))的值,兩者里邊的secret是否相同(不是完整的token值是否相同)來進(jìn)行防御(這個(gè)值攻擊者無法獲取到)。
原因:由于瀏覽器同源策略的限制,釣魚網(wǎng)站無法獲取銀行網(wǎng)站給用戶設(shè)置的cookie,也就無法獲取csrftoken值,自然就無法在form表單或ajax請求頭中偽造該值,而django是要驗(yàn)證這兩個(gè)值的secret是否是一致的
非ajax請求實(shí)現(xiàn)
該方法即form標(biāo)簽的action屬性直接提交表單數(shù)據(jù),當(dāng)使用這種方式時(shí),只需要在模板內(nèi)使用 {% csrf_token %}
(必須在form表單內(nèi)) 即可實(shí)現(xiàn)防御,代碼如下:
<form method="post" action="/login/"> {% csrf_token %} </form>
注意:在視圖函數(shù)內(nèi)return響應(yīng)時(shí),如果不是使用的render()函數(shù),則需要確保 RequestContext
被用于渲染模板(render函數(shù)是自帶上下文管理器的),否則 {% csrf_token %}
無法正常工作
{% csrf_token %}
作用:
1、在form表單內(nèi)生成一個(gè)input標(biāo)簽,內(nèi)容為
<input type="hidden" name="csrfmiddlewaretoken" value="jojovCfhvZhhqhbVf82BkzOA9EGIgPheBt3H0obOxygwCp4NHxcZ1tjhBbPl62DE">
2、在響應(yīng)中設(shè)置cookie csrftoken=dArgJ7X1ygDM2lyau37guxRkwDR1lbd3vFbzeTTyAPC1etr2WshEbrm1Ya0Ebozt
ajax請求實(shí)現(xiàn)
現(xiàn)實(shí)開發(fā)中,我們可能更多的使用ajax的方式進(jìn)行請求。
ajax的方式需要在請求頭上設(shè)置 X-CSRFToken
參數(shù)來記錄csrftoken的值,因此問題變成了如何獲取csrftoken的值。
django提供了幾種方式來獲取該值,首先要區(qū)分 CSRF_USE_SESSIONS
是否開啟,該參數(shù)表示是否將CSRF token 存儲在session中而不是cookie中,一般情況下無需修改此值,默認(rèn)是False。
django官方例子是從cookie中獲取csrftoken。
CSRF_USE_SESSIONS 為False的情況
在此情形下,有兩種方式來確保響應(yīng)的cookie中有 csrftoken 這個(gè)值
1、在模板中添加 {% csrf_token %}(任意位置) ,則響應(yīng)的cookie中自然會(huì)有csrftoken
2、模板中沒有添加 {% csrf_token %} ,那么在視圖函數(shù)上添加裝飾器 `ensure_csrf_cookie`就會(huì)強(qiáng)制在響應(yīng)頭中添加 csrftoken這個(gè)cookie
從cookie中獲取值的js代碼
// using jQuery 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');
或者直接使用js的Cookie庫來獲取
var csrftoken = Cookies.get('csrftoken');
上述僅描述從cookie中獲取csrftoken,如果模板中設(shè)置了 {% csrf_token %}
,那么無論從cookie還是dom取值都是可以的
CSRF_USE_SESSIONS 為True的情況
此種方式若要獲取csrftoken,就要求必須在html中存在 csrftoken(不能在cookie中獲取該值),并使用js從dom中獲取該值,也就是說必須在模板中添加 {% csrf_token %}(任意位置)
{% csrf_token %} <script type="text/javascript"> // using jQuery var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val(); </script>
在ajax請求中設(shè)置token
代碼如下:
function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } // 在jquery的每個(gè)請求發(fā)起前設(shè)置X-CSRFToken $.ajaxSetup({ beforeSend: function(xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } });
以上步驟總結(jié)起來就是:
1、獲取csrftoken值(從cookie或dom中獲取,給cookie中設(shè)置該值的兩種方法)
2、在ajax請求頭中設(shè)置 X-CSRFToken
記錄csrftoken值
3、發(fā)起ajax請求即可
前后端完全分離開發(fā)
在此種情形下,html頁面不由django渲染返回(html頁面作為靜態(tài)文件處理,所有數(shù)據(jù)均為異步加載),因此無法通過上述幾種方式來獲取csrftoken的值
通過了解 CsrfViewMiddleware
可知,為form表單生成csrftoken是通過 get_token(request)
函數(shù)實(shí)現(xiàn)的,源碼如下:
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)
編寫視圖函數(shù)用來返回token值:
def get_token(request): token = django.middleware.csrf.get_token(request) return JsonResponse({'token': token})
前端代碼:
var csrftoken; $.get('/csrf_token/', function (resp) { csrftoken = resp.token; // 接受上邊視圖函數(shù)返回的token,保存到全局變量中 document.cookie = 'csrftoken=' + resp.token; // token設(shè)置到cookie中 }); // 將csrftoken設(shè)置到ajax的請求頭上 function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } // 在jquery的每個(gè)請求發(fā)起前設(shè)置X-CSRFToken $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } }); // 下邊發(fā)起ajax請求即可
取消防御的幾種手段
1、完全取消防御,直接注釋掉 settings
中的 django.middleware.csrf.CsrfViewMiddleware
即可
2、少數(shù)視圖函數(shù)取消防御,使用 csrf_exempt
來裝飾視圖函數(shù)即可
3、少數(shù)視圖函數(shù)需要防御,先注釋掉 settings
中的 django.middleware.csrf.CsrfViewMiddleware
,在需要防御的視圖函數(shù)使用 csrf_protect
裝飾器即可
4、如果在 CsrfViewMiddleware
中間件執(zhí)行之前,視圖函數(shù)先執(zhí)行了(如返回404,500等頁面),此時(shí)仍然需要確保{% csrf_token %}能夠起作用,使用requires_csrf_token
裝飾器
參考鏈接:https://docs.djangoproject.com/zh-hans/2.0/ref/csrf/
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用TensorFlow實(shí)現(xiàn)二分類的方法示例
這篇文章主要介紹了使用TensorFlow實(shí)現(xiàn)二分類的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-02-02win10系統(tǒng)配置GPU版本Pytorch的詳細(xì)教程
這篇文章主要介紹了win10系統(tǒng)配置GPU版本Pytorch,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04pandas按某2列進(jìn)行分層隨機(jī)抽樣的實(shí)現(xiàn)
本文主要介紹了pandas按某2列進(jìn)行分層隨機(jī)抽樣的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12Python進(jìn)程multiprocessing.Process()的使用解讀
這篇文章主要介紹了Python進(jìn)程multiprocessing.Process()的使用,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02python openpyxl提取Excel圖片實(shí)現(xiàn)原理技巧
在這篇文章中,將介紹如何使用openpyxl來提取Excel中的圖片,以及它的原理和技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01Python中三元運(yùn)算符的簡潔性及多用途實(shí)例探究
這篇文章主要為大家介紹了Python中三元運(yùn)算符的簡潔性及多用途實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01python中的繼承機(jī)制super()函數(shù)詳解
這篇文章主要介紹了python中的繼承機(jī)制super()函數(shù)詳解,super 是用來解決多重繼承問題的,直接用類名調(diào)用父類方法在使用單繼承的時(shí)候沒問題,但是如果使用多繼承,會(huì)涉及到查找順序、重復(fù)調(diào)用等問題,需要的朋友可以參考下2023-08-08