接口安全问题
- 请求身份是否合法?
- 请求参数是否被篡改?
- 请求是否唯一?
AppId&AppSecret
请求身份
为开发者分配AppId(开发者标识,确保唯一)和AppSecret(用于接口加密,确保不易被穷举,生成算法不易被猜测)。
防止篡改
参数签名
- 按照请求参数名的字母升序排列非空请求参数(包含AppId),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringSignTemp;
- 对stringSignTemp进行SHA256运算,并将得到的字符串所有字符转换为大写,得到sign值。
请求携带参数AppId和Sign,只有拥有合法的身份AppId和正确的签名Sign才能放行。这样就解决了身份验证和参数篡改问题,即使请求参数被劫持,由于获取不到AppSecret(仅作本地加密使用,不参与网络传输),无法伪造合法的请求。
重放攻击
虽然解决了请求参数被篡改的隐患,但是还存在着重复使用请求参数伪造二次请求的隐患。
timestamp+nonce方案
nonce指唯一的随机字符串,用来标识每个被签名的请求。通过为每个请求提供一个唯一的标识符,服务器能够防止请求被多次使用(记录所有用过的nonce以阻止它们被二次使用)。
然而,对服务器来说永久存储所有接收到的nonce的代价是非常大的。可以使用timestamp来优化nonce的存储。
假设允许客户端和服务端最多能存在15分钟的时间差,同时追踪记录在服务端的nonce集合。当有新的请求进入时,首先检查携带的timestamp是否在15分钟内,如超出时间范围,则拒绝,然后查询携带的nonce,如存在已有集合,则拒绝。否则,记录该nonce,并删除集合内时间戳大于15分钟的nonce(可以使用redis的expire,新增nonce的同时设置它的超时失效时间为15分钟)。
实现
请求接口:https://api.ssf.top/validate?b=13888888888&a=102
- 生成当前时间戳timestamp=now()和唯一随机字符串nonce=random()
- 生成签名SHA256(appId+timestamp+nonce+param+appSecret).toUpperCase()
- 如果是请求路径携带参数,则参数名升序配列后组装成请求参数串,如https://api.xxf.top/validate?b=13888888888&a=102,这里param为a=13888888888&b=102
- 如果是POST请求,并通过body传参,则param取body体的内容
- md5编码后统一转换为大写
- 最终将appId,timestamp,nonce,及签名后的sign值设置到请求头中
网关端: