上周在设计一个对安全要求很高的移动应用服务时,在接口安全性这块停滞了很久,网上找了许多资料,也问了几个朋友,但始终没找到最完善的解决方案,重点有三:

  1. 接口均需身份验证,同一账号不能多个客户端登录。
  2. 通信过程有数据被篡改可能。
  3. 客户端不可信,客户端既可能是受害者,也可能是攻击者。

我们逐一讨论:
首先,除登录外的接口均不能匿名调用,必须有身份证明,所以必须要有token,当登录验证完账户密码后,生成一个token的键值对,作为之后的身份认证信息。这里有几个点需要注意:

  • 不能将完整的token内容传到客户端,应当将token内容存到redis,将key值返回客户端,防止token在客户端被破解和修改内容。
  • key值不能使用纯时间戳,因为当用户量大的时候,很容易被批量扫到key值,所以我们采用10位随机字符串+时间戳的key生成方式,保证key无法被批量扫到。
  • token有效期不能过长,因为token在传输过程中反复出现,假如被抓包得到token值,第三方便能随意伪造客户端身份了。我在这个服务里将token的有效期设为5分钟,超时后需重新申请。
  • token如何续时,网上有两种常用方案,一种是将账户密码存到客户端,每次超时后自动调用登录接口,这种实现简单,但账户和密码哈希反复在通信过程中传输,增加了被抓取的风险,而且对我们的产品来说,无法保证终端的使用者就是账户拥有者,存在客户端很可能被窃取密码;另一种是增加一个有效期更长的refresh_token,用来申请新token用,但这种方案操作起来麻烦一些,需要签名来保证refresh_token不被盗。我最终采用的方案更接近第二种,登录时,记录设备码,之后用设备码当来申请token,当10天没有调用时,该记录失效,需要重新用账户密码登录。

其次通信过程有可能被抓包,被流量劫持,最直接而有效的方案是HTTPS,90%的中间人攻击都可以避免。但
客户端信任了第三方证书,或者客户端本身尝试通过改数据包绕过APP逻辑时,HTTPS就没有效果了。进一步的保证需要考虑以下几点:

  • 采用全文加密还是签名摘要。因为全文加密,无论是对称还是非对称,都需要大量的计算,非常影响运行速度,我们主要是为了防篡改,而非防偷窥,所以选择签名摘要。
  • 采用什么算法。我开始想过RSA签名,但密钥的生成分发会很麻烦,又不能硬编码所有的客户端用同一个密钥对。而哈希就足够保证明文不被篡改了,而且执行速度更快,所以最终决定用SHA256做签名,哈希的盐(密钥)随设备分配。
  • 哈希的盐(密钥),在登录后随设备记录一起生成并返回,客户端之后用这个盐(密钥)与明文一起参与签名。
  • 是否需要增加时间戳参与签名,使用时间戳参与签名的主要目的是防止重放攻击,即第三方抓到数据包后,反复重复发送。我们因为token已经有了时效性,所以大部分接口没必要再增加时间戳参与签名,只有在用机器码申请新token的时候需要加时间戳,来保证不被第三方盗用申请token。
  • 怎么阻止同一账户多设备登录。每次登录后,添加新设备记录并将其他设备的记录更新为停用,之前登录的设备码和密钥失效。

上面提到的第三个重点我们在前面其实已经穿插说过了,直接对接商户的API服务的加密策略和移动应用服务不一样,最大的不同其实也就是第三点。

当然上面假设的大多数情况永远都不会发生,很多数据即便能篡改也不会有实际收益。但我们假设对方完完全全了解我们安全策略的情况下(比如我们中的某开发人员离职了……),怎么杜绝问题出现,也值得讨论。