django框架:csrf跨站请求伪造、csrf校验策、csrf相关装饰器、auth认证模块、auth认证相关模块及操作、扩展auth_user表

一、csrf跨站请求伪造

概念引入

简介

  • 我们通过模仿一个钓鱼网站来提现csrf跨站请求伪造。

钓鱼网站:模仿一个正规的网站 让用户在该网站上做操作 但是操作的结果会影响到用户正常的网站账户 但是其中有一些猫腻
eg:英语四六级考试需要网上先缴费 但是你会发现卡里的钱扣了但是却交到了一个莫名其妙的账户 并不是真正的四六级官方账户

讲解

首先我们看到名称伪造就发现有端倪,CSRF跨站伪造,我们可以简单的理解成钓鱼网站,钓鱼网站会拥有一个跟银行app网页一样的界面(具体到网址,网页内容都一样).当我们输入转账信息的时候,点击确定转账,这个时候钓鱼网站就会把我们提交的转账信息发送给真实的网站,但是在发送之前会把转账对象修改成他自己的账号,接着发送给真实的网站进行转账,并且他们在转账后会在极短的时间内连续把这笔钱转给不同的用户,从而达到让受害者无法追回的目的.所以有些网站就是用这种方式洗黑钱的.

img

代码如下

	钓鱼网站html部分代码
		    <form method="post" action="http://127.0.0.1:8000/real/">
		        <p>username:<input type="text" name="username"></p>
		        <p>target_user<input type="text"><input type="text" name="target_user" value="土匪" style="display: none"></p>
		        <p>Money:<input type="text" name="money"></p>
		        <input type="submit">
		    </form>

            
	real.views(真实网页的后端)
			def real_html(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, 'real.html')

思考:如何区分真假网站页面发送的请求

概念讲解

一.CSRF是什么?

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

二.CSRF可以做什么?

你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…造成的问题包括:个人隐私泄露以及财产安全。

三.CSRF的原理

下图简单阐述了CSRF攻击的思想:

img

从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:

1.登录受信任网站A,并在本地生成Cookie。

2.在不登出A的情况下,访问危险网站B。

看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:

1.你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。

2.你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了…)

3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。

上面大概地讲了一下CSRF攻击的思想,我们可以用开头的银行转账的操作作为例子(仅仅是例子,真实的银行网站没这么傻)

二、csrf校验策略

概念讲解

根据上面的案例,我们发现假网站也是通过post请求给真的网站发送转账请求的,因此解决问题的重点就在于如何检查post请求.

这里我们使用csrf策略来解决这个问题,他的作用就是给网页添加这个csrf策略,然后在网页执行post请求的时候就会传一个csrf_token,根据这个token是否为后端传给前端的token来判断post请求是否符合要求(即是否安全可信).

拓展知识点(了解一下即可,课外找的)

Token的构成

消息[msg]:而msg本身也有两部分组成:一部分:随即字符串,过期时间戳。
分隔符[separator]:用于分割msg部分与加密后生成的signature签名部分,这里用的是"."
签名[signature]:singnature.signature签名,是对“msg消息”用特定算法进行加密后的字符串
token = base64(msg)格式化..base64(sha256("密码",msg))
Token由被base64的msg编码穿+256加密msg再进行Base64编码,两个字符串的内容结合

如果上面的拓展内容不想看,我们也可以简单理解一下这个token的作用,简单来说就是后端给前端发一个所谓的token编码,他是一串随机字符,这个字符只有产生他的服务端能识别,通过这个特性达到防止钓鱼网站进行诈骗。

form表单操作csrf策略

form表单内部添加 {% csrf_token %}

ajax请求csrf策略

	// 方式1:自己动手取值 较为繁琐
   {#data:{'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},#}
   // 方式2:利用模板语法自动获取(一定要用引号引起来)
   {#data:{ 'csrfmiddlewaretoken':'{{ csrf_token }}','username':'jason'},#}
   // 方式3:直接引入一个js脚本即可(官网提供的)
 	参考:https://www.cnblogs.com/Dominic-Ji/p/9234099.html

js脚本代码

ps:直接使用JS脚本 拓展性最高

<script src="/static/csrf.js"></script>
这里我们把下方代码写到一个js文件中然后使用的时候在网页中导入js代码就能生效。

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);
        }
    }
});

