第八章 Django CSRF防护

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

第一章 Django 基本使用
第二章 Django URL路由系统
第三章 Django 视图系统
第四章 Django 模板系统
第五章 Django 数据模型系统(基本使用)
第六章 Django 数据模型系统(多表操作)
第七章 Django 用户认证与会话技术
第八章 Django CSRF防护


文章目录


CSRF介绍

CSRF全称为Cross Site Request Forgery中文名为跨站请求伪造是一种网络攻击方式。具体来说攻击者通过诱骗用户点击链接或执行操作从而在用户不知情的情况下以用户的身份发起恶意请求。

在Django框架中CSRF保护默认只针对POST请求也就是说Django默认对GET请求不做CSRF防御机制。这是因为GET请求通常不会导致数据的修改所以被认为是安全的。而POST、PUT和DELETE等请求可能会对服务器端的数据产生影响因此需要进行CSRF防护。

为了防止CSRF攻击Django提供了CSRF中间件该中间件默认在MIDDLEWARE配置中被激活。服务端响应时会分配一个随机字符串给客户端客户端第二次发送post,put或delete请求时携带上次分配的随机字符串到服务端进行校验。如果禁用了CSRF中间件并不推荐这样做因为这样会增加被CSRF攻击的风险。开发者可以根据需要选择使用csrf_protect()方法对特定视图进行保护。

工作原理

Django的CSRF保护工作原理基于以下几个步骤

  1. 服务端响应时会分配一个随机字符串给客户端这个字符串被称为CSRF token。
  2. 当用户提交表单特别是POST、PUT或DELETE请求时客户端需要携带这个CSRF token。
  3. Django校验器会检查提交的token是否与服务端生成的token匹配。如果不匹配则请求将被拒绝。
  4. CSRF cookie是一个随机的秘密值其他网站无法访问。每当用户登录时这个秘密值的值都会更改这增加了系统的安全性。
  5. 为了防止跨站请求伪造攻击Django中间件在每个响应中都使用一个名为’csrfmiddlewaretoken’的隐藏表单字段出现在所有发送的POST表单中。每次调用 get_token() 时都会随机生成一个掩码因此表单字段的值每次都不同。
  6. 出于安全原因这个字段的值不仅仅是秘密值它在每个响应中都使用一个掩码进行不同方式的混淆。
  7. 关于浏览器发出的request它会在request header的cookie中携带一个token同时在request的body中也携带一个token。然后Django server提取request header cookie中的token和request body中的token进行比较如果这两个Token相同那么请求就会被接受。

如何配置

Django的CSRF防护机制主要通过以下几种方式实现

  1. 中间件保护Django的CSRF中间件默认在MIDDLEWARE配置中被激活对POST、PUT和DELETE等请求进行CSRF防护。如果你修改了这个配置需要保证’django.middleware.csrf.CsrfViewMiddleware’中间件在任何其他处理CSRF的视图中间件之前。如果禁用了CSRF中间件是不安全的。
  2. CSRFToken机制Django使用CSRFToken机制来防止一个站点被另一个站点伪造数据提交的攻击。服务端响应时会分配一个随机字符串给客户端这个字符串被称为CSRF token。当用户提交表单特别是POST、PUT或DELETE请求时客户端需要携带这个CSRF token。Django校验器会检查提交的token是否与服务端生成的token匹配。如果不匹配则请求将被拒绝。
  3. csrf_protect()函数csrf_protect()函数可以手动用于一些特殊情况下的CSRF保护例如提交的数据不是由Django生成的或者使用了第三方库。该函数会在视图函数被调用前验证请求确保请求是合法的。如果请求中不包含有效的CSRF token那么会返回403错误。
  4. Cookie保护CSRF cookie是一个随机的秘密值其他网站无法访问。每当用户登录时这个秘密值的值都会更改这增加了系统的安全性。
  5. 隐藏表单字段为了防止跨站请求伪造攻击Django中间件在每个响应中都使用一个名为’csrfmiddlewaretoken’的隐藏表单字段出现在所有发送的POST表单中。每次调用 get_token() 时都会随机生成一个掩码因此表单字段的值每次都不同。

准备工作

# orm/setting.py
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',
]

image-20231205111759411

方法一

路由

