第八章 Django CSRF防护

发布时间:2023年12月18日

第一章 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'
})
文章来源:https://blog.csdn.net/weixin_42434700/article/details/135003730
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。