第一章 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保护工作原理基于以下几个步骤:
- 服务端响应时会分配一个随机字符串给客户端,这个字符串被称为CSRF token。
- 当用户提交表单(特别是POST、PUT或DELETE请求)时,客户端需要携带这个CSRF token。
- Django校验器会检查提交的token是否与服务端生成的token匹配。如果不匹配,则请求将被拒绝。
- CSRF cookie是一个随机的秘密值,其他网站无法访问。每当用户登录时,这个秘密值的值都会更改,这增加了系统的安全性。
- 为了防止跨站请求伪造攻击,Django中间件在每个响应中都使用一个名为’csrfmiddlewaretoken’的隐藏表单字段,出现在所有发送的POST表单中。每次调用 get_token() 时,都会随机生成一个掩码,因此表单字段的值每次都不同。
- 出于安全原因,这个字段的值不仅仅是秘密值,它在每个响应中都使用一个掩码进行不同方式的混淆。
- 关于浏览器发出的request,它会在request header的cookie中携带一个token,同时在request的body中也携带一个token。然后Django server提取request header cookie中的token和request body中的token进行比较,如果这两个Token相同,那么请求就会被接受。
如何配置
Django的CSRF防护机制主要通过以下几种方式实现:
- 中间件保护:Django的CSRF中间件默认在MIDDLEWARE配置中被激活,对POST、PUT和DELETE等请求进行CSRF防护。如果你修改了这个配置,需要保证’django.middleware.csrf.CsrfViewMiddleware’中间件在任何其他处理CSRF的视图中间件之前。如果禁用了CSRF中间件,是不安全的。
- CSRFToken机制:Django使用CSRFToken机制来防止一个站点被另一个站点伪造数据提交的攻击。服务端响应时会分配一个随机字符串给客户端,这个字符串被称为CSRF token。当用户提交表单(特别是POST、PUT或DELETE请求)时,客户端需要携带这个CSRF token。Django校验器会检查提交的token是否与服务端生成的token匹配。如果不匹配,则请求将被拒绝。
- csrf_protect()函数:csrf_protect()函数可以手动用于一些特殊情况下的CSRF保护,例如提交的数据不是由Django生成的,或者使用了第三方库。该函数会在视图函数被调用前验证请求,确保请求是合法的。如果请求中不包含有效的CSRF token,那么会返回403错误。
- Cookie保护:CSRF cookie是一个随机的秘密值,其他网站无法访问。每当用户登录时,这个秘密值的值都会更改,这增加了系统的安全性。
- 隐藏表单字段:为了防止跨站请求伪造攻击,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',
]
方法一
路由
# 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/
输入账户名密码点击登录报错
{% 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>
可以进行登录,检查源码
方法二
视图
视图增加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'
})