csrf跨站請求偽造
釣魚網站,搭建一個網站與正規網站一模一樣的介面,使用者進入到我們的網站中,給指定使用者轉賬,匯款確實提交到銀行,但是收款人確實我們自己定義的人。
本質在釣魚網站的頁面針對對方賬戶,只給使用者提供一個沒有name屬性的普通input框,在內部則隱藏一個已經寫好的name和value的input框
程式碼舉例:
<form action="" method="post" >
<p>轉賬使用者: <input type="text" name="name"></p>
<p>被轉賬使用者: <input type="text"></p>
<input type="text" name="user" value="junjie" style="display: none;">
<p>金額: <input type="text" name="money"></p>
<input type="submit" value="確定">
</form>
後端:
def transfer(request):
if request.method == 'POST':
print(f'{request.POST.get("name")}向{request.POST.get("user")}轉戰{request.POST.get("money")}')
print(request.POST)
return render(request,'transfer.html')
前端使用者輸入被轉賬使用者名稱稱無name屬性,使用自定義,所以前端不管給誰轉賬都是給junjie轉賬
如何規避上述問題?
csrf跨站請求偽造校驗
網站再給使用者返回一個具有提交資料功能頁面的時候會給這個頁面加一個唯一標識,當這個頁面朝後端傳送post請求的時候,後端先校驗唯一標識,如果唯一標識不對直接拒絕(403 forbbiden),成功則正常執行
針對form表單
<form action="" method="post">
{% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>target_user:<input type="text" name="target_user"></p>
<p>money:<input type="text" name="money"></p>
<input type="submit">
</form>
針對ajax設定固定的靜態檔案
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');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
將下面的檔案配置到你的Django專案的靜態檔案中,在html頁面上透過匯入該檔案即可自動幫我們解決ajax提交post資料時校驗csrf_token的問題,(匯入該配置檔案之前,需要先匯入jQuery,因為這個配置檔案內的內容是基於jQuery來實現的)
更多細節詳見:Djagno官方文件中關於CSRF的內容
# ajax如何符合校驗
// 第一種 利用標籤查詢獲取頁面上的隨機字串
{#data:{"username":'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},#}
// 第二種 利用模版語法提供的快捷書寫
{#data:{"username":'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},#}
// 第三種 通用方式直接複製js程式碼並應用到自己的html頁面上即可
data:{"username":'jason'}
csrf相關裝飾器
現在提出連個需求:
網站整體不校驗csrf,就單單幾個檢視函式需要校驗
網站整體校驗csrf,就單單幾個檢視函式不校驗
針對這兩種情況Django內部有兩個裝飾器
from django.views.decorators.csrf import csrf_protect,csrf_exempt
以下為FBV
反之整個網站都不需要校驗,只有裝飾器裝飾的函式需要校驗。
以下為CBV
url(r'^csrf'/,views.MyCsrfToken.as_view())
from django.views import View
# @method_decorator(csrf_protect,name='post') # 針對csrf_protect 第二種方式可以
# @method_decorator(csrf_exempt,name='post') # 針對csrf_exempt 第二種方式不可以
@method_decorator(csrf_exempt,name='dispatch')
class MyCsrfToken(View):
# @method_decorator(csrf_protect) # 針對csrf_protect 第三種方式可以
# @method_decorator(csrf_exempt) # 針對csrf_exempt 第三種方式可以
def dispatch(self, request, *args, **kwargs): V
return super(MyCsrfToken, self).dispatch(request,*args,**kwargs)
def get(self,request):
return HttpResponse('get')
# @method_decorator(csrf_protect) # 針對csrf_protect 第一種方式可以
# @method_decorator(csrf_exempt) # 針對csrf_exempt 第一種方式不可以
def post(self,request):
return HttpResponse('post')