博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python开发时几种安全验证的实现
阅读量:7048 次
发布时间:2019-06-28

本文共 6061 字,大约阅读时间需要 20 分钟。

额,一个突然的交流让我想起来我耽搁许久各种验证的实现迟迟没做过

趁着这个机会就搞了一下
分为三部分:邮箱验证,短信验证,图片验证码

邮箱验证

这个部分是主要参考的经典书籍-狗书

思路就是根据用户某些信息通过JSON Web签名生成token,然后再发送邮件验证,经典思路
生成和验证函数都加载在模型中
这里介绍了几种签名方式

token生成和验证
TimedJSONWebSignatureSerializer,看这个表面词的意思可以看出这里序列化加入了当前时间
这也是实现设置过期时间的依据吧
查看itsdangerous源码可以看到具体的加密方式
from itsdangerous import (TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired)...class User(db.Model):    __tablename__ = 'user'    id = db.Column(db.Integer,primary_key=True)    name=db.Column(db.String(64),unique=True,index=True)    def genter_auth_token(self,expiration=300): #设置有效期        s=Serializer(current_app.config['SECRET_KEY'],expires_in=expiration)        return s.dumps({'code':self.name}) #将用户名当作签名对象    @staticmethod    def verify_auth_token(token):        s=Serializer(current_app.config['SECRET_KEY'])        try:            data=s.loads(token) #加载数据        except BadSignature:            return None        except SignatureExpired:            return None        return data

结果

同时这里itdangerous类的签名方式都可以接收一个salt

文档中这样描述了salt的作用:

itsdangerous中的盐,是为了一个截然不同的目的而产生的。你可以将它视为成命名空间
假设你想签名两个链接。你的系统有个激活链接,用来激活一个用户账户,并且你有一个升级链接,可以让一个用户账户升级为付费用户,这两个链接使用email发送。在这两种情况下,如果你签名的都是用户ID,那么该用户可以在激活账户和升级账户时,复用URL的可变部分。现在你可以在你签名的地方加上更多信息(如升级或激活的意图),但是你也可以用不同的盐

即只有使用相同盐的序列化器才能成功把值加载出来

def genter_auth_token(self,expiration=300):    s=Serializer(current_app.config['SECRET_KEY'],salt='activate-salt',expires_in=expiration)    return s.dumps({'code':self.name})@staticmethoddef verify_auth_token(token):    s=Serializer(current_app.config['SECRET_KEY'],salt='activate-salt')

图片验证码

这个验证码可以直接调用一些平台的智能验证,也可以用另一种

另一个也许是比较传统的思路,就是自己生成的图片水印,保存验证码
python和php里都有相应的图片操作方法,这里就写下python的

流程就是生成任意的数字,保存,添加图片水印

这里肯定要用的python强大的图片处理库PIL,其中用到了

加线条,滤镜等增加干扰
下面是完整代码,该做注释的地方我已经加了注释
看代码之前,最好先好好看下PIL官方文档,和一些基本概念
部分我参考的博文也贴在了文末

#!/usr/bin/env python #coding=utf-8import osimport randomfrom flask import Flask,send_from_directoryfrom PIL import Image,ImageFont,ImageDraw,ImageFilterapp=Flask(__name__)app.debug=Trueclass picture:    def __init__(self):        self.size = (240,60)        self.mode="RGB"        self.color="white"        self.font = ImageFont.truetype("C:\Windows\Fonts\Arial.ttf", 36) #设置字体大小    def randChar(self):        basic='23456789abcdefghijklmnpqrstwxyzABCDEFGHIJKLMNPQRSTWXYZ'        return basic[random.randint(0,len(basic)-1)] #随机字符    def randBdColor(self):        return (random.randint(64,255),random.randint(64,255),random.randint(64,255)) #背景    def randTextColor(self):        return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127)) #随机颜色    def proPicture(self):        new_image=Image.new(self.mode,self.size,self.color) #创建新图像有三个默认参数:尺寸,颜色,模式        drawObject=ImageDraw.Draw(new_image) #创建一个可以对image操作的对象        line_num = random.randint(4,6) # 干扰线条数        for i in range(line_num):            #size=(240,60)            begin = (random.randint(0, self.size[0]), random.randint(0, self.size[1]))            end = (random.randint(0, self.size[0]), random.randint(0, self.size[1]))            drawObject.line([begin, end], self.randTextColor())        for x in range(240):            for y in range(60):                tmp = random.randint(0,50)                if tmp>30: #调整干扰点数量                    drawObject.point((x,y),self.randBdColor())        randchar=''          for i in range(5):            rand=self.randChar()            randchar+=rand            drawObject.text([50*i+10,10],rand,self.randTextColor(),font=self.font) #写入字符        new_image = new_image.filter(ImageFilter.SHARPEN) # 滤镜            return new_image,randchar@app.route('/
')def get_file(filename): return send_from_directory(os.getcwd(),filename)@app.route('/')def index(): test=picture() image,code=test.proPicture() image.save('new.jpg') url="http://127.0.0.1:5000/new.jpg" return '
'+"图中的code为:"+code #这里有缓存,需要CTRL+F5才会有效果if __name__=="__main__": app.run()

