什麼是CSRF跨站請求偽造?(from表單效驗csrf-ajdax效驗csrf-Ajax設定csrf-CBV裝飾器驗證csrf)

[oJbK]發表於2022-03-20

一:csrf跨站請求偽造

1.什麼是CSRF?
CSRF跨站請求偽造。也被稱為One Click Attack和Session Riding,通常縮寫為CSRF或XSRF。如果從名字你還不知道它表示什麼,你可以這樣理解:攻擊者(黑客,釣魚網站)盜用了你的身份,以你的名義傳送惡意請求,這些請求包括髮送郵件,傳送訊息,盜取賬號,購買商品,銀行轉賬,從而使你的個人隱私洩露和財產損失。
2.CSRF攻擊案例(釣魚網站)
1.我搭建一個跟正規網站一模一樣的介面(中國銀行)
2.使用者不小心進入到了我們的網站,使用者給某個人打錢
3.打錢的操作確確實實是提交給了中國銀行的系統,使用者的錢也確確實實減少了
4.但是唯一不同的是打錢的賬戶不是使用者想要打的賬戶變成了一個莫名其妙的賬戶

image

3.釣魚網站 內部原理
1.我們在釣魚網站的頁面,針對對方賬戶,只給使用者提供一個沒有name屬性的普通input框
2.然後我們在內部隱藏一個已經寫好name和value和input框

image

  • 內部程式碼
正規銀行

# transfer.html
<h1>我是正兒八經的網站</h1>
<form action="" method="post">
    <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>
# views
def transfer(request):
    if request.method == 'POST'
        username = request.POST.:get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print('%s給%s轉了%s元' % (username, target_user, money))
    return render(request, 'transfer.html')

不正規釣魚網站

# transfer.html
<h1>我是釣魚網站</h1>
<form action="http://127.0.0.1:8000/transfer/" method="post">
    <p>username: <input type="text" name="username"></p>
    <p>target_user: <input type="text"></p>
    {# 隱藏 #}
    <input type="text" name="target_user" value="jason" style="display: none">

    <p>money: <input type="text" name="money"></p>
    <input type="submit">
</form>
        
def transfer(request):
    return render(request, 'transfer.html')                
4.CSRF原理(釣魚網站內部本質)

image

5.從上圖可以看出,要完成一次CSRF攻擊,受害者必須依次完成以下兩個步驟:
  • 登陸受信任網站A,並在本地生成Cookie
  • 在不登出A的情況下,訪問危險網站B。

看到這裡,你也許會問:“如果我不滿足以上兩個條件中的一個,我就不會受到CSRF攻擊”。是滴,確實如此,但是你不能保證以下情況不會發生:

  • 你不能保證你登陸了一個網站之後,不再開啟一個tab頁面並訪問其他的網站(黃色網站)
  • 你不能保證你關閉瀏覽器之後,你本地的Cookie立刻過期,你上傳的會話已經結束
  • 上述中所謂的攻擊網站,可能就是一個釣魚網站或者黃色網站
6.CSRF如何防禦規避上述問題
# csrf跨站請求偽造效驗
1.網站在給使用者返回一個具有提交資料功能頁面的時候會給這個頁面加一個唯一標識。
2.當這個頁面朝後端傳送post請求的時候,我的後端會先效驗唯一標識,如果唯一標識不對直接拒絕(403 forbbiden),如果成功則正常執行。

二:如何符合校驗

1.form表單如何符合校驗
# 1.開啟settings內中介軟體開啟

<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>

image

三ajax如何符合校驗

1.第一種 利用標籤查詢獲取頁面上的隨機字串
						// name對應的值          拿到標籤對應的值	
data:{"username":'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},
2.第二種 利用模版語法提供的快捷書寫
data:{"username":'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},
3.第三種 通用方式(直接拷貝js程式碼並應用到自己的html頁面上即可)
  • 不需要寫模板語法,只需要引入一個靜態檔案內js檔案即可
data:{"username":'jason'},
4.配置靜態檔案settings.py
STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

transfer.html

<button id="d1">ajax請求</button>

{# 動態解析 靜態檔案#}
{% load static %}
<script src="{% static 'js/mysetup.js' %}"></script>

<script>
    $('#d1').click(function () {
        $.ajax({
            url:'',  // 不寫預設提交地址當前
            type:'post',  // 請求
            // 第一種
            {#data:{"username":"jason", 'csrfmiddlewaretoken':$('[name=csrfmiddlewretoken]').val()},#}
            // 第二種
            {#data:{"username":'jason', 'csrfmiddlewaretoken':'{{ csrf_token }}'},#}
            data:{"username":'jason'},  // 資料
            success:function () {
            }
        })
    })
</script>

image

四:Ajax請求設定csrf_token

不論是ajax還是誰,只要是向我Django提交post請求的資料,都必須校驗csrf_token來防偽跨站請求

將下面的檔案配置到你的Django專案的靜態檔案中,在html頁面上通過匯入該檔案即可自動幫我們解決ajax提交post資料時校驗csrf_token的問題,(匯入該配置檔案之前,需要先匯入jQuery,因為這個配置檔案內的內容是基於jQuery來實現的)

更多細節詳見:Djagno官方文件中關於CSRF的內容

getCookie方法:

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');

使用$.ajaxSetup()方法為ajax請求統一設定

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);
    }
  }
});

五:csrf相關裝飾器

1.需求:
1.網站整體都不校驗csrf/配置檔案中介軟體註釋掉,就單單幾個檢視函式需要校驗
2.網站整體都校驗csrf/配置檔案中介軟體不註釋,就單單幾個檢視函式不校驗
2.Django內部自帶裝飾器
from django.views.decorators.csrf import csrf_protect,csrf_exempt


csrf_protect  需要校驗
    針對csrf_protect符合我們之前所學的裝飾器的三種玩法
    
csrf_exempt   忽視校驗
    針對csrf_exempt只能給dispatch方法加才有效
3.開始進行驗證Django自帶裝飾器

1.註釋掉給前端頁面form表單新增的唯一標識{% csrf_token %}

4.整個網站都不校驗csfr,就該單單一個檢視需要校驗
  • 註釋掉sessings中介軟體第四個
# @csrf_protect
def transfer(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print('%s給%s轉了%s元'%(username,target_user,money))
    return render(request,'transfer.html')
![image](https://img2022.cnblogs.com/blog/2608805/202203/2608805-20220320233810099-1805901369.png)

![image](uploading...)

###### 5.整個網站都效驗csfr,就單單一個檢視函式不校驗

* settings設定第四個中介軟體開啟

```python
# @csrf_exempt
def transfer(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print('%s給%s轉了%s元'%(username,target_user,money))
    return render(request,'transfer.html')

image

六:CBV裝飾器

1.驗證csrf_protect時 註釋掉settings配置中介軟體第四個 (post與get都不需要效驗)
2.驗證csrf_exempt時 開啟settings配置中介軟體第四個 (post與get都需要效驗)
from django.utils.decorators import method_decorator

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):
        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')
3.總結CBV裝飾器
1.針對csrf_exempt只能加在dispatch才能生效
2.以下兩種是等價的

# 1.指名道姓給dispatch加csrf_exempt
@method_decorator(csrf_exempt,name='dispatch')
# 2.直接給dispatch加csrf_exempt裝飾器
@method_decorator(csrf_exempt)  # 針對csrf_exempt 第三種方式可以
    def dispatch(self, request, *args, **kwargs):
        return super(MyCsrfToken, self).dispatch(request,*args,**kwargs)

相關文章