当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数据加密的更多相关文章

  1. API验证及AES加密

    API验证 API验证: a. 发令牌: 静态 PS: 隐患 key被别人获取 b. 动态令牌 PS: (问题越严重)用户生成的每个令牌被黑客获取到,都会破解 c. 高级版本 PS: 黑客网速快,会窃 ...

  2. php接口数据加密、解密、验证签名代码实例

    php接口数据加密.解密.验证签名 代码非常easy,这里就不多废话了,直接奉上代码 <?php /** * 数据加密.解密.验证签名 * @edit http://www.lai18.com ...

  3. php接口数据加密、解密、验证签名【转】

    <?php/** * 数据加密,解密,验证签名 * @edit http://www.lai18.com * @date 2015-07-08 **///header('Content-Type ...

  4. Oracle TDE的数据加密示例并用logminer验证加密效果

    1.确认数据库版本 2创建密钥钱包 3创建加密列的表并初始值 4演示TDE的数据加密示例 5 logminer验证加密效果

  5. CMDB API验证

    CMDB API验证 为什么做API验证 API验证是防止数据在传输的过程中,保证数据不被篡改 如何设计的API验证 灵感来源于Torando中加密Cookie的源码,主要是生成加密的随机字符串. M ...

  6. CMDB服务器管理系统【s5day90】:API验证

    1.认证思路刨析过程 1.请求头去哪里拿? 1.服务器端代码: def test(request): print(request) return HttpResponse('你得到我了') 2.客户端 ...

  7. API验证

    API验证说明 API验证: a. 发令牌: 静态 PS: 隐患 key被别人获取 b. 动态令牌 PS: (问题越严重)用户生成的每个令牌被黑客获取到,都会破解 c. 高级版本 PS: 黑客网速快, ...

  8. 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 ...

  9. SpringBoot+Swagger整合API

    SpringBoot+Swagger整合API Swagger:整合规范的api,有界面的操作,测试 1.在pom.xml加入swagger依赖 <!--整合Swagger2配置类--> ...

随机推荐

  1. 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 ...

  2. python矩阵相加

    举个栗子: # 两个 3 行 3 列的矩阵,实现其对应位置的数据相加,并返回一个新矩阵: # 使用 for 迭代并取出 X 和 Y 矩阵中对应位置的值,相加后放到新矩阵的对应位置中. import n ...

  3. PHP操作Redis常用技巧总结【转】

    一.Redis连接与认证 //连接参数:ip.端口.连接超时时间,连接成功返回true,否则返回false $ret = $redis->connect('127.0.0.1', 6379, 3 ...

  4. 洛谷P2217 [HAOI2007]分割矩阵

    P2217 [HAOI2007]分割矩阵 题目描述 将一个a*b的数字矩阵进行如下分割:将原矩阵沿某一条直线分割成两个矩阵,再将生成的两个矩阵继续如此分割(当然也可以只分割其中的一个),这样分割了(n ...

  5. 洛谷 P2731 骑马修栅栏 Riding the Fences

    P2731 骑马修栅栏 Riding the Fences 题目背景 Farmer John每年有很多栅栏要修理.他总是骑着马穿过每一个栅栏并修复它破损的地方. 题目描述 John是一个与其他农民一样 ...

  6. 黑马旅游网 解析url查询字符串

    function getUrlParam(name) { let reg = new RegExp("(^|&)" + name + "=([^&]*)( ...

  7. 第一个Three.js程序——加入相机

  8. IOS 打包提示 No iTunes Connect access for the team

    1.可以在提示页直接点击 Manage Accounts按钮,进去页面后,选择开发者账号点减号删除:(或者在Xcode中直接按command+键进入,选择accounts,选中账号按-键删除) 2.完 ...

  9. jqeury实现全选和反选

    注意:对于input获取属性不能用attr(),只能用prop().不然出现undefined. 第一版: <!DOCTYPE html> <html lang="en&q ...

  10. 寒假作业第二组C题题解

    这道题题意很简单,主要是练习map的使用.看输入有三个数据,水果名,地名,和出现次数.再看输出,很容易想到map<string,int> string是水果,int是次数,那个地名怎么用m ...