首先插播一條自己的廣告——有些朋友可能都知道了,我最近建立了一個知識星球,在這裡試了一週,發現私密圈子的效率果然比群要好很多,付費門檻過濾掉了大部分廣告和沒有意願學習分享的人,希望在這裡能聚集更多的熱愛學習熱愛分享的朋友,長按下面的二維碼來加入《程式設計師修仙指南》
App安全二三事
客戶端防作弊,是一個很重要,但又很難做好的事情,矛與盾永遠是道高一尺,魔高一丈。
為什麼要安全
現在幾乎所有App都是網路強相關的,客戶端展示的很多東西都是通過介面從伺服器上獲取的,當然,伺服器也會接收大量從客戶端上傳的資料,這兩端在進行雙向通訊的時候,就很容易被第三方截獲,導致資料被盜取、介面被盜刷。
App的移動安全主要包括下面幾種:
- 金鑰破解,導致本地加密資料被盜取
- 通訊金鑰破解,導致介面資料被盜取
- 偽造介面資料上報
- 介面簽名被破解,導致介面可以被重放攻擊
那麼歸結起來,實際上就是這樣幾種模式:
- 程式碼反編譯
- so破解
- 中間人攻擊
使用者要的安全
對於使用者來說,他所需要的安全,是自己的敏感資料不被洩漏,不被第三方所知曉,所以,客戶端資料的安全,一般會使用加密的方式來保證安全,但資料既然存在本地,那麼自然既需要加密,也需要解密(如果不需要解密,那麼也就沒有保留的必要了),所以,本地就一定會有加解密的金鑰,那麼為了保證這個金鑰的安全,原生程式碼又需要進行加密,這樣突然好像就進入了一個死迴圈,成了一個雞生蛋,蛋生雞的問題,這也是為什麼『本地沒有絕對的安全』這樣一說的原因。
本地加密
本地的加密,我們通常從混淆——proguard入手,這是最簡單的加密,成本最低,而且可以比較有效的扼殺一些在破解邊緣徘徊的初級破解者,讓他們能夠懸崖勒馬,浪子回頭,然而,對於真正想要破解的人來說,混淆只等於加大了一點閱讀難度而已,相信做開發的同學基本上也都反編譯過別人家的App,通過像jadx、apktool、dex2jar這樣的反編譯工具,可以非常方便的找到破解的蛛絲馬跡,特別像jadx這樣的反編譯神器,直接匯出gradle工程去AS裡面檢視程式碼,簡直不要太舒服。
再高階一點,我們通過Dexguard、各種第三方so加固服務、加殼服務等方式來進行保護,這些方式的確會極大的增加破解者的破解成本,到對於主流的加固技術,相應的破解技術也是非常成熟的,所以說,雖然技術很牛逼,但只要破解者知道了你加固的方式,就可以輕而易舉的找到破解的方法,也就是比proguard多了一次Google的過程。
說完了這些程式碼的安全,我們再來看看金鑰的安全問題,前面說了,金鑰一定會『藏』在本地。
最低階的,金鑰被直接放在Java程式碼中,這種基本上就是為了糊弄老闆的,稍微高階點的,也放在Java程式碼中,但是並不是直接讓你找到的,為了增加自己的一點信心,他會把金鑰拆成幾個部分,然後通過一定演算法計算合成完整的金鑰,自欺欺人罷了,再高階一點,會把金鑰和加解密放so中,再進一步,同樣將金鑰打散,通過一定演算法進行組裝,再高階一點,so再做下簽名校驗,加個花指令,甚至是一些人肉混淆(1、I、l),一步步的,過濾了一批批小白、初級、中級、高階破解者,然而,天下無利不往,如果你的App真的有這樣的價值,那也一定會吸引那些骨灰破解者,畢竟人怕出名豬怕壯。
當然Google也總是後知後覺,在各種廠商提供了TrustZone/TEE硬體加密方案後,Google也推出了Keystore,當然,最低要API26才能使用,所以在現在來說,幾乎不會有App能做到最低版本26,也就沒辦法藉助Keystore來進行安全儲存了。
介面簽名
介面上的安全,最基本的保證就是Https,同時對SSL協議的域名進行校驗(關鍵詞:X509TrustManager、hostnameVerifier),相信大部分的開發者都沒有對這兩個地方進行校驗,在此之上,請求的介面上,我們一般會帶上一個簽名,或者叫token,這個加密的金鑰串,就是我們身份的象徵,一般來說,這個簽名也就是通過前面我們千辛萬苦要藏好的本地金鑰來進行生成的,通常也就是那幾個引數,例如時間戳、UserID、IMEI、Mac地址等等進行拼裝,然後通過DES、3DES、AES、hmacSHA1等方式進行加密後,再經過Base64進行編碼生成的,這些加密過程就不贅述了,反正大家的都不一樣,根據關鍵詞大家去Google下就好了。
服務端要的安全
服務端需要的安全,主要是希望收到的請求,都真實的來自正常使用者的正常觸發。
但客戶端在由不受信第三方(比如使用者)控制的情況下,基本不存在能夠驗證請求是來自“自己的”客戶端的方法,只能通過以下兩種方式來增加破解者的破解成本。
- 本地祕鑰+演算法,用於生成介面簽名,難點在於如何保證本地祕鑰和演算法的安全性,也就是我們前面說的
- 動態祕鑰,將金鑰的生成放在服務端,難點在於如何保證通訊協議的安全性,同時也需要本地金鑰來保證請求動態金鑰的介面安全
動態祕鑰下發的方案,需要在保證通訊協議安全的情況下,才有實現價值,例如某活動頁面的刷榜,可以增加一個前置依賴介面用於動態返回祕鑰,客戶端使用該動態祕鑰來進行活動頁面的請求,祕鑰不存本地,每次請求都是新的祕鑰,設定網路請求框架的NO_PROXY模式,就是一個最簡單的方案。
考慮到伺服器裝置的安全性,目前主流的防作弊檢測都是在服務端進行,當然最主要的原因還是本地根本沒辦法保證絕對的安全。
識別使用者請求鏈路
根據必要的API呼叫流程和閉環,限制一組API呼叫中不同個體API相對於其它API的呼叫頻率(相對次數)限制。設定幾個隱祕的引數關聯邏輯,是跟業務邏輯環環相扣的,如果其他人想要自己拼裝引數,往往會打破這個隱祕約束。
但這個檢測通常需要耗費一定的系統資源,同時,當業務比較複雜的時候,如何保證請求檢測的實時性和高效性,就成了一個很難平衡的問題。
閘道器層攔截、人機識別
- 閘道器層攔截同IP的大量重複請求,設定同IP訪問的閾值。
- 大資料識別,對識別為惡意請求的進行封號處理
這是目前比較主流的做法。
TCP加密
目前大部分的App都是通過Http來進行資料互動,但基於TCP,我們可以實現自己的通訊協議,另外,利用TCP包的無序性來增加破解的難度,這樣,利用TCP心跳來維持一個安全的通訊通道,也是一個非常不錯的方案,不過操作難度比較大。
修改業務邏輯處理方式
在設計業務技術實現方案時,將業務判斷邏輯放在後端,客戶端只做指令上發,判斷是否生效,在服務端進行判斷。
後現代安全
量子加密、白盒加密、人工智慧分析,這些基本都是下一代的安全策略,就當前來說,還比較虛幻,不過只要技術一旦成熟,一定將是劃時代的里程碑。
另外,知識星球是可以通過分享來獲取獎勵的,在『程式設計師修仙智慧』中,點選左上角可以進行分享介面,選擇分享星球即可拿到自己的獎勵。