Open API 中签名的使用

Open API 中签名的使用

什么是 Open API

在遥远的时代,每家公司都保有各自的数据和数据库,在此之上搭建专属的业务体系(APP或web service。这个时候数据库与APP的交互的接口可以暂时称之为内部API。随着数据量和广度的扩张,企业与外界的交互合作变得越来越频繁,某些时候需要双方互相传输数据,可完全将对方当做内部来对待是违反安全准则的。于是企业把部分对外服务的功能封装成一系列API并供对方或第三方使用,这种API就叫做Open API,而集合了各类功能的API的平台,就叫做开放平台。


可是随着接口的暴露,数据安全性就显得尤为重要了。Open API的签名就是为了保证数据安全性而产生的,那么以下提及的签名规则是参考国内各大厂的开放平台的设计。


Open API需要解决以下3个问题

请求是否合法:是否是我规定的那个人

请求是否被篡改:是否被第三方劫持并篡改参数

防止重复请求(防重放):是否重复请求


情况复现

假设服务方数据库内存有两张关于超级英雄的表:

对于每张表我们可以认为是一类微服务(mirco-service),对于某一类请求方只需指明请求参数即可,例子里是 heroname+movie。

正常的http请求应该是

www.sample.com/openapi/getmessage?name=spiderman&movie=Spider-Man:Homecoming

返回是:

可是这样的请求实在是太简陋了,除了必要的请求参数,其余的验证措施一个都没有,谁都可以调用。而且参数以明文信息传输,如果被截取,信息安全也无法得到保证。


签名生成规则

那么数据保有方为了控制调用权限,会要求第三方开发者在自己的网站进行注册,并为其分配唯一的appKey 、 appSecert和预定义的加密方式。

appKey 是为了保证该调用请求是平台授权过的调用方发出的,保证请求方唯一性,如 test01,如果发现 appKey 不再注册库中则认为该请求方不合法;

appSecert 是组成签名的一部分,增加暴力解密的难度的,通常是一段密文,如 SECERT_A;

预定义加密方式是双方约定好的加密方式,一般为散列非可逆加密,如 MD5、SHA1;

ok,我们设定签名设成规则:

  1. 将 APPKey 加入值请求参数
www.sample.com/openapi/getmessage?appKey=test01&name=spiderman&movie=Spider-Man:Homecoming

2. 将每个请求参数去除“=”,按ASCII升序按“参数1参数值1参数2参数值2……”的格式组成特定的字符串

appKeytest01movieSpiderManHomecomingnamespiderman

3. 将秘钥SECERT_A加入上述字符串的前后成为

SECERT_AappKeytest01movieSpiderManHomecomingnamespidermanSECERT_A

4. 按约定方式(如 SHA1)加密

2995c83bbc12b147c9b5645396e5700e6af92b7f

5. 拼接所有参数,组成API请求

第4步生成的字符串,就是我们的签名sign,也作为一个查询参数加入到我们的请求中那么请求就变成了

www.sample.com/openapi/getmessage?name=spiderman&movie=Spider-Man:Homecoming&sign=2995c83bbc12b147c9b5645396e5700e6af92b7f

平台服务器在接到这个请求之后,会将请求包中的所有参数按以上相同的方式进行加密。如果生成的参数签名一致,则签名通过,请求的合法性和请求参数都得到保护,不会被第三方劫持后篡改变为它用。


更进一步地

但是等等,以上措施依然不是最严谨的,虽然仿冒者无法轻易模仿签名规则再生成一模一样的签名,可事实上,如果仿冒者监听并截取到了请求片段,然后把签名单独截取出来模仿正式请求方欺骗服务器进行重复请求,这也会造成安全问题,这攻击方式就叫重放攻击(replay 攻击)。

我们可以通过加入 timestamp+nonce 两个参数来控制请求有效性,防止重放攻击。

请求端:timestamp由请求方生成,代表请求被发送的时间(需双方共用一套时间计数系统)随请求参数一并发出,并将 timestamp作为一个参数加入 sign 加密计算。

服务端:平台服务器接到请求后对比当前时间戳,设定不超过30s 即认为该请求正常,否则认为超时并不反馈结果(由于实际传输时间差的存在所以不可能无限缩小超时时间)。

但是这样仍然是仅仅不够的,仿冒者仍然有30秒的时间来模仿请求进行重放攻击。所以更进一步地,可以为sign 加上一个随机码(称之为盐值)这里我们定义为 nonce。


请求端:nonce 是由请求方生成的随机数(在规定的时间内保证有充足的随机数产生,即在30s 内产生的随机数重复的概率为0)也作为参数之一加入 sign 签名。

服务端:服务器接受到请求先判定 nonce 是否被请求过,如果发现 nonce 参数在规定时间是全新的则正常返回结果,反之,则判定是重放攻击。而由于以上2个参数也写入了签名当中,攻击方刻意增加或伪造 timestamp 和 nonce 企图逃过重放判定都会导致签名不通过而失败。

这样最终的请求是:

www.sample.com/openapi/getmessage?name=spiderman&movie=Spider-Man:Homecoming&timestamp=2018020419500034&nonce=ajklhggH&sign=4d5c01842f37d90651f9693783c6564279fed6f4

总结

按照以上的签名规则,最重要的秘钥是 appSecert,是每个调用方打死也不能告诉他人的参数,因为它不参与通讯,仅仅作为请求端和服务端两方知道的秘钥保证签名的唯一性。当然了有些平台也提供 IP 白名单服务,可以进一步防止Open API 被冒用。


后话

这是我在数据平台领域的第一篇文章,上述Open API 签名验证也算是业内通用的方法,如果各位有更先进的方法,欢迎指出。

下一篇预告: Open API 平台设计规则

编辑于 2018-05-19

文章被以下专栏收录