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 存儲(chǔ)在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-02
win10系統(tǒng)配置GPU版本Pytorch的詳細(xì)教程
這篇文章主要介紹了win10系統(tǒng)配置GPU版本Pytorch,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
pandas按某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-12
Python進(jìn)程multiprocessing.Process()的使用解讀
這篇文章主要介紹了Python進(jìn)程multiprocessing.Process()的使用,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02
python openpyxl提取Excel圖片實(shí)現(xiàn)原理技巧
在這篇文章中,將介紹如何使用openpyxl來提取Excel中的圖片,以及它的原理和技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
Python中三元運(yùn)算符的簡潔性及多用途實(shí)例探究
這篇文章主要為大家介紹了Python中三元運(yùn)算符的簡潔性及多用途實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
python中的繼承機(jī)制super()函數(shù)詳解
這篇文章主要介紹了python中的繼承機(jī)制super()函數(shù)詳解,super 是用來解決多重繼承問題的,直接用類名調(diào)用父類方法在使用單繼承的時(shí)候沒問題,但是如果使用多繼承,會(huì)涉及到查找順序、重復(fù)調(diào)用等問題,需要的朋友可以參考下2023-08-08

