[toc]
# 接口常见的安全问题
* 数据篡改
* DOS攻击/高频请求
# 接口安全的解决策略
* 全参加密
* 一次性接口(记录+超时)
# 代码思路
```[sequence]
title:接口请求时序图
participant APP as app
participant 服务器 as server
participant 服务器缓存 as cache
app->server: APP带参数访问服务器
note left of app: 检查请求是否包含安全参数
server->server:参数中是否包含安全参数time
server-->app: 返回错误: 缺少安全参数 time!
server->server:参数中是否包含安全参数token
server-->app: 返回错误: 缺少安全参数 token!
note left of app: 确保接口请求的一次性
server->cache: 以token为键名查询缓存的请求记录
cache-->server: 返回请求记录查询结果
server-->app: 返回错误: 该接口已经请求过一次!
note left of app: 确保接口请求没有超时
server->server: 请求时间戳和服务器时间戳比对
server-->app: 返回错误: 请求超时!
note left of app: 确保接口数据没有篡改
server->server: 参数时间戳加密生成随机字符串
server->server: 参数去除token后排序并加密, 生成token
server->server: 把参数中的token和服务器生成的token进行比对
server-->app: token不正确!
note left of app: 缓存接口请求记录
server->cache: 把此次请求写入缓存
server->cache: 清除缓存中超时的记录
note left of app: 通过验证
server->server: 进行下面的逻辑运算...
```
# 代码示例(python2)
```python
# 建立token缓存, 实现接口一次性处理
token_cache_dict = dict()
# 构造返回数据
private_key = 'public_bike_project'
response_data = dict()
response_data['data'] = dict()
# 获取post过来的参数
post_dicts = self.request.body_arguments
post_dict = dict()
for k, v in post_dicts.items():
post_dict[k] = v[0]
# 判断是否有time和token这两个关键参数
if not post_dict.has_key('time'):
response_data['ret'] = 2
response_data['data']['msg'] = '缺少安全参数 time!'
self.write(json.dumps(response_data))
return False
if not post_dict.has_key('token'):
response_data['ret'] = 2
response_data['data']['msg'] = '缺少安全参数 token!'
self.write(json.dumps(response_data))
return False
# 如果有, 把这两个关键参数存起来
app_time = post_dict['time']
app_token = post_dict['token']
# 接口一次性认证(缓存中是否有相同token)
if self.token_cache_dict.has_key(app_token):
response_data['ret'] = 2
response_data['data']['msg'] = '该接口已经请求过一次!'
self.write(json.dumps(response_data))
return False
# 获取服务器毫秒级时间戳, 用于比对接口请求是否超时
server_time = int(round(time.time() * 1000))
# 判断请求是否超时(300秒)
if (server_time - int(app_time)) > 300000:
response_data['ret'] = 1
response_data['data']['msg'] = '请求超时!'
self.write(json.dumps(response_data))
return False
# 生成随机字符串, 生成token时要用
noncestr = self.make_sign(self.make_sign(app_time) + private_key)
# 取出token
app_token = post_dict['token']
post_dict.pop('token')
# 所有参数排序后加key, md5
pre_str = self.sort_data(post_dict)
str_for_token = pre_str + "&key=" + noncestr
server_token = self.make_sign(str_for_token)
# 根据参数生成token, 比对token
if app_token != server_token:
response_data['ret'] = 2
response_data['data']['msg'] = 'token不正确!'
self.write(json.dumps(response_data))
return False
# 最终验证通过, 把token缓存中超时的token去掉, 并把本次token加入token缓存
server_time = int(round(time.time() * 1000))
for k, v in self.token_cache_dict.items():
if (server_time - v) > 300000:
del self.token_cache_dict['k']
# 请求没有问题, 把此次请求记录写入缓存
self.token_cache_dict[app_token] = server_time
return True
```
# 扩展阅读
* [API接口安全性设计](https://www.jianshu.com/p/c6518a8f4040)
- 打造高逼格接口管理平台
- 开篇
- 课程简介
- 聊聊接口平台
- 接口平台简介
- 优雅的使用看云
- 接口和markdown
- 接口文档版本演进
- 微软的硬菜--vscode
- markdown基础语法
- markdown进阶语法--流程图
- markdown进阶语法--时序图
- markdown进阶语法--API文档
- 接口文档的基本概念
- 接口管理平台的基本元素
- 编写接口文档并且发布更新
- 接口安全
- 文档安全
- 接口安全
- Git化你的文档
- 使用Git管理文档
- 自动化
- 自动化文档更新
- 收尾
- 如何反馈问题
- 课程总结
- 示例
- 更新信息
- 查询历史天气
- markdown语法示例
- 流程图示例
- 时序图示例
- 登录/注册
- 数据字典示例
- 课程问题解答