CMDB 数据加密 最终整合API验证+AES数据加密
当CMDB运行在内网的时候,经过API验证的三关是没有问题的,但是如果运行在外网,有一个问题是,黑客截取后的访问速度比客户端快的时候还会造成数据泄露。为了解决这个问题,就要对数据进行加密
RSA加密
RSA是一种非对称加密,但是对加密的字符串的长度需要设置。但是字符串的长度是不固定的,而且这种方式的加密,加密的字符串越长,加密的时间越长。
- pip install rsa
- rsa.newkeys(256) 256代表加密的位数 256/8=32 就是32个字节,但是源码中32-11=21 也就是最多加密21个字节
import rsa
import base64
# ######### 1. 生成公钥私钥 #########
pub_key_obj, priv_key_obj = rsa.newkeys(256)
pub_key_str = pub_key_obj.save_pkcs1()
pub_key_code = base64.standard_b64encode(pub_key_str)
priv_key_str = priv_key_obj.save_pkcs1()
priv_key_code = base64.standard_b64encode(priv_key_str)
print(pub_key_code)
print(priv_key_code)
# ######### 2. 加密 #########
def encrypt(value):
key_str = base64.standard_b64decode(pub_key_code)
pk = rsa.PublicKey.load_pkcs1(key_str)
val = rsa.encrypt(value.encode('utf-8'), pk)
return val
# ######### 3. 解密 #########
def decrypt(value):
key_str = base64.standard_b64decode(priv_key_code)
pk = rsa.PrivateKey.load_pkcs1(key_str)
val = rsa.decrypt(value, pk)
return val
# ######### 基本使用 #########
if __name__ == '__main__':
v = 'Django'
v1 = encrypt(v)
print(v1)
v2 = decrypt(v1)
print(v2.decode('utf8'))
AES 加密
微信公众号的加密方式,而且对字符串的长度没有限制
但是要加密的字符串必须是16个字节或者是16个字节的倍数
安装模块
由于用到了Crypto,通过pip安装后Python3.6中仍然不能运行。自己是用的Anconda中的自带的,可以使用。
在GitHub找到了已经编译好的Python3.5的pycrypto.
加密过程
- 加密用到的key 必须是16个字节或16字节的倍数
- 被加密的字符串同样也必须是16个字节或16字节的倍数
- 通过bytearray能对字节的长度进行修改
- 用空格补足的算法 对bytearray用16取余,16-余数+原来的长度就补到了32
# ############## 加密 ##############
from Crypto.Cipher import AES
key = b'jlaksdflj77asdfh' # 16个字节或16字节的倍数
cipher = AES.new(key, AES.MODE_CBC, key)
data = '要加密的字符串' # 一个中文是3个字节
byte_data = bytearray(data, encoding='utf8') # 想要动态修改字节,用bytearray 相当于把字节转换成一个数组
# print(len(byte_data))
v1 = len(byte_data) # 这是要加密的数据的长度 21
v2 = v1 % 16 # 取余 5
v3 = 16 - v2 # 这是要补足的数 11 : 21+11=32 是16的倍数
for i in range(v3):
byte_data.append(32) # 32代表ASCII中的空格 chr(32) 可以查看
# print(len(byte_data))
final_data = byte_data.decode('utf8') # 把字节解码成字符串
print(final_data) # 加密的内容并加上了补足的空格
msg = cipher.encrypt(final_data) # 进行加密 final_data必须是16个字节或16字节的倍数
print(msg) # 加密后是字节
解密过程
- 下面是直接利用了加密后的字节msg
- 解密后删除空格
# ############## 解密 ##############
msg = b'G\xf47P\xf4X\xbf\x88\xba^;\xbcA\xde(\xb0)\xafA\xb8\xd8\t\xca\xef\xd7\xcdZhw\x98?N'
key = b'jlaksdflj77asdfh' # 需要同样的key
cipher_jm = AES.new(key, AES.MODE_CBC, key)
result = cipher_jm.decrypt(msg) # 对字节进行解密
print(result.decode('utf8').strip()) # 解密后删除空格
不用空格进行补足--机智的加密解密
如果原来要加密的字符串中存在空格,机密后会删除空格,这是一个问题
机智的加密方式
- 当需要补几个字节的时候,就补上几个。如需要11个字节,就补11个11(ascii)
for i in range(v3):
byte_data.append(v3) # 当需要补几个字节的时候,就补上几个。如需要11个字节,就补11个11(ascii)
机智的解密取值
- result[-1] 是取到了最后一个值,而我们加密的时候,补足的值和个数是相同的
- result[0:result[-1]] 就代表真正的数据 牛逼!!!
result = cipher_jm.decrypt(msg)
result_data = result[0:-result[-1]]
例子:
result = b'\xe8\xa6\x81\xe5\x8a\xa0\xe5\xaf\x86\xe7\x9a\x84\xe5\xad\x97\xe7\xac\xa6\xe4\xb8\xb2\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b' # result 是解密后的数据
print('最后一个字节',result[-1]) # 取最后一个值
print('真实的数据',result[0:-result[-1]]) # 获取真实的数据 result[0:-11]
还有一个问题是 v2取余是0的情况
v2取余是0的情况,加密的字符串正好就是16的倍数,那么解密的时候就出问题了,解决问题还得从加密的地方入手。思路是主动设置一个值,如16
v1 = len(byte_data)
v2 = v1 % 16 # 取余 5
if v2 == 0:
v3 = 16 # 字符串正好是16的倍数 余数是0 主动设置成16
else:
v3 = 16 - v2 # 这是要补足的数 11 : 21+11=32 是16的倍数
for i in range(v3):
byte_data.append(v3)
最终封装成函数
from Crypto.Cipher import AES
def encrypt(message):
key = b'jlaksdflj77asdfh' # 16个字节或16字节的倍数
cipher = AES.new(key, AES.MODE_CBC, key)
byte_data = bytearray(message, encoding='utf8') # 想要动态修改字节,用bytearray 相当于把字节转换成一个数组
v1 = len(byte_data) # 这是要加密的数据的长度 21
v2 = v1 % 16 # 取余 5
if v2 == 0:
v3 = 16
else:
v3 = 16 - v2 # 这是要补足的数 11 : 21+11=32 是16的倍数
for i in range(v3):
byte_data.append(v3)
final_data = byte_data.decode('utf8') # 把字节解码成字符串
msg = cipher.encrypt(final_data) # 进行加密 final_data必须是16个字节或16字节的倍数
return msg
# ############## 解密 ##############
def decrypt(msg):
key = b'jlaksdflj77asdfh' # 需要同样的key
cipher = AES.new(key, AES.MODE_CBC, key)
result = cipher.decrypt(msg) # 对字节进行解密
data = result[0:-result[-1]]
return data.decode('utf8')
if __name__ == '__main__':
msg = encrypt("我要加密了")
data = decrypt(msg)
print(data)
API 加密和AES数据加密结合
客户端发送数据的形式
当客户端把采集的信心向API发送的时候,request.post(url,data,json) ,使用json的的时候默认在请求头中添加'content_type': 'application/json'
,会把字典类型的数据json序列化成字符串发送到API。
# 源码中
if not data and json is not None:
# urllib3 requires a bytes-like body. Python 2's json.dumps
# provides this natively, but Python 3 gives a Unicode string.
content_type = 'application/json'
body = complexjson.dumps(json)
if not isinstance(body, bytes):
body = body.encode('utf-8')
request就是相当于socket,在传输的过程中会把字符串转换成字节发送,所以后台接收的数据实际字节数据。在这里可以自己把数据用json序列化然后转换成字节,用data发送。
import requests
import json
data = {'k1': 'v1'}
data = bytes(json.dumps(data),encoding='utf8')
# response = requests.post("http://127.0.0.1:8000/api/asset.html", headers={'OpenKey': auth()},json={'k1':'v1'})
response = requests.post(
"http://127.0.0.1:8000/api/asset.html",
headers={'OpenKey': auth(), 'content_type': 'application/json'},
data=data
)
print(response.text)
服务端接收数据并解密
elif request.method == 'POST':
server_info= decrypt(request.body) # 对数据进行解密
server_info = json.loads(server_info) # 对字符串反序列化成字典类型的数据
print(server_info)
结合后的客户端
- 把加密和验证制作成插件 lib/utils
from Crypto.Cipher import AES
import time
import hashlib
from config import settings
def encrypt(message):
"""
数据加密函数
:param message:
:return:
"""
key = b'jlaksdflj77asdfh' # 16个字节或16字节的倍数
cipher = AES.new(key, AES.MODE_CBC, key)
byte_data = bytearray(message, encoding='utf8') # 想要动态修改字节,用bytearray 相当于把字节转换成一个数组
v1 = len(byte_data) # 这是要加密的数据的长度 21
v2 = v1 % 16 # 取余 5
if v2 == 0:
v3 = 16
else:
v3 = 16 - v2 # 这是要补足的数 11 : 21+11=32 是16的倍数
for i in range(v3):
byte_data.append(v3)
final_data = byte_data.decode('utf8') # 把字节解码成字符串
msg = cipher.encrypt(final_data) # 进行加密 final_data必须是16个字节或16字节的倍数
return msg
def decrypt(msg):
"""
数据解密函数
:param msg:
:return:
"""
key = b'jlaksdflj77asdfh' # 需要同样的key
cipher = AES.new(key, AES.MODE_CBC, key)
result = cipher.decrypt(msg) # 对字节进行解密
data = result[0:-result[-1]]
return data.decode('utf8')
def auth():
"""
API验证
:return:
"""
ctime = time.time()
key = settings.AUTH_KEY
new_key = '%s|%s' % (key, ctime)
m = hashlib.md5()
m.update(bytes(new_key, encoding='utf8')) # python3中是字节
md5_key = m.hexdigest() # 加密后是字符串
md5_key_time = '%s|%s' % (md5_key, ctime) # 把当前时间发送后API,API利用时间进行加密,最后比较的是密文
return md5_key_time
调用:
- client/Base
- 注意要有请求头,主要是包含OpenKey
class Base(object):
def post_asset(self, server_info):
data = encrypt(json.dumps(server_info)) # 把采集到的资产信息序列化化成字符串 加密后变成字节
# requests.post(settings.API, json=server_info) # 向API发送数据 json=() 内部json序列化
requests.post(settings.API, data=data,headers={'OpenKey': auth(), 'content_type': 'application/json'}) # 向API发送数据 json=() 内部json序列化
结合后的服务端
- server_info= decrypt(request.body) # 对数据进行解密
- server_info = json.loads(server_info) # 对字符串反序列化成字典类型的数据
import json
import hashlib
import time
from django.shortcuts import render, HttpResponse
from repository import models
from api.service import PluginManager
from autoserver import settings
def decrypt(msg):
from Crypto.Cipher import AES
key = b'jlaksdflj77asdfh' # 需要同样的key
cipher = AES.new(key, AES.MODE_CBC, key)
result = cipher.decrypt(msg) # 对字节进行解密
data = result[0:-result[-1]]
return data.decode('utf8')
# 在全局(存在内存中)建立已访问列表,格式是{'key秘钥|时间':超时时间} 后期可以用redis memcache等直接设置超时时间
api_key_record = {
}
def asset(request):
client_md5_citme = request.META.get('HTTP_OPENKEY') # 客户端发送过来的MD5和用于加密的时间
# print(client_md5_citme)
client_md5_key,client_time = client_md5_citme.split('|') # 进行分割
# 第一关:时间验证
client_time = float(client_time) # 把字符串类型的转化成数字
server_time = time.time()
if server_time - client_time > 10:
return HttpResponse("小伙子,超时了,第一关没有通过验证")
# 第二关:规则验证
temp = '%s|%s'%(settings.AUTH_KEY,client_time)
m = hashlib.md5()
m.update(bytes(temp, encoding='utf8'))
server_md5_key = m.hexdigest() # 服务端利用客户端发送的时间进行MD5加密的数据
if server_md5_key!= client_md5_key:
return HttpResponse('第二关没有通过验证')
# 在进入第三关之前删除api_key_record中的超时的值
# 字典不能通过for循环中删除
for k in list(api_key_record.keys()): # 需要把字典的keys()转换成列表
v = api_key_record[k]
if server_time > v:
del api_key_record[k] # 如果超时,则删除api_key_record中相应的记录
# 第三关:建立访问列表(全局)
if client_md5_citme in api_key_record:
return HttpResponse('第三关没有通过验证')
else:
api_key_record[client_md5_citme] = client_time + 10 # 设置超时时间 10s后超时,然后从列表中删除
if request.method == "GET":
aaa = "重要的数据"
return HttpResponse(aaa)
elif request.method == 'POST':
server_info= decrypt(request.body) # 对数据进行解密
server_info = json.loads(server_info) # 对字符串反序列化成字典类型的数据
# print(server_info)
# 新资产信息
# server_info = json.loads(request.body.decode('utf-8')) # 数据存在于body中
print(server_info)
# 数据库中的老的资产信息
hostname = server_info['basic']['data']['hostname'] # 获取主机名
server_obj = models.Server.objects.filter(hostname=hostname).first() # 获得server对象
# print(server_obj) # 这里获得是主机名
if not server_obj:
return HttpResponse('当前主机名在资产中未录入')
asset_obj = server_obj.asset # 直接获得是资产对象 一对一的关系
PluginManager(hostname,server_info,asset_obj,server_obj).exec_plugin()
return HttpResponse('...')
CMDB 数据加密 最终整合API验证+AES数据加密的更多相关文章
- API验证及AES加密
API验证 API验证: a. 发令牌: 静态 PS: 隐患 key被别人获取 b. 动态令牌 PS: (问题越严重)用户生成的每个令牌被黑客获取到,都会破解 c. 高级版本 PS: 黑客网速快,会窃 ...
- php接口数据加密、解密、验证签名代码实例
php接口数据加密.解密.验证签名 代码非常easy,这里就不多废话了,直接奉上代码 <?php /** * 数据加密.解密.验证签名 * @edit http://www.lai18.com ...
- php接口数据加密、解密、验证签名【转】
<?php/** * 数据加密,解密,验证签名 * @edit http://www.lai18.com * @date 2015-07-08 **///header('Content-Type ...
- Oracle TDE的数据加密示例并用logminer验证加密效果
1.确认数据库版本 2创建密钥钱包 3创建加密列的表并初始值 4演示TDE的数据加密示例 5 logminer验证加密效果
- CMDB API验证
CMDB API验证 为什么做API验证 API验证是防止数据在传输的过程中,保证数据不被篡改 如何设计的API验证 灵感来源于Torando中加密Cookie的源码,主要是生成加密的随机字符串. M ...
- CMDB服务器管理系统【s5day90】:API验证
1.认证思路刨析过程 1.请求头去哪里拿? 1.服务器端代码: def test(request): print(request) return HttpResponse('你得到我了') 2.客户端 ...
- API验证
API验证说明 API验证: a. 发令牌: 静态 PS: 隐患 key被别人获取 b. 动态令牌 PS: (问题越严重)用户生成的每个令牌被黑客获取到,都会破解 c. 高级版本 PS: 黑客网速快, ...
- python API验证
API验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 API验证: a. 发令牌: 静态 PS: 隐患 key ...
- SpringBoot+Swagger整合API
SpringBoot+Swagger整合API Swagger:整合规范的api,有界面的操作,测试 1.在pom.xml加入swagger依赖 <!--整合Swagger2配置类--> ...
随机推荐
- LeetCode: 669 Trim a Binary Search Tree(easy)
题目: Given a binary search tree and the lowest and highest boundaries as L and R, trim the tree so th ...
- python矩阵相加
举个栗子: # 两个 3 行 3 列的矩阵,实现其对应位置的数据相加,并返回一个新矩阵: # 使用 for 迭代并取出 X 和 Y 矩阵中对应位置的值,相加后放到新矩阵的对应位置中. import n ...
- PHP操作Redis常用技巧总结【转】
一.Redis连接与认证 //连接参数:ip.端口.连接超时时间,连接成功返回true,否则返回false $ret = $redis->connect('127.0.0.1', 6379, 3 ...
- 洛谷P2217 [HAOI2007]分割矩阵
P2217 [HAOI2007]分割矩阵 题目描述 将一个a*b的数字矩阵进行如下分割:将原矩阵沿某一条直线分割成两个矩阵,再将生成的两个矩阵继续如此分割(当然也可以只分割其中的一个),这样分割了(n ...
- 洛谷 P2731 骑马修栅栏 Riding the Fences
P2731 骑马修栅栏 Riding the Fences 题目背景 Farmer John每年有很多栅栏要修理.他总是骑着马穿过每一个栅栏并修复它破损的地方. 题目描述 John是一个与其他农民一样 ...
- 黑马旅游网 解析url查询字符串
function getUrlParam(name) { let reg = new RegExp("(^|&)" + name + "=([^&]*)( ...
- 第一个Three.js程序——加入相机
- IOS 打包提示 No iTunes Connect access for the team
1.可以在提示页直接点击 Manage Accounts按钮,进去页面后,选择开发者账号点减号删除:(或者在Xcode中直接按command+键进入,选择accounts,选中账号按-键删除) 2.完 ...
- jqeury实现全选和反选
注意:对于input获取属性不能用attr(),只能用prop().不然出现undefined. 第一版: <!DOCTYPE html> <html lang="en&q ...
- 寒假作业第二组C题题解
这道题题意很简单,主要是练习map的使用.看输入有三个数据,水果名,地名,和出现次数.再看输出,很容易想到map<string,int> string是水果,int是次数,那个地名怎么用m ...