CSRF
跨站請求偽造(CSRF)與跨站請求腳本正好相反寥粹。跨站請求腳本的問題在于埃元,客戶端信任服務(wù)器端發(fā)送的數(shù)據(jù)涝涤。跨站請求偽造的問題在于岛杀,服務(wù)器信任來自客戶端的數(shù)據(jù)阔拳。
無CSRF時存在的隱患
跨站請求偽造是指攻擊者通過HTTP請求江數(shù)據(jù)傳送到服務(wù)器,從而盜取回話的cookie类嗤。盜取回話cookie之后糊肠,攻擊者不僅可以獲取用戶的信息,還可以修改該cookie關(guān)聯(lián)的賬戶信息遗锣。
CSRF在Django中應(yīng)用
django為用戶實現(xiàn)防止跨站請求偽造的功能货裹,通過中間件django.middleware.csrf.CsrfViewMiddleware 來完成。使用前需要在配置文件 settings.py 中開啟黄伊。而對于django中設(shè)置防跨站請求偽造功能有分為全局和局部。
全局:
中間件 django.middleware.csrf.CsrfViewMiddleware
局部:
@csrf_protect派殷,為當前函數(shù)強制設(shè)置防跨站請求偽造功能还最,即便settings中沒有設(shè)置全局中間件。
@csrf_exempt毡惜,取消當前函數(shù)防跨站請求偽造功能拓轻,即便settings中設(shè)置了全局中間件。
注意:from django.views.decorators.csrf import csrf_exempt,csrf_protect
- 處理csrf四種方法
django 第一次響應(yīng)來自某個客戶端的請求時(get方式)经伙,會在服務(wù)器端隨機生成一個 token扶叉,然后把這個 token 寫在用戶請求的 cookie 里勿锅,同時也會給客戶端頁面發(fā)送一個隨機的 token (form表單中以{% csrf_token %}方式獲取)用以認證。之后客戶端每次以 POST 方式向服務(wù)端提交請求時枣氧,都會帶上這個 token溢十,這樣就能避免被 CSRF 攻擊。
方式:
1达吞、在返回的 HTTP 響應(yīng)的 cookie 里张弛,django 會為你添加一個 csrftoken 字段,其值為一個自動生成的 token酪劫;
2吞鸭、在所有的 POST 表單中,必須包含一個 csrfmiddlewaretoken 字段覆糟;
3刻剥、在處理 POST 請求之前,django 會驗證這個請求的 cookie 里的 csrftoken 字段的值和提交的表單里的 csrfmiddlewaretoken 字段的值是否一樣滩字。如果一樣造虏,則表明這是一個合法的請求;否則踢械,這個請求可能是來自于別人的 csrf 攻擊酗电,返回 403 Forbidden。
4内列、在所有 ajax POST 請求里撵术,添加一個 X-CSRFTOKEN header,其值為 cookie 里的 csrftoken 的值话瞧。
- Form提交(CSRF)
那么在Django中CSRF驗證大體是一個什么樣的原理呢嫩与?下面通過一個小例子來簡單說明一下:
1、先在settings.py配置文件中開啟中間件:
MIDDLEWARE = [ 'django.middleware.csrf.CsrfViewMiddleware', ]
2交排、在app01的views.py中寫一個簡單的后臺:
def csrf1(request):
if request.method == 'GET':
return render(request, 'csrf1.html')
elif request.method == 'POST':
return HttpResponse('CSRF 驗證通過划滋!')
3、在 csrf1.html 模板中寫入:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSRF</title>
</head>
<body>
<form method="POST" action="/csrf1.html">
<input type="text" name="test" />
<input type="submit" value="提交"/>
</form>
</body>
</html>
4埃篓、啟動Django服務(wù)后处坪,進入頁面顯示內(nèi)容:
那么如果這個時候,我們點擊登陸提交架专,django會因為無法通過csrf驗證返回一個403:
5同窘、而csrf驗證其實是對http請求中一段隨機字符串的驗證,那么這段隨機字符串從何而來呢部脚?這個時候我們嘗試把csrf1.html做一個修改添加一句
{% csrf_token %}:
直接在form表單中添加:
{% csrf_token %} -----> 轉(zhuǎn)換成一個hidden屬性的input標簽
{{ csrf_token }} -----> 直接獲取csrf的隨機字符串
注意:本地的cookies中也會添加隨機字符串 ---> 注意key名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSRF</title>
</head>
<body>
<form method="POST" action="/csrf1.html">
{% csrf_token %}
<input type="text" name="test" />
<input type="submit" value="提交"/>
</form>
</body>
</html>
6想邦、添加 {%csrf_token%} 之后,我們再通過瀏覽器元素審查委刘,就會發(fā)現(xiàn)一段新的代碼:
并且 cookies 中也攜帶了 csrf_token 隨機碼數(shù)據(jù):
Django在html中創(chuàng)建一個基于input框value值的隨機字符串 丧没,當再次提交數(shù)據(jù)時鹰椒,就會通過 csrf 驗證機制:
CSRF驗證通過!
這就是csrf的基本原理呕童,如果沒有這樣一段隨機字符串做驗證漆际,我們只要在另一個站點,寫一個表單拉庵,提交到這個地址下灿椅,是一樣可以發(fā)送數(shù)據(jù)的,這樣就造成了極大的安全隱患钞支。而我們新添加的csrf_token就是在我們自己的站點中茫蛹,設(shè)置的隱藏參數(shù),用來進行csrf驗證烁挟。
全站禁用
整個框架不使用csrf安全機制婴洼,直接在settings.py文件中注銷,整個網(wǎng)站都不再應(yīng)用撼嗓。
'django.middleware.csrf.CsrfViewMiddleware',
局部禁用:全局使用柬采,但是某些函數(shù)不需要應(yīng)用
settings.py文件中不注銷:
MIDDLEWARE = [ 'django.middleware.csrf.CsrfViewMiddleware', ]
在項目的views.py函數(shù)中導(dǎo)入模塊,給函數(shù)或是類加上對應(yīng)方法的裝飾器:
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt #不再做檢測且警!其他沒加裝飾器的函數(shù)還是會檢測
def csrf1(request):
if request.method == 'GET':
return render(request,'csrf1.html')
elif request.method == 'POST':
return HttpResponse('CSRF 驗證通過粉捻!')
局部使用:全局不使用,但是某些函數(shù)需要應(yīng)用
settings.py文件中注銷:
MIDDLEWARE = [ # 'django.middleware.csrf.CsrfViewMiddleware', ]
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_protect #全站不用斑芜,某個函數(shù)需要使用認證
def csrf1(request):
if request.method == 'GET':
return render(request,'csrf1.html')
elif request.method == 'POST':
return HttpResponse('CSRF 驗證通過肩刃!')
特殊CBV:在CBV應(yīng)用中
django 不認給類內(nèi)的函數(shù)名上添加裝飾器,只能是在類上添加杏头。
同時django限制若是應(yīng)用裝飾器盈包,必須用它的方法去添加,同時添加的語法格式也有限制醇王。
from django.views import View
from django.utils.decorators import method_decorator #必須使用這個方法
"""
語法:@method_decorator(裝飾器函數(shù)名稱或方法,name='被裝飾的函數(shù)名')
#先導(dǎo)入方法呢燥,然后裝飾器以參數(shù)的形式添加,其次指定要添加這個方法的函數(shù)名<樣式:name="函數(shù)名">
"""
@method_decorator(csrf_protect,name='dispatch')
class Foo(View):
"""請求來了,都是先執(zhí)行類View的內(nèi)置函數(shù)dispatch寓娩,然后再映射到對應(yīng)的方法上叛氨!
所以給dispatch添加上就相當于給所有的方法添加了"""
def get(self,request):
pass
def post(self,request):
pass
''' CBV中應(yīng)用裝飾器 '''
#自定義的裝飾器
def wrapper(func):
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
# 1. 指定方法上添加裝飾器
class Foo(View):
@method_decorator(wrapper) #先導(dǎo)入方法,然后裝飾器以參數(shù)的形式添加
def get(self,request):
pass
def post(self,request):
pass
# 2. 在類上添加
@method_decorator(wrapper,name='get') # name=方法名棘伴,寫那個方法就是給誰加
@method_decorator(wrapper,name='post') # name=方法名寞埠,寫那個方法就是給誰加
class Foo(View):
def get(self,request):
pass
def post(self,request):
pass
Ajax 中POST方式提交時候,放置在data中攜帶CSRF
<form method="POST" action="/csrf1.html">
{% csrf_token %}
<input id="user" type="text" name="user" />
<input type="submit" value="提交"/>
<a onclick="submitForm();">Ajax提交</a>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
function submitForm(){
var csrf = $('input[name="csrfmiddlewaretoken"]').val();
var user = $('#user').val();
$.ajax({
url: '/csrf1.html',
type: 'POST',
data: { "user":user,'csrfmiddlewaretoken': csrf},
"""注意csrf隨機字符串排嫌,后臺有固定的名字接收,這種方式是通過標簽獲取的對應(yīng)值"""
success:function(arg){
console.log(arg); }
})
}
</script>
AJAX 中POST方式提交畸裳,信息放在請求頭中缰犁,從cookies中獲取的csrf隨機字符串
jquery.cookie.js官網(wǎng)下載地址:https://plugins.jquery.com/cookie/
其它下載方式:https://www.jq22.com/jquery-info122
<form method="POST" action="/csrf1.html">
{% csrf_token %}
<input id="user" type="text" name="user" />
<input type="submit" value="提交"/>
<a onclick="submitForm();">Ajax提交</a>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script> <!--# 插件:幫助我們自動分割cookie-->
<script>
function submitForm(){
var token = $.cookie('csrftoken'); # 獲取cookie值
var user = $('#user').val();
$.ajax({
url: '/csrf1.html',
type: 'POST',
headers:{'X-CSRFToken': token}, #注意: X-CSRFToken 是Django規(guī)定寫法
data: { "user":user},
success:function(arg){
console.log(arg); }
})
}
</script>
django csrf 注意事項
注意:ajax POST提交的時候淳地,csrf-token 隨機字符串直接放在data數(shù)據(jù)中的方式為:data:{csrfmiddlewaretoken:"{{ csrf_token }}"}怖糊;
若是導(dǎo)入自己寫的JS文件,那上述方法就不能獲取到Django后臺發(fā)送的隨機字符串颇象,而是需要利用上面介紹的兩種方式獲取(頁面寫上{% csrf_token %}伍伤,通過隱藏的input標簽取value值寫在 POST 提交的data數(shù)據(jù)中;或是從cookie中獲取遣钳,寫在頭文件中扰魂。)
使用django框架時:
- 每次初始化一個項目時都要看看 django.middleware.csrf.CsrfViewMiddleware 這個中間件;
- 每次在模板里寫 form 時都需要加一個 {% csrf_token %} tag 蕴茴;
- 每次發(fā) ajax POST 請求劝评,都需要加一個 X_CSRFTOKEN 的 head ;