CMDB API验证
CMDB API验证
为什么做API验证
API验证是防止数据在传输的过程中,保证数据不被篡改
如何设计的API验证
灵感来源于Torando中加密Cookie的源码,主要是生成加密的随机字符串。
MD5(key+time)|time
就是把秘钥和客户端的当前时间通过MD5进行加密,同时把当前时间发送到API
然后自己在服务端设计了三关验证的设计:
- 时间,
- 算法规则,验证密文
- 已访问的记录
想API获取数据的时候,需要进行验证是否是合法的请求,为了达到这一目的,采用的基础方案是在装机的时候在服务器中和客户端中都保存一份秘钥,客户端想要获取数据的时候需要携带秘钥过来,GET请求的时候通过请求头发送。
API验证之字符串+请求头
客户端向API发送GET请求的时候可以在请求有中加数据,是字典的格式headers={},在Django中,自己定义的请求头发送过去后,会转换成大写,并在前面加上HTTP_。
客户端,header中的OpenKey中间不能有下划线,否则后台接收不到
import requests
key = 'sdflajskldf9asfdalsdf9sdgsdgs55523asdf'
response = requests.get("http://127.0.0.1:8000/api/asset.html", headers={'OpenKey': key})
print(response.text)
服务端接收数据中获取请求头的中的内容,通过request.MEAT
可以获取全部的请求信息,是一个字典类型。
def asset(request):
for k, v in request.META.items():
print(k, v)
if request.method == "GET":
aaa = "重要的数据"
return HttpResponse(aaa)
HTTP_OPENKEY sdflajskldf9asfdalsdf9sdgsdgs55523asdf
当客户端发送数据的请求头中没有和服务端的key匹配的时候,不通过验证
key = request.META.get('HTTP_OPENKEY')
if settings.AUTH_KEY != key:
return HttpResponse('不能通过验证')
if request.method == "GET":
aaa = "重要的数据"
return HttpResponse(aaa)
动态令牌
上面的是静态的,当网络请求被截获后,别人就可以可以利用这个漏洞。需要改良成动态的令牌,每次访问的时候客户端发送不同的随机字符串并进行加密。要动态用当前时间,或者是UUID
- 利用当前时间和key形成动态的随机字符串
- 通过MD5进行加密
- 把当前时间发送到API,API利用这个时间进行MD5加密,然后比较密文
客户端
import requests
import time
import hashlib
ctime = time.time()
key = 'sdflajskldf9asfdalsdf9sdgsdgs55523asdf'
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利用时间进行加密,最后比较的是密文
response = requests.get("http://127.0.0.1:8000/api/asset.html", headers={'OpenKey': md5_key_time})
print(response.text)
服务端
- 服务端接收到客户端发送的数据中包含MD5和客户端用于加密的时间
- 服务端利用客户端发送过来的时间进行MD5加密
- 客户端和服务端进行密文的验证
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('|') # 进行分割
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('不能通过验证')
if request.method == "GET":
aaa = "重要的数据"
return HttpResponse(aaa)
API三关验证
上面存在的问题,当被加密的md5_key_time别截获任意一个后(有成千上万个),别人在任何时候都能发送请求,服务端都能返回真实数据,这是一个很大的问题。
解决方案:
- 时间,服务端时间和客户端发送过来的时间进行比较,超过10s后,认为失效
- 在服务端维护一个已访问列表,仅仅是10s内的,超过后删除,如果有同样的再次访问的话,认为是被被人获取的,不通过验证
第一关:时间
- 在服务端进行判断,如果发送过来的客户端的时间和当前时间比,超过10s后,排除
服务端
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('|') # 进行分割
temp = '%s|%s'%(settings.AUTH_KEY,client_time)
m = hashlib.md5()
m.update(bytes(temp, encoding='utf8'))
server_md5_key = m.hexdigest() # 服务端利用客户端发送的时间进行MD5加密的数据
client_time = float(client_time) # 把字符串类型的转化成数字
server_time = time.time()
if server_time - client_time > 10:
return HttpResponse("小伙子,超时了")
客户端
客户端能正常发送,如果在10s内,客户端发送的秘钥被获取,并在10s内被请求,也是可以通过的
下面的key是黑客获取的秘钥,并在10s中内向API发送请求,同样是可以获取到数据的
import requests
key = '582d3d4bd95997954f70ae6906732de3|1501941968.090018'
response = requests.get("http://127.0.0.1:8000/api/asset.html", headers={'OpenKey': key})
print(response.text)
第二关:验证规则
这里就是在服务端进行MD5的验证
第三关: 建立已经访问的列表
如果是只采用时间的话,是有缺陷的,所以通过建立一个已经访问的列表进行进一步限制。即便是黑客获取了秘钥,当黑客发送请求的时候就能阻止黑客的请求
- 在全局建立已经访问过的列表,这里采用字典的形式,key是客户端发送过来的密钥,value是超时时间
- 后期使用redis memcache可以直接设置超时时间后自动回删除,这里是存在内存中,自己通过判断进行删除
# 在全局(存在内存中)建立已访问列表,格式是{'key秘钥|时间':超时时间} 后期可以用redis memcache等直接设置超时时间
api_key_record = {
}
# 在进入第三关之前删除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后超时
关于字典删除的补充
字典、列表在for循环的过程中是不同删除的,在for循环的时候,就已经建立了需要遍历的数据,在循环中删除,就没法迭代了
例如:
下面的程序会报错:RuntimeError: dictionary changed size during iteration
# 字典 列表等在循环的时候不能删除
api_dict = {'k1':'v1','k2':'v2','k3':'v3'}
for k,v in api_dict.items():
if v == 'v2':
del api_dict[k] # RuntimeError: dictionary changed size during iteration
解决方法:
- deepcopy
- list(api_dict.keys())
- 看下面的输出结果可知字典的keys()得到的是字典一个一种列表,通过list转换成列表
- 循环获取字典中的value进行比较,然后进行删除
api_dict = {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
print(api_dict.keys()) # dict_keys(['k1', 'k2', 'k3'])
print(list(api_dict.keys())) # 通过list转换成列表
for k in list(api_dict.keys()):
v = api_dict[k] # 获取字典中所有的值value
if v == 'v2':
del api_dict[k]
print(api_dict)
结果:
dict_keys(['k1', 'k2', 'k3'])
['k1', 'k2', 'k3']
{'k1': 'v1', 'k3': 'v3'}
API三关验证总结
流程:
客户端和服务端中都存放一份相同的随机字符串,客户端发请求的时候把随机字符串和当前时间进行MD5加密,同时带着当前时间通过请求头发送到API,进入三关验证。
- 第一关是时间验证 (验证服务器当前时间和客户端发送过来的时间,超过10s后,验证不通过)
- 第二关是MD5规则验证(服务端把自己的密钥同客户端发送过来的时间进行MD5加密,进行密文的比较)
- 第三关是访问列表验证(从访问列表中查询是否存在,如果存在,验证不通过,否则把当前值存到列表中,并设置超时时间),这里的时间可以设置成2S
# 在全局(存在内存中)建立已访问列表,格式是{'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)
CMDB API验证的更多相关文章
- CMDB 数据加密 最终整合API验证+AES数据加密
当CMDB运行在内网的时候,经过API验证的三关是没有问题的,但是如果运行在外网,有一个问题是,黑客截取后的访问速度比客户端快的时候还会造成数据泄露.为了解决这个问题,就要对数据进行加密 RSA加密 ...
- CMDB服务器管理系统【s5day90】:API验证
1.认证思路刨析过程 1.请求头去哪里拿? 1.服务器端代码: def test(request): print(request) return HttpResponse('你得到我了') 2.客户端 ...
- 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 ...
- API验证及AES加密
API验证 API验证: a. 发令牌: 静态 PS: 隐患 key被别人获取 b. 动态令牌 PS: (问题越严重)用户生成的每个令牌被黑客获取到,都会破解 c. 高级版本 PS: 黑客网速快,会窃 ...
- API验证插件
前言 如果在访问某WebAPI过程中request信息被他人截获,若是get请求获取数据还好,如果是post提交数据,势必威胁数据安全,所以对于一个对安全性要求较高的API来说,对每个请求做身份验证显 ...
- API验证
API验证说明 API验证: a. 发令牌: 静态 PS: 隐患 key被别人获取 b. 动态令牌 PS: (问题越严重)用户生成的每个令牌被黑客获取到,都会破解 c. 高级版本 PS: 黑客网速快, ...
- 基于 JWT-Auth 实现 API 验证
基于 JWT-Auth 实现 API 验证 如果想要了解其生成Token的算法原理,请自行查阅相关资料 需要提及的几点: 使用session存在的问题: session和cookie是为了解决http ...
- CMDB资产管理系统开发【day27】:cmdb API安全认证
1.API验证分析 API三关验证 客户端和服务端中都存放一份相同的随机字符串,客户端发请求的时候把随机字符串和当前时间进行MD5加密,同时带着当前时间通过请求头发送到API,进入三关验证. 第一关是 ...
- Django学习手册 - 基于requests API验证(二)
原理分析: API接口验证 1.一个认证的key server端 和 client端都必须有这么一个认证key. 2.认证登录的时间限定 3.保存已验证的信息,在以后的验证不能再次登录 client ...
随机推荐
- MVC用户验证
MVC提供了四种Filter(钩子),用于在Action执行之前或者之后,我们能够做一些事情,比如说判断有没有登录,比如说判断有没有权限. IAuthorizationFilter:在所有Filter ...
- c++弱引用与强引用
https://www.zhihu.com/question/26851369 智能指针一个很重要的概念是"所有权",所有权意味着当这个智能指针被销毁的时候,它指向的内存(或其它资 ...
- Noip2016day2 组合数问题problem
题目描述 组合数表示的是从n个物品中选出m个物品的方案数.举个例子,从(1,2,3) 三个物品中选择两个物品可以有(1,2),(1,3),(2,3)这三种选择方法.根据组合数的定 义,我们可以给出计算 ...
- 2017百度之星程序设计大赛 - 复赛 Arithmetic of Bomb
http://acm.hdu.edu.cn/showproblem.php?pid=6144 解法:一个简单的模拟 #include <bits/stdc++.h> using names ...
- CSS3动画总结学习(一)
参考文章: CSS3 Transitions, Transforms和Animation使用简介与应用展示 CSS 参考手册 动画的分类 平移动画 transform: 就是变换, 变换, 变换 也就 ...
- Java通过图片url地址获取图片base64位字符串的两种方式
工作中遇到通过图片的url获取图片base64位的需求.一开始是用网上的方法,通过工具类Toolkit,虽然实现的代码比较简短,不过偶尔会遇到图片转成base64位不正确的情况,至今不知道为啥. 之后 ...
- myeclipse 最佳设置
http://www.cnblogs.com/wuyifu/p/3593035.html
- zabbix agent 配置
http://blog.csdn.net/z644041867/article/details/76618644 https://www.cnblogs.com/miclesvic/p/6144924 ...
- shell 经典
使用新写法 这里的新写法不是指有多厉害,而是指我们可能更希望使用较新引入的一些语法,更多是偏向代码风格的,比如 尽量使用func(){}来定义函数,而不是func{} 尽量使用[[]]来代替[] 尽量 ...
- Shell分割字符得到数组
#!/bin/bash p=$(hadoop fs -ls /tgl/data |awk '{print $8}') #要将$a分割开,先存储旧的分隔符 OLD_IFS="$IFS" ...