Django防御csrf攻擊的實現(xiàn)方式(包括ajax請求)
csrf攻擊簡要說明
用戶A現(xiàn)在登錄了某銀行網(wǎng)站的官網(wǎng),瀏覽器記錄了該網(wǎng)站生成的記錄用戶信息的cookie,而后用戶A點擊了釣魚網(wǎng)站(該網(wǎng)站和銀行網(wǎng)站長的一樣),該釣魚網(wǎng)站內有js代碼向銀行官網(wǎng)發(fā)起轉賬請求,此時該請求會自動攜帶記錄用戶信息的cookie值去訪問銀行官網(wǎng)進行轉賬操作,銀行網(wǎng)站的后端若沒有設置csrf防御機制,僅僅驗證用戶是否登錄,就會認為該轉賬操作是該用戶的合法操作。如果銀行網(wǎng)站不僅驗證cookie,同時再驗證一個攻擊者無法獲取到的值,就能防御上述漏洞。
CsrfViewMiddleware中間件
Django防御csrf攻擊是通過 django.middleware.csrf.CsrfViewMiddleware 中間件實現(xiàn)的,當我們創(chuàng)建項目后,默認在 settings 文件內的 MIDDLEWARE 內已經開啟了該中間件
防御機理:驗證cookie中的 csrftoken 值和表單中 csrfmiddlewaretoken (或ajax請求頭中的X-CSRFToken參數(shù))的值,兩者里邊的secret是否相同(不是完整的token值是否相同)來進行防御(這個值攻擊者無法獲取到)。
原因:由于瀏覽器同源策略的限制,釣魚網(wǎng)站無法獲取銀行網(wǎng)站給用戶設置的cookie,也就無法獲取csrftoken值,自然就無法在form表單或ajax請求頭中偽造該值,而django是要驗證這兩個值的secret是否是一致的
非ajax請求實現(xiàn)
該方法即form標簽的action屬性直接提交表單數(shù)據(jù),當使用這種方式時,只需要在模板內使用 {% csrf_token %} (必須在form表單內) 即可實現(xiàn)防御,代碼如下:
<form method="post" action="/login/">
{% csrf_token %}
</form>注意:在視圖函數(shù)內return響應時,如果不是使用的render()函數(shù),則需要確保 RequestContext 被用于渲染模板(render函數(shù)是自帶上下文管理器的),否則 {% csrf_token %} 無法正常工作
{% csrf_token %} 作用:
1、在form表單內生成一個input標簽,內容為
<input type="hidden" name="csrfmiddlewaretoken" value="jojovCfhvZhhqhbVf82BkzOA9EGIgPheBt3H0obOxygwCp4NHxcZ1tjhBbPl62DE">
2、在響應中設置cookie csrftoken=dArgJ7X1ygDM2lyau37guxRkwDR1lbd3vFbzeTTyAPC1etr2WshEbrm1Ya0Ebozt
ajax請求實現(xiàn)
現(xiàn)實開發(fā)中,我們可能更多的使用ajax的方式進行請求。
ajax的方式需要在請求頭上設置 X-CSRFToken 參數(shù)來記錄csrftoken的值,因此問題變成了如何獲取csrftoken的值。
django提供了幾種方式來獲取該值,首先要區(qū)分 CSRF_USE_SESSIONS 是否開啟,該參數(shù)表示是否將CSRF token 存儲在session中而不是cookie中,一般情況下無需修改此值,默認是False。
django官方例子是從cookie中獲取csrftoken。
CSRF_USE_SESSIONS 為False的情況
在此情形下,有兩種方式來確保響應的cookie中有 csrftoken 這個值
1、在模板中添加 {% csrf_token %}(任意位置) ,則響應的cookie中自然會有csrftoken
2、模板中沒有添加 {% csrf_token %} ,那么在視圖函數(shù)上添加裝飾器 `ensure_csrf_cookie`就會強制在響應頭中添加 csrftoken這個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,如果模板中設置了 {% 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請求中設置token
代碼如下:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// 在jquery的每個請求發(fā)起前設置X-CSRFToken
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});以上步驟總結起來就是:
1、獲取csrftoken值(從cookie或dom中獲取,給cookie中設置該值的兩種方法)
2、在ajax請求頭中設置 X-CSRFToken 記錄csrftoken值
3、發(fā)起ajax請求即可
前后端完全分離開發(fā)
在此種情形下,html頁面不由django渲染返回(html頁面作為靜態(tài)文件處理,所有數(shù)據(jù)均為異步加載),因此無法通過上述幾種方式來獲取csrftoken的值
通過了解 CsrfViewMiddleware 可知,為form表單生成csrftoken是通過 get_token(request) 函數(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設置到cookie中
});
// 將csrftoken設置到ajax的請求頭上
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// 在jquery的每個請求發(fā)起前設置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等頁面),此時仍然需要確保{% csrf_token %}能夠起作用,使用requires_csrf_token裝飾器
參考鏈接:https://docs.djangoproject.com/zh-hans/2.0/ref/csrf/
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
win10系統(tǒng)配置GPU版本Pytorch的詳細教程
這篇文章主要介紹了win10系統(tǒng)配置GPU版本Pytorch,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04
Python進程multiprocessing.Process()的使用解讀
這篇文章主要介紹了Python進程multiprocessing.Process()的使用,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02
python openpyxl提取Excel圖片實現(xiàn)原理技巧
在這篇文章中,將介紹如何使用openpyxl來提取Excel中的圖片,以及它的原理和技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01

