第八章 Django CSRF防护

第一章 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'
})
  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XMYX-0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值