1. 基本應用
Django中通過生成隨機字符串的方式對客戶發送提交數據的請求(POST)進行驗證,基本原理:客戶端向服務端先發送GET請求,服務端會把隨機字符串寫到請求的頁面上,客戶端再來提交數據的時候需要帶上這個隨機字符串,服務端纔會處理請求。默認中間件的配置中SCRF是開啓的狀態:
settings.py:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', # CSRF
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
views.py:
def csrf(request):
if request.method == 'GET':
return render(request, 'csrf.html')
else:
return HttpResponse('ok')
Django中的 {% csrf_token %}
就是服務端生成的隨機字符串,如果在頁面上沒有加上它,那麼請求的時候就不會帶上這個隨機字符串:
csrf.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<input type="text">
<input type="submit">
</form>
</body>
</html>
在提交數據的時候會 403 Forbidden的錯誤:
如果在頁面上加上 {% csrf_token %}
,客戶端在發送GET請求後,服務端返回的頁面上就會顯示這個隨機字符串,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<input type="text">
<input type="hidden" name="csrfmiddlewaretoken" value="a7brzXos3mDRZlmdWyJcKs72MJ4WtvXaZfa8xWUJq7E3dvNhFrWk3gesKdup3TR2">
<input type="submit">
</form>
</body>
</html>
客戶端直接帶着這個隨機字符串提交提交數據即可,不會再包請求拒絕的問題。
{{ csrf_token }}會在頁面上直接顯示這個隨機字符串,而{% csrf_token %}會生成input標籤,這是有區別的。
2. 全站禁用
如果全站都不想使用CSRF的驗證,可以全局禁用處理裏CSRF,只需要在配置文件中的中間件部分註釋掉CSRF的過濾,
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
如果是這樣,客戶端提交數據請求時不會進行CSRF的處理。
3. 局部禁用
如果不想在某些頁面提交請求數據時做CSRF驗證,這個時候 django.middleware.csrf.CsrfViewMiddleware
中間件是(全局)開啓的狀態,那就可以通過 裝飾器
的方式單獨禁用這個頁面發送提交數據請求的CSRF的驗證,只需要 在視圖函數中加入csrf_exempt裝飾器
:
views.py:
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def csrf(request):
if request.method == 'GET':
return render(request, 'csrf.html')
else:
return HttpResponse('ok')
4. 局部使用
如果在全局禁用的狀態下,局部使用CSRF驗證。需要在視圖函數中加上 `` 裝飾器,表示在這個視圖函數對應的模板中提交數據的請求必須進行CSRF驗證:
views.py:
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def csrf(request):
if request.method == 'GET':
return render(request, 'csrf.html')
else:
return HttpResponse('ok')
如果在模板中沒有加上 {% csrf_token%}
同樣是請求被拒絕的:
加入csrf認證後服務端只接收來自本站提交數據的請求,別的站點發送的是不被接收的。所以,csrf驗證是可以防止別的站點發送提交數據的請求到本站。但是,csrf認證不是完全可以避免的,如過是其它站點獲取到了本站的csrf,帶着它去請求本站,服務端也會認爲是自己的站點發送的請求,會選擇接收請求的。所以,csrf認證只是在一定程度上保護了站點。
5. AJAX攜帶CSRF提交數據
如果AJAX不攜帶CSRF字符串提交數據,也會出現 403 Forbidde 報錯,所以 AJAX提交數據也是要攜帶CSRF字符串的
。AJAX攜帶CSRF提交數據的方法有兩種,一種是把頁面上的csrf隨機字符串放在 data
中傳到後端,另一種是放在 把csrf隨機字符串放在請求頭中
。首先探討第一種方法:
views.py:
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def csrf(request):
if request.method == 'GET':
return render(request, 'csrf.html')
else:
return HttpResponse('ok')
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<input type="text" id="user">
{% csrf_token %}
<input type="button" value="提交" id="submit">
</form>
</body>
<script src="/static/js/jquery-3.5.1.min.js"></script>
<script>
$(function () {
submitForm();
});
function submitForm() {
$('#submit').click(function () {
var user = $('#user').val();
var csrfmiddlewaretoken = $('input[name="csrfmiddlewaretoken"]').val()
$.ajax({
url: '/index.html/', //可以不寫,默認發送到本頁面對應的視圖函數中
type: 'post',
data: {
'user': user,
'csrfmiddlewaretoken': csrfmiddlewaretoken //csrfmiddlewaretoken也可以不獲取直接拿"{{csrf_token}}"
},
success: function (arg) {
alert(arg) //ok
}
})
})
}
</script>
</html>
第二種方法,把cookie放在請求頭中
。首先引入插件 jquery.cookie.js
,這個插件專門對cookie進行操作的,通過cokkie名可以直接獲取cookie的值,也還可以設置 cookie:
Django規定從 瀏覽器獲取的cookie
傳入到Django中要傳到 請求頭
中,要使用到headers:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<input type="text" id="user">
<input type="button" value="提交" id="submit">
</form>
</body>
<script src="/static/js/jquery-3.5.1.min.js"></script>
<script src="/static/js/jquery.cookie.js"></script>
<script>
$(function () {
submitForm();
});
function submitForm() {
$('#submit').click(function () {
var user = $('#user').val();
var token = $.cookie('csrftoken');
$.ajax({
url: '/csrf.html/', //可以不寫,默認發送到本頁面對應的視圖函數中
type: 'post',
headers:{'X-CSRFToken':token},
data: {
'user': user
},
success: function (arg) {
alert(arg) //ok
}
})
})
}
</script>
</html>