Django解決跨站請求僞造CSRF ---- 第16章

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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章