JSPatch 部署安全策略
2015-8-31
使用 JSPatch 有两个安全问题:
- 传输安全:JS 脚本可以调用任意 OC 方法,权限非常大,若被中间人攻击替换代码,会造成较大的危害。
- 执行安全:下发的 JS 脚本灵活度大,相当于一次小型更新,若未进行充分测试,可能会出现 crash 等情况对 APP 稳定性造成影响。
接下来说下这两个问题的解决方案。
传输安全
方案一:对称加密
若要让 JS 代码传输过程中不轻易被中间人截获替换,很容易想到的方式就是对代码进行加密,可以用 zip 的加密压缩,也可以用 AES 等加密算法。这个方案的优点是非常简单,缺点是安全性低,容易被破解。因为密钥是要保存在客户端的,只要客户端被人拿去反编译,把密码字段找出来,就完成破解了。
对此也有一些改进方案,例如:
1.可以把密码保存到 keychain 上,但这种方式也是不可靠的,只要随便找一台机器越狱装了这个 APP,用 hook 的方式在 APP 上添加一些代码,获得 keychain 里的密钥值,就可以用于其他所有机器的传输解密了。
2.给每个用户下发不同的密钥。但这样就非常繁琐,需要对下发密钥的请求做好保护,后台需要每次都对脚本进行不同密钥的加密操作,复杂性高了。
综上,对称加密安全性低,若要稍微提高点安全性,就会提升程序复杂度。
方案二:HTTPS
第二个方案是直接使用 HTTPS 传输,优点是安全性高,只要使用正确,证书在服务端未泄露,就不会被破解。缺点是部署麻烦,需要使用者服务器支持 HTTPS,门槛较高。另外客户端需要做好 HTTPS 的证书验证(有些使用者可能会漏掉这个验证,导致安全性大降),具体的认证方式可见网上一些文章,例如这篇。如果服务器本来就支持 HTTPS,使用这种方案也是一种不错的选择。
方案三:RSA 校验
有没有安全性高,部署简单,门槛低的方案?RSA 校验就是。
这种方式属于数字签名,用了跟 HTTPS 一样的非对称加密,只是简化了,把非对称加密只用于校验文件,而不解决传输过程中数据内容泄露的问题,而我们的目的只是防止传输过程中数据被篡改,对于数据内容泄露并不是太在意。整个校验过程如下:
- 服务端计算出脚本文件的 MD5 值,作为这个文件的数字签名。
- 服务端通过私钥加密第 1 步算出的 MD5 值,得到一个加密后的 MD5 值。
- 把脚本文件和加密后的 MD5 值一起下发给客户端。
- 客户端拿到加密后的 MD5 值,通过保存在客户端的公钥解密。
- 客户端计算脚本文件的 MD5 值。
- 对比第 4/5 步的两个 MD5 值(分别是客户端和服务端计算出来的 MD5 值),若相等则通过校验。
只要通过校验,就能确保脚本在传输的过程中没有被篡改,因为第三方若要篡改脚本文件,必须计算出新的脚本文件 MD5 并用私钥加密,客户端公钥才能解密出这个 MD5 值,而在服务端未泄露的情况下第三方是拿不到私钥的。
这种方案安全性跟 HTTPS 一致,但不像 HTTPS 一样部署麻烦,一套代码即可通用。对于它的缺点:数据内容泄露,其实在传输过程中不泄露,保存在本地同样会泄露,若对此在意,可以对脚本文件再加一层简单的对称加密。这个方案优点多缺点少,推荐使用,目前 JSPatch 平台就是使用这个方案。
最后有个小问题,保存在客户端的代码也可能被人篡改,需不需要采取措施?这个要看各人需求了,因为这个安全问题不大,能篡改本地文件,差不多已经有手机所有权限了,这时也无所谓脚本会不会被篡改了。若有需要,可以加个简单的对称加密,或者按上述流程每次都验证一遍MD5值。
执行安全
对于中大型 APP,下发 JS 脚本需要谨慎,有可能因为疏忽下发了有问题的代码,导致大量 APP crash,或一些其他异常情况,需要有一些机制避免这种情况。若要做得完整,可以分为:事发前(灰度),事发中(监控),事发后(回退)。
灰度
首先需要在事发前把出现问题的影响面降到最低,对于中大型 APP,不能一次把脚本下发给所有用户,需要有灰度机制,也就是一开始只下发给其中一部分用户,看看会不会出现异常情况,再逐步覆盖到所有用户。有条件的话灰度的用户最好按机型/系统/地域等属性随机分配,尽量让最少的人覆盖到大部分情况。
监控
接着是事发了我们需要知道脚本有问题,需要对 APP 有一些监控机制,像 crash 监控,这个一般所有 APP 都有接入,再按需求自行加入其他监控指标。
回退
最后是事发后回退代码。一般为了避免不可预料的情况出现,JSPatch 脚本建议在启动时执行,APP 运行过程中不去除,所以这个回退建议的实现方式是后台下发命令,让 APP 在下次启动时不执行 JSPatch 脚本即可。
但这里能回退的前提是 APP 可以接收到后台下发的回退命令,若因为下发的脚本导致 APP 启动即时 crash,这个回退命令也会接收不到。所以建议再加一层防启动 crash 的机制,APP 在连续启动即 crash 后,下次启动不再执行脚本文件。
灰度和监控中小型 APP 可以考虑不用,回退机制是每个使用 JSPatch 都建议加上的。目前 JSPatch 平台实现了上述回退方案。
本质上来说,方案三相当于对脚本进行签名,建议使用成熟的消息签名算法,而不是重新发明一种自制的算法。另外 MD5 算法很容易找到碰撞,应当在任何应用中禁止使用