JSPatch 部署安全策略
使用 JSPatch 有兩個(gè)安全問(wèn)題:
- 傳輸安全:JS 腳本可以調(diào)用任意 OC 方法,權(quán)限非常大,若被中間人攻擊替換代碼,會(huì)造成較大的危害。
- 執(zhí)行安全:下發(fā)的 JS 腳本靈活度大,相當(dāng)于一次小型更新,若未進(jìn)行充分測(cè)試,可能會(huì)出現(xiàn) crash 等情況對(duì) APP 穩(wěn)定性造成影響。
接下來(lái)說(shuō)下這兩個(gè)問(wèn)題的解決方案。
傳輸安全
方案一:對(duì)稱加密
若要讓 JS 代碼傳輸過(guò)程中不輕易被中間人截獲替換,很容易想到的方式就是對(duì)代碼進(jìn)行加密,可以用 zip 的加密壓縮,也可以用 AES 等加密算法。這個(gè)方案的優(yōu)點(diǎn)是非常簡(jiǎn)單,缺點(diǎn)是安全性低,容易被破解。因?yàn)槊荑€是要保存在客戶端的,只要客戶端被人拿去反編譯,把密碼字段找出來(lái),就完成破解了。
對(duì)此也有一些改進(jìn)方案,例如:
1.可以把密碼保存到 keychain 上,但這種方式也是不可靠的,只要隨便找一臺(tái)機(jī)器越獄裝了這個(gè) APP,用 hook 的方式在 APP 上添加一些代碼,獲得 keychain 里的密鑰值,就可以用于其他所有機(jī)器的傳輸解密了。
2.給每個(gè)用戶下發(fā)不同的密鑰。但這樣就非常繁瑣,需要對(duì)下發(fā)密鑰的請(qǐng)求做好保護(hù),后臺(tái)需要每次都對(duì)腳本進(jìn)行不同密鑰的加密操作,復(fù)雜性高了。
綜上,對(duì)稱加密安全性低,若要稍微提高點(diǎn)安全性,就會(huì)提升程序復(fù)雜度。
方案二:HTTPS
第二個(gè)方案是直接使用 HTTPS 傳輸,優(yōu)點(diǎn)是安全性高,只要使用正確,證書(shū)在服務(wù)端未泄露,就不會(huì)被破解。缺點(diǎn)是部署麻煩,需要使用者服務(wù)器支持 HTTPS,門檻較高。另外客戶端需要做好 HTTPS 的證書(shū)驗(yàn)證(有些使用者可能會(huì)漏掉這個(gè)驗(yàn)證,導(dǎo)致安全性大降),具體的認(rèn)證方式可見(jiàn)網(wǎng)上一些文章,例如這篇。如果服務(wù)器本來(lái)就支持 HTTPS,使用這種方案也是一種不錯(cuò)的選擇。
方案三:RSA 校驗(yàn)
有沒(méi)有安全性高,部署簡(jiǎn)單,門檻低的方案?RSA 校驗(yàn)就是。
這種方式其實(shí)原理跟 HTTPS 是一樣的,同樣使用非對(duì)稱加密,只是簡(jiǎn)化了,把非對(duì)稱加密只用于校驗(yàn)文件,而不解決傳輸過(guò)程中數(shù)據(jù)內(nèi)容泄露的問(wèn)題,而我們的目的只是防止傳輸過(guò)程中數(shù)據(jù)被篡改,對(duì)于數(shù)據(jù)內(nèi)容泄露并不是太在意。整個(gè)校驗(yàn)過(guò)程如下:
- 服務(wù)端計(jì)算出腳本文件的 MD5 值,作為這個(gè)文件的數(shù)字簽名。
- 服務(wù)端通過(guò)私鑰加密第 1 步算出的 MD5 值,得到一個(gè)加密后的 MD5 值。
- 把腳本文件和加密后的 MD5 值一起下發(fā)給客戶端。
- 客戶端拿到加密后的 MD5 值,通過(guò)保存在客戶端的公鑰解密。
- 客戶端計(jì)算腳本文件的 MD5 值。
- 對(duì)比第 4/5 步的兩個(gè) MD5 值(分別是客戶端和服務(wù)端計(jì)算出來(lái)的 MD5 值),若相等則通過(guò)校驗(yàn)。
只要通過(guò)校驗(yàn),就能確保腳本在傳輸?shù)倪^(guò)程中沒(méi)有被篡改,因?yàn)榈谌饺粢鄹哪_本文件,必須計(jì)算出新的腳本文件 MD5 并用私鑰加密,客戶端公鑰才能解密出這個(gè) MD5 值,而在服務(wù)端未泄露的情況下第三方是拿不到私鑰的。
這種方案安全性跟 HTTPS 一致,但不像 HTTPS 一樣部署麻煩,一套代碼即可通用。對(duì)于它的缺點(diǎn):數(shù)據(jù)內(nèi)容泄露,其實(shí)在傳輸過(guò)程中不泄露,保存在本地同樣會(huì)泄露,若對(duì)此在意,可以對(duì)腳本文件再加一層簡(jiǎn)單的對(duì)稱加密。這個(gè)方案優(yōu)點(diǎn)多缺點(diǎn)少,推薦使用,目前 JSPatch 平臺(tái)就是使用這個(gè)方案。
最后有個(gè)小問(wèn)題,保存在客戶端的代碼也可能被人篡改,需不需要采取措施?這個(gè)要看各人需求了,因?yàn)檫@個(gè)安全問(wèn)題不大,能篡改本地文件,差不多已經(jīng)有手機(jī)所有權(quán)限了,這時(shí)也無(wú)所謂腳本會(huì)不會(huì)被篡改了。若有需要,可以加個(gè)簡(jiǎn)單的對(duì)稱加密。
執(zhí)行安全
對(duì)于中大型 APP,下發(fā) JS 腳本需要謹(jǐn)慎,有可能因?yàn)槭韬鱿掳l(fā)了有問(wèn)題的代碼,導(dǎo)致大量 APP crash,或一些其他異常情況,需要有一些機(jī)制避免這種情況。若要做得完整,可以分為:事發(fā)前(灰度),事發(fā)中(監(jiān)控),事發(fā)后(回退)。
灰度
首先需要在事發(fā)前把出現(xiàn)問(wèn)題的影響面降到最低,對(duì)于中大型 APP,不能一次把腳本下發(fā)給所有用戶,需要有灰度機(jī)制,也就是一開(kāi)始只下發(fā)給其中一部分用戶,看看會(huì)不會(huì)出現(xiàn)異常情況,再逐步覆蓋到所有用戶。有條件的話灰度的用戶最好按機(jī)型/系統(tǒng)/地域等屬性隨機(jī)分配,盡量讓最少的人覆蓋到大部分情況。
監(jiān)控
接著是事發(fā)了我們需要知道腳本有問(wèn)題,需要對(duì) APP 有一些監(jiān)控機(jī)制,像 crash 監(jiān)控,這個(gè)一般所有 APP 都有接入,再按需求自行加入其他監(jiān)控指標(biāo)。
回退
最后是事發(fā)后回退代碼。一般為了避免不可預(yù)料的情況出現(xiàn),JSPatch 腳本建議在啟動(dòng)時(shí)執(zhí)行,APP 運(yùn)行過(guò)程中不去除,所以這個(gè)回退建議的實(shí)現(xiàn)方式是后臺(tái)下發(fā)命令,讓 APP 在下次啟動(dòng)時(shí)不執(zhí)行 JSPatch 腳本即可。
但這里能回退的前提是 APP 可以接收到后臺(tái)下發(fā)的回退命令,若因?yàn)橄掳l(fā)的腳本導(dǎo)致 APP 啟動(dòng)即時(shí) crash,這個(gè)回退命令也會(huì)接收不到。所以建議再加一層防啟動(dòng) crash 的機(jī)制,APP 在連續(xù)啟動(dòng)即 crash 后,下次啟動(dòng)不再執(zhí)行腳本文件。
灰度和監(jiān)控中小型 APP 可以考慮不用,回退機(jī)制是每個(gè)使用 JSPatch 都建議加上的。目前 JSPatch 平臺(tái)實(shí)現(xiàn)了上述回退方案。