API验证
在web应用中,服务端提供的API接口必须有某种安全机制,来识别Http请求是否合法,以防别有用心之人截获http请求,对服务端进行攻击的行为。这里,参考Tornado签名cookie源码,来实现一个API验证的功能。
基本思路:
- 服务端给有权限访问的客户端发放key,客户端每次访问时携带key给服务端进行验证。
- 为了防止请求被截获后key泄露,将key与当前时间进行加密后生成动态密钥。
- 请求被截获后,不法分子依然能够直接使用这个动态密钥通过服务端的验证。因此服务端增加有效期验证,比如5S超时,超过这个时间的请求无法通过验证。
- 如果不法分子截获请求,并在超时前访问API,还是有风险。因此服务端增加第一次访问验证,同样密钥如果是第二次请求,无法通过。
下面我们在Django中实践一下:
新建项目API_AUTH,创建应用app01,定义路由如下:
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^api_auth/$', views.api_auth), ]
写一个脚本test.py,通过request模块对上述路由发起Http请求
import requests, time, hashlib key = 'asdgasgewfqsef' url = 'http://127.0.0.1:8000/server.html' # 将key与当前时间加密生成动态密钥token timestamp = time.time() temp = '{key}|{time}'.format(key=key,time=timestamp) md5_str = hashlib.md5(temp.encode('utf-8')).hexdigest() token = '{md5_str}|{time}'.format(md5_str=md5_str, time=timestamp) # 在Http请求头中携带token response = requests.get(url, headers={"auth-key": token}) print(response.text) # 模拟超时发送请求 fake = token time.sleep(6) response = requests.get(url, headers={'auth-key': fake}) print('second...',response.text)
说明:
- 通过
headers={key: value}
发送自定义请求头
- 通过
服务端api_auth视图函数:
def api_auth(request, *args, **kwargs): key = 'asdgasgewfqsef' # 服务端与客户端持有同样的key temp_key = { # client_md5: timestamp --> 过期自动清除, 判断是第一次请求 # 通过redis,Memcache来做过期自动清除 } token = request.META.get('HTTP_AUTH_KEY') # 提取请求头中的密钥 print('token...',token) # 6e632f8e858e07ffcc3b636a1577121b|1506983215.7659595 # 分割出客户端动态key和时间字符串 client_md5 = token.split('|')[0] timestamp = token.split('|')[1] # 用客户端的时间字符串和服务端的key,生成服务端动态key temp = '{key}|{time}'.format(key=key, time=timestamp) server_md5 = hashlib.md5(temp.encode('utf-8')).hexdigest() now = time.time() if float(timestamp) + 5 < now: # 5s过期验证 return HttpResponse('呵呵') elif server_md5 != client_md5: # 动态key验证 return HttpResponse('呵呵') elif temp_key[client_md5]: # 第一次访问验证 return HttpResponse('呵呵') else: # 验证通过,将这次的请求的动态key和时间戳作为记录保存,以作下次第一次访问验证。 temp_key[client_md5] = timestamp return HttpResponse('验证通过')
说明:
- 自定义请求头的信息从
request.META
中获取; - 请求头中的
'auth-key'
会被转化为HTTP_AUTH_KEY
- temp_key中的记录后期通过redis,Memcache来做过期自动清除, 参考AES加密
- 自定义请求头的信息从
封装成装饰器,可以给需要的视图函数使用
def api_auth(func): def deco(request, *args, **kwargs): key = 'asdgasgewfqsef' # 服务端与客户端持有同样的key # 同上...... now = time.time() if float(timestamp) + 5 < now: # 5s过期验证 return HttpResponse('呵呵') elif server_md5 != client_md5: # 动态key验证 return HttpResponse('呵呵') elif temp_key[client_md5]: # 第一次访问验证 return HttpResponse('呵呵') else: # 验证通过,将这次的请求的动态key和时间戳作为记录保存,以作下次第一次访问验证。 temp_key[client_md5] = timestamp return func(request, *args, **kwargs) return deco @api_auth def view_func(): pass