第一章 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() 时,都会随机生成一个掩码,因此表单字段的值每次都不同。
准备工作
python
# 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',
]
方法一
路由
python
# 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'),
]
视图
python
# 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')
网页
html
<!-- 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>
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 %}
增加这一行
html
<!-- 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也可以跳过防护
python
# 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')
方法三
这块不太清楚,也没有测试过,后续要是使用了,在进行更新
python
方法2:
var csrf_token = $("[name='csrfmiddlewaretoken']").val();
var data = {'id': '123', 'csrfmiddlewaretoken': csrf_token};
$.ajax({
type: "POST",
url: "/api",
data: data,
dataType: 'json'
})