csrf跨站请求伪造、auth认证模块
csrf跨站请求伪造
什么是csrf跨站请求伪造
简单的来说就是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作,由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行
经典案例>>>:钓鱼网站
钓鱼网站:假设是一个跟银行一模一样的网站页面,用户在该页面上转账
账户的钱会减少,但是收款人却不是自己想要转账的那个人
模拟
一台计算机上两个服务端不同端口启动,钓鱼网站提交地址改为正规网站的地址
真网站代码
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h2>这是真正的网站</h2>
<form action="" method="post">
<p>username:
<input type="text" name="username">
</p>
<p>target_user:
<input type="text" name="target_user">
</p>
<p>money:
<input type="text" name="money">
</p>
<input type="submit">
</form>
</body>
</html>
后端
from django.shortcuts import render
# Create your views here.
def transfer(request):
if request.method == 'POST':
username = request.POST.get('username')
target_user = request.POST.get(target_user)
money = request.POST.get('money')
print(f'{username}给{target_user}转了{money}元')
return render(request, 'tranfer.html')
路由
from django.contribfrom django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('transfer/', views.transfer),
]
钓鱼网站代码
前端
前端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h2>这是钓鱼网站</h2>
<form action="http://127.0.0.1:8000/transfer/" method="post">
<p>username:
<input type="text" name="username">
</p>
<!== 将被转账用户设置两个input标签,将被真正被转账的隐藏起来,让用户以为转的是正确的,实际上转的是钓鱼网站的账户上了==>
<p>target_user:
<input type="text">
<input type="text" name="target_user" value="黑客jason" style="display: none">
</p>
<p>money:
<input type="text" name="money">
</p>
<input type="submit">
</form>
</body>
</html>
后端
from django.shortcuts import render
# Create your views here.
def transfer(request):
if request.method == 'POST':
username = request.POST.get('username')
target_user = request.POST.get('target_user')
money = request.POST.get('money')
print(f'{username}给{target_user}转了{money}')
return render(request, 'transfer.html')
路由
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('transfer/', views.transfer),
]
除了前端被转账用户那里有些区别,后端,与路由都与真正的网站一模一样
钓鱼网站实现效果
如何避免这种现象
csrf策略:通过在返回页面上添加独一无二的标识信息从而区分正规网站和钓鱼网站的请求
如何在django中解决这个问题
form表单
<form action="" methon="post">
{% csrf_token %}
</form>
ajax
方式1:先编写csrf模板语法 然后利用标签查找和值获取,手动添加
'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()
方式2:直接利用模板语法即可
'csrfmiddlewaretoken':'{{ csrf_token }}'
方式3:通用方式(js脚本)
兼容性最好,扩展性最高,前后端分离
方式3的js代码,直接copy就可以
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
csrf相关装饰器
1.当整个网站默认都不校验csrf 但是局部视图函数需要校验 如何处理
2.当整个网站默认都校验csrf 但是局部视图函数不需要校验 如何处理
FBV
from django.views.decorators.csrf import csrf_protect,csrf_exempt
"""
csrf_protect 校验csrf
csrf_exempt 不校验csrf
"""
# @csrf_protect # 全局不校验的时候,想让谁校验就给谁带上这个
@csrf_exempt # 全局都校验,想要那个不校验就添加这个
def home(request):
return HttpResponse('不在原地踏步')
CBV
针对CBV不能直接在方法上添加装饰器 需要借助于专门添加装饰器的方法
from django.utils.decorators import method_decorator
from django.utils.decorators import method_decorator
# @method_decorator(csrf_protect, name='post') # 方式2:指名道姓的添加
class MyHome(views.View):
@method_decorator(csrf_protect) # 方式3:影响类中所有的方法
def dispatch(self, request, *args, **kwargs):
super(MyHome, self).dispatch(request, *args, **kwargs)
'''
当路由匹配成功之后,才会执行dispatch方法,然后它会去找视图函数,所以给dispatch装上就相当于给所有得到方法否装上了装饰器
'''
def get(self, request):
return HttpResponse('Home Get view')
# @method_decorator(csrf_protect) # 方式1:指名道姓的添加
def post(self, request):
return HttpResponse('Home Post view')
针对csrf_exempt只有方式3有效 针对其他装饰器上述三种方式都有效
auth认证模块
auth模块是什么
auth模块是django自带的用户认证模块
我们在开发一个网站,无可避免的需要设计实现网站的用户系统,django执行数据库迁移命令之后会产生一个auth_user表,该表可以配合auth模块做用户相关的功能:注册 登录 修改密码 注销...,该表还是django后台管理默认的表
django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点,它内置了强大的用户认证系统————auth模块
使用admin后台管理
创建了超级管理员
管理员登录成功
这个管理员功能是非常强大的,可以直接对表进行增删改查、修改数据等
增加表中数据
auth模块方法编写注册登录功能
auth.authenticate(request, username=username, password=password)返回的是一个对象
res = auth.authenticate(request, username=username, password=password)
print(res)
print(res.username) # jason
print(res.password) # pbkdf2_sha256$150000$wymiPvzXEUIH$vn6XTMN/YWwiGa98WScB/Hs5MfPtB5br+JCM3oWBtwk=
print(res.pk) # 2
is_user_obj = auth.authenticate(request, username=username, password=password)
"""
数据存在的时候返回的是一个数据对象
数据不存在的时候返回的是None
"""
用户登录时候与用户没有登录(request.user)
print(request.user)
"""
当用户登录成功之后(执行了auth.login) 该方法返回当前登录用户对象(admin)
当用户没有登录成功(没有执行auth_login) 该方法返回匿名用户对象(AnonymousUser)
"""
判断当前用户是否登录
request.user.is_authenticated
校验用户是否登录
导入模块
form django.contrib import auth
from django.contrib.auth.decorators import login_requiblack
局部校验
校验用户是否登录, 默认跳转登录地址比较复杂, 我们可以自定义跳转登录的地址
@login_requiblack(login_url='/login/') 它会自动跳转到login
全局校验
在settings.py中配置: LOGIN_URL = '/login/'
注册功能
def register(request):
User.object.creat_user(username='jerry', password=123) # 创建普通用户
User.object.creat(username='jerry1', password=123) # 创建普通用户,密码不能加密
return HttpResponse('注册功能')
def register(request):
User.objects.create_superuser(username='oscar', password=123, email='oscar@163.com') # 创建超级管理员
return HttpResponse('注册功能')
auth模块常用的方法
导入auth模块:from django.contrib import auth
1.authenticate()
校验用户名和密码是否正确
auth.authenticate(request, username, password)
2.login
用户登录
auth.login(request, user_obj)
3.is_authecticated
判断用户是否登录
request.user.is_authecticated
4.create_user、create_superuser
创建用户
User.object.contrib.auth.models import User
User.object.create_user(username, password)
User.object.create_superuser(username, password)
5.check_password
校验密码是否正确
request.user.check_password(old_password)
6.set_password
修改密码
request.user.set_password(新密码)
request.user.save() # 必须要保存
7.校验用户登录装饰器
from django.contrib.auth.decorators import login_requiblack
跳转局部配置
login_requiblack(login_url='/login/')
跳转全局配置
LOGIN_URL = '/login/'
9.获取登录用户对象
request.user
10.退出登录
auth.logout(request)