CMDB那些事儿 ------> API认证及数据加密解密
API验证
1、为什么要做API验证?
- 保证仅是有权限的人才能访问
- 访问过程中,保证数据安全,库不会被篡改
2、你是如何设计的?
- 与Tornado 中加密Cookie的原理类似
- 创建动态KEY:md5(key+time)|time
- 为防止被黑客截获KEY,利用某人API,操作我后台数据,优化代码加以限制!
- 此处要:维护一个访问记录表(字典类型),仅当前10秒内访问的所有用户,超时相应用户会删除!
(解释:为什么要10s,各位看官,如果是每次访问生成的动态KEY我都记录的话,那到后期我这个表得大到什么地步!!!创建他的目的是优化访问权限,而不是增加累赘)
- 实现优化的鬼门三关(仅为娱乐)
第一关:时间 ------------> 超时无权限
第二关:算法规则 --------> 数据截获篡改,MD5算法值不一致,滚粗!
第三关:已访问记录 ------> 俗称人头关,首次访问会记录,同样的KEY再来一次,你当我算法是菜啊!
- 但是: 如果黑客截获KEY,他的网速比你快无比,这样是不是数据又被截获泄漏了?!怎么解决?--->数据加密!
别担心加密后,这样他来访问,就是来拉高你的网速的!!!
3、数据加密方法:
- RSA(公钥私钥) ----> 由于内部有字节限制操作,生成公钥时需要指定bytes位数,同时也不清楚每次发送数据的大小,拘谨很大!
摒弃吧!我的哥,我们需要的是随心所遇的加密方法!
- AES(仿微信高级加密) ---> 通过一个16位字节格式(及倍数)的key,对数据进行加密(数据必须是要bytes)一定要注意,此种加密的数据长度必须是16个字节的倍数!
不够则需要在字节数据之后加值凑够倍数,解码的时候,再把凑数的值去掉,获取真正的数据!
4、数据API认证及数据加密代码:
#!/usr/bin/env python # _*_ coding:utf-8 _*_ """ 用于数据加密及API加密认证的插件 """ from Crypto.Cipher import AES from lib.conf.config import settings def encrypt(message): """ 数据加密 :param message: :return: """ key = settings.DATA_KEY #获取配置文件中的KEY 注意是16个字节的倍数 cipher = AES.new(key, AES.MODE_CBC, key) #根据KEY生成AES对象 ba_data = bytearray(message,encoding='utf-8') #把要字符串转码成bytearray(bytes格式的数组) v1 = len(ba_data) #计算bytes数据的长度 v2 = v1 % 16 #对长度取余看是否是16的倍数 v3 = 16-v2 if v2 != 0 else 16 #v3是追加的长度 # if v2 == 0: # v3 = 16 # else: # v3 = 16 - v2 for i in range(v3): ba_data.append(v3) #bytearray只能追加数字,默认把数字转成字节 补齐差位,一般是余数为几就补多少个这个数字(一个数字占一个字节) final_data = ba_data.decode("utf-8") #先把数组类型的字节数据转成字符串 data = final_data.encode("utf-8") #再把字符串转成字节 msg = cipher.encrypt(data) # 要加密的字符串,加密后是bytes格式,必须是16个字节或16个字节的倍数;可以对bytes格式的直接加密! return msg def decrypt(msg): """ 数据解密 :param message: :return: """ key = settings.DATA_KEY cipher = AES.new(key, AES.MODE_CBC, key) #根据KEY生成AES对象 result = cipher.decrypt(msg) #对接收到的数据进行解密 ---->数据还是字节格式 data = result[0:-result[-1]] #利用切片的方法获取真实的数据,不取后边凑位拼接的数据 # (小技巧:由于补充的是数字,先获取这个数字,然后切片到倒数这个数) return str(data,encoding='utf-8') #bytes转成字符串格式 def auth(): """ API验证算法 :return: """ import time import hashlib ctime = time.time() #客户端获取当前机器的时间戳 key = "asdfasdfasdfasdf098712sdfs" #API认证的KEY(自定义) new_key = "%s|%s" %(key,ctime,) #组成新的字符串 m = hashlib.md5() #利用md5算法生成一个不可逆的随机码 m.update(bytes(new_key,encoding='utf-8')) #里面是字节数据 md5_key = m.hexdigest() #返回值是字符串类型 md5_time_key = "%s|%s" %(md5_key,ctime) #把计算出的随机码与参与计算的时间重新拼接成一个新的字符串,用以API验证 return md5_time_key
def post_asset(self,server_info): """ 向API发送数据,POST方式提交,注意数据提交的样式, 注意API接收方式,发送数据的时候没有key,POST没有key获取不到,而是用request.body API加密认证,写到请求头中 利用AES算法,加密采集到的数据,data方式传值(加密的bytes数据),非json(自动json序列化) :param server_info: :return: """ # requests.post(settings.API,json=server_info) data = encrypt(json.dumps(server_info)) response = requests.post( url=settings.API, data=data, headers={'OpenKey':auth(),'Content-Type':'application/json'} ) print(response.text) #用于显示服务端返回的信息 # 发送的数据由字符串变成字节原因: # unicode是万国码,至少是用16位表示一个字符串,py3中内存中默认使用4个字节表示一个字符串,真正传输或保存过程中, # 会通过字符编码成别的格式,这样就会对占位进行压缩;在网络传输过程中,bytes的占位还会更小,所以传输的数据都是bytes格式。 # 我们手动的把字符串数据转成bytes格式,就省却了post内部转码的过程,让他直接发送bytes数据。 # data 方式发送数据,请求头header中加入:'Content-Type':'application/json' 是为了让后台以json的形式发送数据!
import json import hashlib import time from django.shortcuts import render,redirect,HttpResponse from Crypto.Cipher import AES from django.conf import settings from repository import models from api.services import Services def decrypt(msg): """ 数据解密 :param message: :return: """ key = settings.DATA_KEY #获取settings中的KEY cipher = AES.new(key, AES.MODE_CBC, key) #根据KEY生成AES对象 result = cipher.decrypt(msg) #对接收到的数据进行解密 ---->数据还是字节格式 data = result[0:-result[-1]] #利用切片的方法获取真实的数据,不取后边凑位拼接的数据 # (小技巧:由于补充的是数字,先获取这个数字,然后切片到倒数这个数) return str(data,encoding='utf-8') #bytes转成字符串格式 api_key_record ={} def asset(request): client_md5_time_key = request.META.get("HTTP_OPENKEY") #从MESA中获取发送的API认证信息 client_md5_key,client_ctime = client_md5_time_key.split("|") client_ctime = float(client_ctime) server_time = time.time() # ######################### 鬼门三关 ############################### #第一关,时间关 if server_time - client_ctime > 10: return HttpResponse("【第一关】小伙咂,别唬我,你都超时了!!!") #第二关,规则关 temp = "%s|%s"%(settings.AUTH_KEY,client_ctime,) m = hashlib.md5() m.update(bytes(temp,encoding="utf-8")) server_md5_key = m.hexdigest() if server_md5_key != client_md5_key: return HttpResponse("【第二关】小子,你是不是改值啦?改规则可不是好孩子哦!") for k in list(api_key_record.keys()): v = api_key_record[k] if server_time > v: del api_key_record[k] #第三关,人头关 if client_md5_time_key in api_key_record: return HttpResponse("【第三关】老哥,你慢了!人头已拿走....") else: api_key_record[client_md5_time_key] = client_ctime + 10 if server_md5_key != client_md5_key: return HttpResponse("认证失败...") if request.method == "GET": ys = "重要的不能被现在人等看的数据" return HttpResponse(ys) #验证不通过! else: #新资产信息 server_info = json.loads(decrypt(request.body)) for k,v in server_info.items(): #用以判断接收到的数据信息 print(k,v) return HttpResponse("收到了!!") #用于验证是否接收到数据
加密解密方法----->参考武sir!
浙公网安备 33010602011771号