img

img

注意事项

当我们使用csrf校验策略的时候需要打开配置文件中的中间件注册信息(就是我们平时在使用post请求时需要去配置文件注释掉的那个配置).

打开配置就等于所有的请求都需要检验csrf校验策略,注释掉就等于都不检查.

img

三、csrf相关装饰器

当我们知道csrf的作用和使用方式后,我们联系实际情况考虑到实际应用中会出现下列情况:

  • 整个django项目都校验csrf 但是某些个视图函数\类不想校验
  • 整个django项目都不校验csrf 但是某些个视图函数\类需要校验

根据FBV和CBV的分类,导入时候有所区别:

FBV添加装饰器的方式(与正常函数添加装饰器一致)
from django.views.decorators.csrf import csrf_exempt, csrf_protect

# @csrf_exempt
想让csrf校验检测就使用装饰器@csrf_protect,不想让csrf校验检测就使用@csrf_exempt
@csrf_protect
def transfer_func(request):pass


CBV添加装饰器的方式(与正常情况不一样 需要注意)
主要有三种方式
# @method_decorator(csrf_protect, name='post')  # 方式2:单独生效
class MyView(views.View):
    @method_decorator(csrf_protect)  # 方式3:整个类中生效
    这里需要会去看CBV的源码剖析,CBV源码内起作用的也是dispaly,通过反射执行字符串表示的功能,因此第三种方式是整个类中生效的。
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    # @method_decorator(csrf_protect)  # 方式1:单独生效
    def post(self, request):
        return HttpResponse('from cbv post view')
  
注意有一个装饰器是特例只能有一种添加方式>>>:csrf_exempt
 	只有在dispatch方法处添加才会生效

img

四、auth认证模块

前戏:在刚学习django的时候我们发现django自带一个admin路由,但是需要我们提供管理员账号和密码,接下去学习的过程中,我们又发现数据库迁移后创建了十多张新的表,大部分我们都没有研究,其中的一张表就是专门用来配合Auth模块做用户的登录、校验用户是否登录、修改密码、注销登录等功能的。

如果想要使用admin后台管理 需要先创建表 然后创建管理员账号。

直接执行数据库迁移命令即可产生默认的auth_user表 该表就是admin后台管理默认的认证表。

创建超级管理员的命令

	python38 manage.py createsuperuser

img

基于auth_user表编写用户相关的各项功能

  • 登录、校验用户是否登录、修改密码、注销登录等都是在这张表完成的.

五、auth认证相关模块及操作

功能及方法介绍

说在前面

auth封装了很多方法,他让我们不需要自己编写逻辑判断用户信息是否正确(不止这个一个功能哦).

部分属性功能介绍

字段 释义
id ID
password 密码
last_login 最后登录时间
is_superuser 是否是管理员
username 用户名
first_name
last_name
email 邮箱
is_staff 是否是工作人员
is_active 是否激活
date_joined 创建时间

导入auth模块

python

from django.contrib import auth

在执行auth模块相关操作前要先导入数据库中的表

from django.contrib.auth.models import User

用户注册功能(即用户创建)

User.objects.create_user(username=username, password=password)

判断用户名和密码是否正确

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='邮箱',...)

判断用户是否登录

request.user.is_authenticated

如果登陆了,结果为布尔值Ture,否则为False

获取登录用户对象数据

request.user

用户信息可以在user中用点的方式获取

校验用户是否登录装饰器

	from django.contrib.auth.decorators import login_required
    我们如果不指定这里的url,就不会跳转到我们制作的登陆页面.
    @login_required(login_url='/login/')  局部配置 
    @login_required						全局配置
 	 配置文件中LOGIN_URL = '/login/'
     配置文件中修改,会让全局的@login_required影响下的视图函数/类全都跳转向一个页面

校验原密码是否正确

request.user.check_password(原密码)

修改密码

	request.user.set_password(新密码)
 	request.user.save()

