csrf的使用以及中间件插拔设计
csrf的使用以及中间件插拔设计
csrf简介
-
CSRF是什么?
- CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding, 缩写为:CSRF/XSRF
-
CSRF可以做什么?
- 钓鱼网站: 假设是一个和银行一模一样的网址页面,用户在该页面上转账账户的钱就会减少,三十受益人却不是自己指定想要转账的那个人
- 你可以这么理解CSRF共计:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你的明见发送邮件,发信息,盗取你的账号,甚至于购买商品,虚拟货币转账...造成的问题包括:个人隐私泄露以及财产安全。
-
模拟
-
一台计算机上的两个服务端口启动,钓鱼网站提交地址改为正规网站的地址
-
用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
-
2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
-
3.用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
-
4.网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
-
5.浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
-
-
-
预防
- csrf策略:通过在返回的页面上添加独一无二的标识信息从而区分正规网站和钓鱼网站的请求
- CSRF防护的一个重点就是要对用户凭证进行处理,通过这种机制可以对用户的请求合法是否合法进行判断,判断是不是跨站攻击的行为。因为用户凭证是Cookie中存储的,所以防护机制的处理对象也是Cookie的数据,我们要在防护的数据中加入签名校验,并对数据进行生命周期时间的管理,就是数据过期管理。
csrf校验策略
1.Token的构成
消息[msg]:而msg本身也有两部分组成:一部分:随即字符串,过期时间戳。
分隔符[separator]:用于分割msg部分与加密后生成的signature签名部分,这里用的是"."
签名[signature]:singnature.signature签名,是对“msg消息”用特定算法进行加密后的字符串
token = base64(msg)格式化..base64(sha256("密码",msg))
Token由被base64的msg编码穿+256加密msg再进行Base64编码,两个字符串的内容结合
2.form表单操作csrf
<h1>这是一个真实的网站</h1>
<form action="" method="post">
{% csrf_token %}
</form>
3.Ajax操作csrf
方法一:先编写csrf模板语法,然后利用标签查找和值获取,手动添加
data:{'username':'','csrmiddlewaretoken':$('[name="csrmiddlewaretoken"]').val()},
方法二:直接利用模板语法即可
data:{'username':'','csrfmiddlewaretoken':'{{ csrf_token }}'},
方法三:通用方法(js脚本)CV大法即可
更多详情请见:https://docs.djangoproject.com/en/1.11/ref/csrf/
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,但是有局部视图函数需要校验
关键字:@csrf_protect
2.当整个网站默认都校验csrf,但是有局部视图函数不需要校验
关键字:@csrf_exempt
3.两种装饰器的使用
先导入装饰器:
form django.views.decorators.csrf import csrf_protect,csrf_exempt
4.针对FBV添加装饰器
@csrf_protect
def home(request):
return HttpResponse('就离谱')
@csrf_exempt
def index(request):
return HttpResponse('难搞')
5.针对CBV不能直接在方法上添加装饰器,需要借助专门添加装饰器的方法mothod_decorator
from django import views
from django.utils.decorators import method_decorator
6.针对CBV添加装饰器
@method_decorator(csrf_protect, name='post') # 方法二:指名道姓的添加
class MyHome(views.View):
@method_decorator(csrf_protect) # 影响类中所有的方法
def dispatch(self, request, *args, **kwargs):
super(MyHome, self).dispatch(request, *args, **kwargs)
def get(self, request):
return HttpResponse('Home Get view')
@method_decorator(csrf_protect) # 方法一:直接在该函数头上添加
def post(self, request):
return HttpResponse('Home Post view')
7.针对csrf_exempt这个装饰器只有方法三有效,其他的装饰器上述方法都可以直接使用(包括自定义装饰器)
auth模块常见方法
1.我们在开发一个网页的时候无可避免的需要设计实现网站的用户系统,此时我们需要实现包括用户注册、用户登录、注销、修改密码等功能。为了节省这个麻烦的过程,Django自带了强大的用户认证系统Auth,它默认使用auth_user表来存储用户的数据。
2.该表还是Django admin后台管理默认的表
django admin后台管理员账号创建
3.创建方法:
python manage.py createsuperuser
4.导入auth模块
from django.contrib import auth
字段 | 释义 |
---|---|
id | ID |
password | 密码 |
last_login | 最后登录时间 |
is_superuser | 是否是管理员 |
username | 用户名 |
first_name | 姓 |
last_name | 名 |
邮箱 | |
is_staff | 是否是工作人员 |
is_active | 是否激活 |
date_joined | 创建时间 |
auth常见功能
-
创建用户
from django.contrib.auth.models import User 1.普通用户 user = User.objects.create_user(username='用户名',password='密码',...) 2.管理员用户,必须要有email user = User.object.create_superuser(username='用户名',password='密码',email='邮箱',...)
-
校验用户名或密码是否非正确
1.作用:提供了用户认证功能,即验证用户名以及密码是否正确 2.必须传入两个参数:username & password 3.校验成功返回True,失败返回None from django.contrib import auth user_obj = auth.authenticate(request,username=username,password=password) if user_obj: print(user_obj.username) print(user_obj.password) else: print('验证失败')
-
用户登录,保存用户状态
1.作用:该函数接受了一个HttpResponse对象,以及一个经过认证的User对象 2.实现一个用户登录功能,本质上会在后端为用户生成session数据存在于session表中 3.只要执行了该方法,你就可以在任何地方通过request.user获取到当前登录的用户对象,否则拿到的就是一个匿名用户对象也就是none auth.login(request,user_obj) # 内部调用的就是request.session['key']=user_obj
-
判断用户是否登录
1.作用:用来判断当前请求是否用过了认证 # 视图中使用 if not request.user.is_authenticated(): print('未登入,请先登入') # 模板中使用 {% if request.user.is_authenticated %} {{ request.user.username }} 欢迎回家 {% else %} <a href="/auth_login/">请去登录</a> {% endif %}
-
获取登录用户对象
1.作用:获取当前登录的用户对象 user_obj = request.user
-
校验用户登录装饰器
1.作用:一个装饰器工具,用来快捷的给某个视图添加登录校验 from django.contrib.auth.decorators import login_required # 使用方式一: 直接在视图函数上添加 @login_required(login_url='/login/') # 需要指定url @login_required # 全局配置 def logout_func(request): auth.login(request) return redirect("/login/") # 方法二:在配置文件中全局配置 LOGIN_URL = '/login/'
-
校验密码是否正确
1.作用:提供一个检查密码是否正确的方法,需要提供当前请求用户的密码 2.密码正确返回True,否则返回False if user.check_password('你的密码old_password') print('ok')
-
修改密码
1.作用:提供一个需要改密码的方法,接受要设置新密码作为参数 2.设置完一定要调用用户对象的save方法 request.user.set_password(new_password) request.user.save()
-
注销登录
1.作用:该函数接收一个HttpResponse对象,无返回值 2.当调用该函数时,当前请求的session信息会全部清除,该用户既没有登录,使用该函数也不会报错 auth.login(request) # 内部使用的就是request.session.flush()
auth_user表切换方法
1.models.py
from django.contrib.auth.models import AbstractUser
class Userinfo(AbstractUser):
'''扩展auth_user表中没有的字段'''
phone = models.BigIntegerField()
desc = models.TextField()
2.setting.py
AUTH_USER_MODEL = 'app01.Userinfo'
基于中间件思想的功能插拔式设计
1.模块的导入方法
1.1import 句式
1.2from ... import ...
1.3类似中间件功能的导入方式(importlib)
s1 = 'dajngo.bbb.b'
print(s1.rsplit('.',maxsplit=1)) # 底层原理
import importlib
res = importlib.import_module(s1) # 自动将点号做标记来切割 from bbb import b
print(res.name)
2.类中间件导入方式
2.1简单的函数式封装
def send_qq(content):
print('qq消息通知:',content)
def send_wx(content):
print('微信消息通知:',content)
def send_mag(content):
print('短信消息通知:',content)
def seed_all(content):
send_qq(content)
send_wx(content)
send_mag(content)
if __name__ == '__main__':
seed_all('累了,毁灭吧')
2.2配置文件插拔设计
auth扩展表字段
auth_user 中的字段有限, 想要在 auth_user 表中添加新的字段, 我们可以对其进行扩展。
扩展方式一 : 建立一对一外键关系
新建一个模型类, 比如要添加 Phone 字段, 在该模型内中写入, 并添加 User 的外键字段
Copyfrom django.contrib.auth.models import User
class user_detail(models.Model):
user=models.OneToOneField(to='User')
phone=models.CharField(max_length=32)
扩展方式二 : 继承 AbstractUser 类来扩写 (步骤)
Copy 1. 首先大前提是没有生成 auth_user 表(也就是没进行数据库迁移操作)(有的话需要删干净迁移记录和表)
2. 书写一个类, 并继承 AbstractUser 类
3. 在类中可以书写你需要扩展的字段, 也可以重写原来的字段
class MyAuthUser(AbstractUser):
username=models.CharField(max_length=12) # 重写字段
phone=models.CharField(max_length=32) # 书写新字段
4. 到 setting.py 配置文件中设置 AUTH_USER_MODEL 参数, 不然报错
AUTH_USER_MODEL = "[app名].[类名]"
AUTH_USER_MODEL = "app01.MyAuthUser" # 示例
5. 最后进行数据库迁移命令
python3 manage.py makemigrations
python3 manage.py migrate
如果 auth_user 表已经存在
1. 先删库
2. 并清空项目中所有的 makemigrations 而来的迁移记录
3. 再清空源码中admin,auth俩app的 makemigrations 产生的记录