# myorm/urls.py
from django.urls import include, path, re_path
from myorm import views
urlpatterns = [
    path('login/', views.login, name='login'),
    path('home_page/', views.home_page, name='home_page'),
    path('logout/', views.logout, name='logout'),
]

视图

# myorm/views.py
from django.shortcuts import render, get_object_or_404,redirect,HttpResponse
from .models import Course, Student
from django.contrib import auth
from django.contrib.auth.decorators import login_required


# 定义一个装饰器
def self_login_required(func):
    def inner(request):
        is_login = request.session.get('is_login', False)
        if not is_login:
            return redirect(login)
        else:
            return func(request)
    return inner

@self_login_required
def home_page(request):
    return render(request,"home_page.html")

def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    elif request.method == "POST":
        username = request.POST.get("username",None)
        password = request.POST.get("password",None)
        # 对用户数据验证
        user = User.objects.filter(user=username)
        if user:
            for i in user:
                passwd = i.password
            if password == passwd:
                # 验证通过后将request与用户对象包含session传给login()函数
                request.session['is_login'] =True
                request.session['username'] = username
                # 跳转到http://49.232.221.200:8080/myorm/home_page
                return redirect("home_page")
            else:
                mag = '用户名密码错误'
        else:
            mag = '用户名密码错误'
        return render(request, "login.html",{'mag': mag})

def logout(request):
    # 清除当前用户的session信息
    auth.logout(request)
    return redirect('login')

网页

<!-- myrom/templates/login.html -->
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
    </head>
    <body>
        <form action="" method="post">
            <h1>登录</h1>
            用户名<input type="text" name="username"><br>
            密码<input type="text" name="password"><br>
            <button type="submit">登录</button>
        </form><br>
        <span style="color: red;">{{ mag }}</span>
    </body>
</html>
<!-- myrom/templates/home_page.html -->
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>标题</title>
    </head>
    <body>
        
        <h1>欢迎访问首页</h1>
        <a href="/myorm/logout">退出</a>
    </body>
</html>

验证

http://49.232.221.200:8080/myorm/login/

输入账户名密码点击登录报错

image-20231205111719071

{% csrf_token %} 增加这一行

<!-- myrom/templates/login.html -->
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
    </head>
    <body>
        <form action="" method="post">
            {% csrf_token %} 
            <h1>登录</h1>
            用户名<input type="text" name="username"><br>
            密码<input type="text" name="password"><br>
            <button type="submit">登录</button>
        </form><br>
        <span style="color: red;">{{ mag }}</span>
    </body>
</html>

image-20231205111945870

可以进行登录检查源码

image-20231205112018124

方法二

视图

视图增加csrf_exempt也可以跳过防护

# myorm/views.py
from django.shortcuts import render, get_object_or_404,redirect,HttpResponse
from .models import Course, Student
from django.contrib import auth
from django.contrib.auth.decorators import login_required
from django.views.decorators.csrf import csrf_exempt


# 定义一个装饰器
def self_login_required(func):
    def inner(request):
        is_login = request.session.get('is_login', False)
        if not is_login:
            return redirect(login)
        else:
            return func(request)
    return inner

@self_login_required
def home_page(request):
    return render(request,"home_page.html")

### 在这一进行添加然后跳过
@csrf_exempt
def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    elif request.method == "POST":
        username = request.POST.get("username",None)
        password = request.POST.get("password",None)
        # 对用户数据验证
        user = User.objects.filter(user=username)
        if user:
            for i in user:
                passwd = i.password
            if password == passwd:
                # 验证通过后将request与用户对象包含session传给login()函数
                request.session['is_login'] =True
                request.session['username'] = username
                # 跳转到http://49.232.221.200:8080/myorm/home_page
                return redirect("home_page")
            else:
                mag = '用户名密码错误'
        else:
            mag = '用户名密码错误'
        return render(request, "login.html",{'mag': mag})

def logout(request):
    # 清除当前用户的session信息
    auth.logout(request)
    return redirect('login')

方法三

这块不太清楚也没有测试过后续要是使用了在进行更新

方法2
var csrf_token = $("[name='csrfmiddlewaretoken']").val();
var data = {'id': '123', 'csrfmiddlewaretoken': csrf_token};
$.ajax({
type: "POST",
url: "/api",
data: data,
dataType: 'json'
})
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6