csrf跨站請求偽造、csrf相關裝飾器、auth認證模組、基於django中間件設計項目功能

  • 2022 年 9 月 13 日
  • 筆記

csrf跨站請求網站

什麼是csrf跨站請求網站

簡單的來說就是攻擊者通過一些技術手段欺騙用戶的瀏覽器去訪問一個自己曾經認證過的網站並執行一些操作,由於瀏覽器曾經認證過,所以被訪問的網站會認為是真正的用戶操作而去執行

經典例子-釣魚網站

釣魚網站:假設是一個跟銀行一模一樣的網站頁面,用戶在該頁面上轉賬
賬戶的錢會減少,但是受益人卻不是自己想要轉賬的那個人

模擬

一台電腦上倆個服務端不同埠啟動,釣魚網站提交地址改為正規網站的地址
真正網站的程式碼

前端:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="//cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
    <link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
    <script src="//stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h2>這是真正的網站</h2>
<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>
</body>
</html>

後端:
from django.shortcuts import render

# Create your views here.
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(f'{username}給{target_user}轉了{money}')
    return render(request, 'transfer.html')

路由:
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('transfer/', views.transfer),
]

釣魚網站的程式碼

前端:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="//cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
    <link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
    <script src="//stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h2>這是釣魚網站</h2>
<form action="//127.0.0.1:8000/transfer/" method="post">
    <p>username:
        <input type="text" name="username">
    </p>
    <p>target_user:
        <input type="text">
        <input type="text" name="target_user" value="小笨蛋" style="display: none">

    </p>
    <p>money:
        <input type="text" name="money">
    </p>
    <input type="submit">
</form>
</body>
</html>

後端:
from django.shortcuts import render

# Create your views here.
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(f'{username}給{target_user}轉了{money}')
    return render(request, 'transfer.html')

路由:
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('transfer/', views.transfer),
]

釣魚網站的實現效果
image

如何避免這種現象(預防)

image
csrf策略:通過在返回的頁面上添加獨一無二的標識資訊從而區分正規網站和釣魚網站的請求

如何在django中解決這個問題

image

form表單

<form action="" method="post">
    	{% csrf_token %}
</form>

image

image

ajax

image

方式1:先編寫csrf模板語法 然後利用標籤查找和值獲取 手動添加
      'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()
方式2:直接利用模板語法即可
      'csrfmiddlewaretoken':'{{ csrf_token }}'
方式3:通用方式(js腳本)
      兼容性最好,擴展性最高,前後端分離
方式3的js程式碼,直接copy就可以
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);
    }
  }
});

csrf相關裝飾器

1.當整個網站默認都不校驗csrf 但是局部視圖函數需要校驗 如何處理

2.當整個網站默認都校驗csrf 但是局部視圖函數不需要校驗 如何處理

FBV

from django.views.decorators.csrf import csrf_protect,csrf_exempt
"""
csrf_protect 校驗csrf
csrf_exempt  不校驗csrf
"""
# @csrf_protect  # 全局不校驗的時候,想叫誰校驗就給誰安這個
@csrf_exempt  # 全局都校驗,只有這個不校驗就添加這個
def home(request):
    return HttpResponse('不在原地踏步')

CBV

針對CBV不能直接在方法上添加裝飾器 需要藉助於專門添加裝飾器的方法

from django.utils.decorators import method_decorator

# @method_decorator(csrf_protect, name='post')  # 方式2:指名道姓的添加
class MyHome(views.View):
    @method_decorator(csrf_protect)  # 方式3:影響類中所有的方法
    def dispatch(self, request, *args, **kwargs):
        super(MyHome, self).dispatch(request, *args, **kwargs)
	'''
	當路由匹配成功之後,才會執行dispatch方法,然後它會去找視圖函數,所以給dispatch裝上就·相當於給所有的方法都裝上了裝飾器
	'''
    def get(self, request):
        return HttpResponse('Home Get view')

    # @method_decorator(csrf_protect)  # 方式1:指名道姓的添加
    def post(self, request):
        return HttpResponse('Home Post view')
針對csrf_exempt只有方式3有效 針對其他裝飾器上述三種方式都有效

auth認證模組

auth模組模組是什麼

