为提高数据库安全,有必要对通过API的来访者进行判断是否为合法客户端。
通过验证签名方式。将key提前放在合法客户端,访问服务端时将key放在请求头,服务端进行验证是否为合法key。
但此方式,key是明文,被截获则泄漏。所以,必须加密。
在客户端通过MD5加密发送key,服务端收到后,取出存在服务端的key加密后比对收到的密文。
自此,我们实现密文发送。但是,密文被截获后仍然可以直接登录服务端,所以,密文必须是变化的。
我们将客户端发送的密文改为key与时间戳的组合的加密后结果,实现密文每一次都不同,并将时间戳放入请求头。在服务端收到密文后,通过从请求头中取出时间戳,并与key 组合加密后 与 密文比对,来执行验证。
然后,我们发现过去产生的所有密文都可以通过验证。
我们必须加入对密文时效性的限制。我们在请求头取出时间戳后将与当前时间戳比对,如超过一定时间则无法通过验证。
我们又考虑,黑客在截获密文后马上访问可能可以赶在密文失效前访问,所有我们将使用过的密文记录在服务端,当密文已经使用,那当前访问便无法通过验证。
client
def md5(arg): hs = hashlib.md5() hs.update(arg.encode('utf-8')) return hs.hexdigest() key = "asdfuasodijfoausfnasdf" ctime = str(time.time()) new_key = "%s|%s" %(key,ctime,) # asdfuasodijfoausfnasdf|时间戳 md5_str = md5(new_key) # 6f800b6a11d3f9c08c77ef8f77b2d460, # asdfuasodijfoausfnasdf|时间戳 auth_header_val = "%s|%s" %(md5_str,ctime,) # 6f800b6a11d3f9c08c77ef8f77b2d460|时间戳 print(auth_header_val) response = requests.get('http://127.0.0.1:8000/api/test.html',headers={'auth-api':auth_header_val}) print(response.text)
server
def md5(arg): hs = hashlib.md5() hs.update(arg.encode('utf-8')) return hs.hexdigest() key = "asdfuasodijfoausfnasdf" # redis,Memcache visited_keys = { # "841770f74ef3b7867d90be37c5b4adfc":时间, 10 } def api_auth(func): def inner(request,*args,**kwargs): server_float_ctime = time.time() auth_header_val = request.META.get('HTTP_AUTH_API') # 841770f74ef3b7867d90be37c5b4adfc|1506571253.9937866 client_md5_str, client_ctime = auth_header_val.split('|', maxsplit=1) client_float_ctime = float(client_ctime) # 第一关 时效性验证 if (client_float_ctime + 20) < server_float_ctime: return HttpResponse('时间太久了,再去买一个吧') # 第二关: 加密规则 server_md5_str = md5("%s|%s" % (key, client_ctime,)) if server_md5_str != client_md5_str: return HttpResponse('休想') # 第三关: 是否已使用密文 if visited_keys.get(client_md5_str): return HttpResponse('你放弃吧,来晚了') visited_keys[client_md5_str] = client_float_ctime return func(request,*args,**kwargs) return inner @api_auth def test(request): return HttpResponse('正常用户')