今天在新公司开了个关于公网接口安全性校验的讨论会,貌似这边做这种对公的接口比较少,所以组长就组织大家都参与了一下。

我之前做这种接口做得比较多,对服务端、客户端的都有,加密方式也比较多样,但是仔细想来自己从来没有进行过比较系统的总结。

首先我们组长总结了接口安全性验证的三个核心点:

1.请求不能被篡改
2.请求不能被重放
3.请求可以被溯源

我就针对这三点来分别说说为了实现这三点一般会采用哪些手段。

不能被篡改

这一点是最基础的,之前的实际工作中其实很大一部分接口都只做到了这一点。这一点也可以看作是鉴权(authentication)。

一般常见的对请求合法性进行校验的方式如下:

  • 将参数按一定规则进行拼接,然后加上加密用的 key (或者叫 secret)进行 md5、sha1 之类的 hash,将 hash 值作为签名放在参数或者 http 请求头中进行传递
  • 将参数进行 aes 加密,这种方式的话可以单独对参数值进行 aes 加密,也可以将所有的参数序列化后整个进行 aes 加密
  • RSA 非对称加密,这种方式一般将所有参数序列化后整段进行 RSA 加密,由于非对称加密的资源消耗较大,所以有些应用会采用通过非对称加密方式获取 secret,然后后续请求转为对称加密的方式
  • 其他自定义对称加密算法,这种一般也是把所有参数序列化后通过自定义对称加密算法将其转化成一个可以被解密的字符串进行传递

不能被重放

这一点是为了防止请求链接被拦截后,恶意攻击者通过重复相同的请求对系统造成危害。从根本上来说,防止重放是需要提供接口的服务端能保证对外接口的幂等性。

防止重放的最简易的一个方式是在请求参数中加入时间戳,在处理接口请求时验证时间戳是否过期,过期则判定请求失效。这种方式存在两个弊端,第一点就是允许的时间范围内重复请求仍然可以生效,另一点是林子大了什么鸟都有,不同公司的时间服务器可能存在不同步,或者说不是所有的公司时间都和北京时间同步。

对于仅进行读操作的接口来说由于本身就是幂等的,只要有时间戳其实就可以了,然而对于包含写操作的接口来说,显然单纯的时间戳并不能满足这个需求。不过加入时间戳还有一个用处是相当于加了一层缓存,重复的失效请求在最外层就被屏蔽了,不会给服务器增加业务处理逻辑的压力。

一般实现保障接口请求幂等的方式是在请求参数中增加一个唯一性字段作为标识符,如支付、加币等类似业务中的订单号(或流水号)的概念。

另外也有一些类型的接口中会用 nonce 之类的一次性令牌来防止请求重放,这种方式就和 oauth 协议中使用的 code 基本差不多。

可以被溯源

接口请求可以被溯源对很多应用场景其实都有用,比如:

  • 方便异常的排查,异常请求可直接找到对应请求人
  • 便于特殊情况,比如部分用户密钥泄漏等的处理
  • 对接口进行分用户授权(authorization)
  • 可以进行详细数据统计

比较常见的一些云服务的服务提供商的接口常用的方式就是提供给接口使用者对应的 app_id 和 app_secret(叫法各有不同),其中 app_id 就是接口使用者的身份标识符,该标识符随其他请求参数一起传递,供接口提供者确定请求来源,并取出该标识符对应的使用者的 app_secret 来进行接口的鉴权和授权判断。