开放平台设计第1篇:接口安全
假如某天我创业了,我们公司具备非常强悍的数据服务能力。
于是我们考虑商业化,将这部分能力通过API的形式对外提供。
这就需要开发一个开放平台。如何设计这样的开放平台呢?首先第一个要处理的就是接口安全问题,而接口安全涉及到两个层面:身份识别和回放攻击。
身份识别
换句话说,怎么知道发请求的人是合法用户(客户)?
在很多文章中都会提到AppID、AppPublicKey、AppSecretKey这三个概念:
AppID:唯一标识一个用户或者客户
AppPublicKey:公钥,可对外暴露
AppSecretKey:私钥,必须严格保密
在绝大多数场景中,只要AppID和AppSecretKey就可以了。下面介绍具体用法:
客户端请求服务端时,需要先生成一个随机数nonce,然后使用私钥(AppSecretKey),对AppID、客户端当前时间戳timestamp、nonce字段生成一个签名signature(sign),然后连同这几个字段一起发给服务端。伪代码如下:
|
|
注意,私钥AppSecretKey绝对不应该出现在请求里。
服务端收到请求后,先通过请求参数里的AppID查到对应的AppSecretKey,使用同样的方法,对AppID、timestamp、nonce进行签名,然后判断和请求参数里的sign是否相等。如果相等,身份校验通过,否则校验失败
|
|
注意事项:
1,生成str时字段的摆放顺序,可以按照对field(AppId、timestamp、nonce)进行字典序排序。只要客户端和服务端都同时约定好就行。
2,timestamp和nonce还有别的用途。下面会继续介绍
3,一般说来,平台给每个客户只分配一个AppID,但是一个客户可以有多个AppSecretKey,不同的AppSecretKey用于不同的接口或者对应不同的权限。如果是这种情况,那么客户端发请求的时候需要在参数里把对应的AppPublicKey带上(仍然使用AppSecretKey进行签名),服务端收到请求后,通过AppID + AppPublicKey唯一定位对应的AppSecretKey。
回放攻击
使用上面的方案,身份识别的问题是解决了,但是还是存在回放攻击的问题:攻击者拿到链接后,重复给服务端发请求。如何解决呢?
这个本质上是请求判重问题。解决方法如下:
1,服务端收到请求后,拿出timestamp,和服务端当前时间进行比较,如果超出一个阈值(比如说十分钟),那拒绝该请求。否则继续。
2,服务端拿出nonce,去db/cache里查询。如果存在,说明请求重复,直接拒绝。如果不存在,正常处理请求,并把nonce写入db/cache。
由于nonce存储量较大,可以考虑设置一个过期时间。如果使用redis来做选型,则可以使用SetNX命令来实现。
有资料说nonce在客户端生成,有资料说nonce应该在服务端生成。个人感觉本质没有差别,都是用来唯一标识请求(request_uuid),而在客户端生成会更加自然一点。