Auth模組是Django自帶的用戶認證模組

我們在開發一個網站的時候,無可避免的需要設計實現網站的用戶系統,django執行資料庫遷移命令之後會產生一個auth_user表,該表可以配合auth模組做用戶相關的功能:註冊 登錄 修改密碼 註銷 ...,該表還是django admin後台管理默認的表

Django作為一個完美主義者的終極框架,當然也會想到用戶的這些痛點,它內置了強大的用戶認證系統———Auth模組

使用admin後台管理

創建了超級管理員

image

管理員登錄成功
image

這個管理員功能是非常強大的,可以直接對錶進行增刪改查、修改數據等,並且頁面也挺好看
image

增加表中數據
image

Auth模組方法編寫登錄、註冊功能

auth.authenticate(request, username=username, password=password)返回的是一個對象

# res = auth.authenticate(request, username=username, password=password)
# print(res)
# print(res.username)  # jason
# print(res.password)  # pbkdf2_sha256$150000$wymiPvzXEUIH$vn6XTMN/YWwiGa98WScB/Hs5MfPtB5br+JCM3oWBtwk=
# print(res.pk)  # 2
is_user_obj = auth.authenticate(request, username=username, password=password)
        """
        數據存在的時候返回的是一個數據對象
        數據不存在的時候返回的是一個None
        """

用戶登錄時候與用戶沒有登錄(request.user)

print(request.user)
"""
    當用戶登錄成功之後(執行了auth.login) 該方法返回當前登錄用戶對象(admin)
    當用戶沒有登錄成功(沒有執行了auth.login) 該方法返回匿名用戶對象(AnonymousUser)
"""

判斷當前用戶是否登錄 (request.user.is_authenticated)

校驗用戶是否登錄

導入模組
from django.contrib import auth

from django.contrib.auth.decorators import login_requilightSeagreen

局部校驗

校驗用戶是否登錄, 默認跳轉登錄地址比較複雜, 我們可以自定義跳轉登錄的地址
image

@login_requilightSeagreen(login_url='/login/') 它會自動跳轉到login

image

全局校驗

在settings.py中配置: LOGIN_URL = '/login/'

註冊功能

image

def register(request):
    User.objects.create_user(username='jerry', password=123)  # 創建普通用戶
    User.objects.create(username='jerry1', password=123)  # 創建普通用戶,密碼不能加密
    return HttpResponse('註冊功能')

image

def register(request):
    User.objects.create_superuser(username='oscar', password=123, email='[email protected]')  # 創建超級管理員
    return HttpResponse('註冊功能')

Auth模組常用的方法

導入Auth模組:from django.contrib import auth

1.authenticate()
    校驗用戶名和密碼是否正確
    auth.authenticate(request,username,password)
2.login
    用戶登錄
    auth.login(request, user_obj)
3.is_authecticated
    判斷用戶是否登錄
    request.user.is_authecticated
4.create_user、create_superuser
    創建用戶
    User.object.contrib.auth.models import User
    User.object.create_user(username, password)
    User.object.create_superUser(username, password, email)
5.check_password
    校驗密碼是否正確
    request.user.check_password(old_password)
6.set_password
    request.user.set_password(new_password)
    request.user.save()  # 必須要保存
7.logout
    auth.logout(request)
8.校驗用戶登錄裝飾器
    from django.contrib.auth.decorators import login_requilightSeagreen
    跳轉局部配置
       login_requilightSeagreen(login_url='/login/')
    跳轉全局配置
        LOGIN_URL = '/login/'
9.獲取登錄用戶對象
    request.user

auth_user表切換

我們還想使用auth模組的方法並且擴展auth_user表欄位

1.models.py
    from django.contrib.auth.models import AbstractUser  # auth_user表序列化的一個類

    class Userinfo(AbstractUser):
        '''擴展auth_user表中沒有的欄位'''
        phone = models.BigIntegerField()
        desc = models.TextField()
"""
寫一個類繼承AbstractUser這個類,那麼我寫的這個類就擁有了AbstractUser這個類裡面所有的欄位
"""

2.settings.py
    AUTH_USER_MODEL = 'app01.Userinfo'

基於django中間件設計項目功能