退出登录

auth.logout(request)

部分功能操作代码展示

		'''判断当前用户有没有登录操作'''
		html
		    <form method="post">
		        {% csrf_token %}
		        <p>username:<input type="text" name="username"></p>
		        <p>password:<input type="text" name="password"></p>
		        <input type="submit">
		    </form>
		    
		views
			def login(request):		
			    print(request.user)
			    '''
			        用户登录成功之后执行Auth.login 该方法返回当前登录用户对象  admin
			        用户没有登录成功没有执行Auth.login 该方法返回匿名用户对象  AnonymousUser
			    '''
			    print(request.user.is_authenticated)    # 判断当前用户是否登录 返回True False
			    if request.method == 'POST':
			        username = request.POST.get('username')
			        password = request.POST.get('password')
			        # 现在遇到一个问题就是怎么通过models.的方式去操作auth_user表格呢? 这个时候就有auth模块了
			        res = auth.authenticate(request, username=username, password=password)
			        # print(res.username, res.password,
			        #       res.pk)  # admin pbkdf2_sha256$150000$4NZp3wuIgf9A$7hfw7pcm35QosjTRWFpj9U3F5SoGTRqUv/H8DpBp308= 2
			        '''数据正确的情况下会有一个返回值是数据对象 数据错误的情况下返回的是None'''
			        if res:
			            auth.login(request, res)    # 自动操作Cookie与Session
			    return render(request, 'login.html')


		'''
		用户登录装饰器
		当用户没有登录的情况下跳转到注册界面 在settings文件中设置跳转的页面地址 LOGIN_URL = '/login/'
		'''
		
		from django.contrib.auth.decorators import login_required

		@login_required
		def index(request):
		    return HttpResponse('Form index view')
		
		@login_required
		def func(request):
		    return HttpResponse('Form func view')

		'''重置密码'''
		views
			@login_required
			def set_password(request):
			    if request.method == 'POST':
			        old_password = request.POST.get('old_password')
			        new_password = request.POST.get('new_password')
			        '''校验原密码是否正确 自定加密并检验'''
			        is_right = request.user.check_password(old_password)
			        if is_right:
			            request.user.set_password(new_password)  # 修改密码
			            request.user.save()  # 保存数据
			    return render(request, 'set_password.html')
		html
			<form action="" method="post">
			    {% csrf_token %}
			    <p>old_password:<input type="text" name=""></p>
			    <p>new_password:<input type="text" name=""></p>
			    <input type="submit">
			</form>
		
		'''自动清除Cookie与Session'''
		views
			@login_required
				def loginout(request):
				    auth.logout(request)  # 自动清楚cookie和Session
				    return HttpResponse('清除成功!!!')

		'''创建用户'''
		views
			from django.contrib.auth.models import User
			
			def register(request):
			    User.objects.create(username='Like', password=123, email='120@qq.com')	# 这里创建的是普通用户 不能登录管理员系统
			    return HttpResponse('注册成功!!!')

img

六、扩展auth_user表

如果我们既想使用auth模块的功能,并且又想扩展auth_user表的字段,有以下两种方式:

  • 思路1:一对一字段关联

因为操作相对繁琐,不建议使用.

img

  • 思路2:替换auth_user表

我们点进调入的User表查看源码,我们发现他只是继承了AbstractUser模块,然后点进AbstractUser源码,我们发现他就相当于一张模型表,配置了auth需要使用的表的具体内容.因此我们可以自己定义一个模型表,只要导入父类AbstractUser即可自定义功能.

操作简单,推荐使用.

	步骤1:模型层编写模型类继承AbstractUser
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
        # 填写AbstractUser表中没有的字段
        phone = models.BigIntegerField()
        desc = models.TextField()
 	步骤2:一定要在配置文件中声明替换关系(把表替换成我们自定义的模型表)
        AUTH_USER_MODEL = 'app01.UserInfo'
	ps:替换还有一个前提 就是数据库迁移没有执行过(auth相关表没有创建)
posted @ 2023-02-11 19:26  wwwxxx123  阅读(15)  评论(0编辑  收藏  举报