另外,有的前辈会再加入了扭曲图像增加分辨难度

# 图形扭曲参数 params = [1 - float(random.randint(1, 2)) / 100,               0,               0,               0,               1 - float(random.randint(1, 10)) / 100,               float(random.randint(1, 2)) / 500,               0.001,               float(random.randint(1, 2)) / 500               ] img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲

这里详细的介绍了下:

对当前图像进行透视变换,产生给定尺寸的新图像。
变量data是一个8元组(a,b,c,d,e,f,g,h),包括一个透视变换的系数。对于输出图像中的每个像素点,新的值来自于输入图像的位置的(a x + b y + c)/(g x + h y + 1), (d x+ e y + f)/(g x + h y + 1)像素,使用最接近的像素进行近似

这个的源定义就牵涉到了一个仿射变换,涉及一些数学的计算

看得我有点懵逼,就没加到我的代码中,先留坑
效果

这个地方现在很多网站会使用另一种回答问题的方式,这个方法的实现
我个人感觉也是应该也是相同的手段,只是将随机的字符串改为问题,将验证方式改为答案
不过这里或许要把问题和答案存进数据库,更方便点,也才能实现

另一种

短信验证

有时候想自己是不是出生太晚了。。。。。想写的东西,都能搜到很好的博文,如下:

这里是文中所用平台的开发文档,不过平台可以自由选择,结果都是一样
这里就简化一下前辈的代码,把关于验证码处理的重点代码撸了出来,用到了Redis,我也趁机学了一波,的确挺好用的

import redisimport randomphonenumber=188888888#这里可以利用正则过滤一下电话号码,比如:#/^(13[0-9]|14[5-9]|15[0-9]|16[6]|17[0-8]|18[0-9]|19[8-9])\d{8}$/conn=redis.StrictRedis(host='127.0.0.1',port=6379)def producCode():    verifyCode=str(random.randint(100000,999999))    pipe=conn.pipeline() #添加管道,可以一次连接执行多次命令    pipe.set("phone%s"%phonenumber,verifyCode)    pipe.expire("phone%s"%phonenumber,60) #设置过期时间一分钟    pipe.execute()def checkCode():    pipe=conn.pipeline() #添加管道,可以一次连接执行多次命令    pipe.set('postNum%s'%phonenumber,'0')    validate_number = request.get_json().get('validate_number')    pipe.incr('postNum%s'%phonenumber) #记录提交次数防止爆破    if conn.get('postNum%s'%phonenumber)>3:        pass    ...    if validate_number != validate_number_in_redis:        return jsonify({'code': 0, 'message': '验证没有通过'})    pipe.set('is_validate:%s' % phone_number, '1') #通过验证码设置value为1    pipe.expire('is_validate:%s' % phone_number, 120)    pipe.execute()    return jsonify({'code': 1, 'message': '验证通过'})def postMessage():    result=conn.get("phone%s"%phonenumber)    #此时如果通过验证码,result为1,否则为0    ...    #剩下的其他操作

这里提到了泄露接口导致验证码爆破的情况,我也添加了一些代码

另外就是在某些功能模块,也易出现漏洞,比如修改资料处,验证码不仅仅要与phone一致
也要检查用户名的一致性,要不然如果只是通过验证码,用户修改为自己的号码,验证码手机号都通过验证
(感觉一般人不会出现这种错误)

而你的代码又是直接传入用户名进行修改操作,这将可能导致任意用户重置密码
或者你的代码直接将phone作为索引进行修改

参考文章:

转载地址:http://gjkol.baihongyu.com/

你可能感兴趣的文章
maven 项目打包配置(build节点)
查看>>
保存指定品质的图片
查看>>
多目标跟踪baseline methods
查看>>
关于QT_Creator不能在线调试问题
查看>>
六、python小功能记录——递归删除bin和obj内文件
查看>>
阅读《移山之道》及讲义感想
查看>>
python进阶-面向对象编程五:类的内置方法
查看>>
JAVA入门到精通-第52讲-面试题讲评
查看>>
05-spark streaming & kafka
查看>>
python杂记
查看>>
cd 简化命令
查看>>
LeetCode--205--同构字符串
查看>>
python-ConfigParser模块【读写配置文件】
查看>>
wireshark使用方法总结
查看>>
Window Server 2008 R2 TFS2010 安装前的准备
查看>>
20141123
查看>>
translucent 属性
查看>>
android listView嵌套gridview的使用心得
查看>>
[ES7] Descorator: evaluated & call order
查看>>
安卓动态调试七种武器之离别钩 – Hooking(上)
查看>>