談到 Android 安全性話題宫静,Android Developers 官方網(wǎng)站給出了許多很好的建議和講解,涵蓋了存儲數(shù)據(jù)、權(quán)限囊嘉、網(wǎng)絡(luò)温技、處理憑據(jù)、輸入驗證扭粱、處理用戶數(shù)據(jù)、加密等方方面面震檩,甚至對于動態(tài)加載代碼也提供了建議琢蛤,具體可以看看 training 的 security tips 章節(jié)。而今天抛虏,我想特別來講一講在 Android 密鑰保護和 C/S 網(wǎng)絡(luò)傳輸安全 這兩方面的具體安全措施博其。
密鑰的保護以及網(wǎng)絡(luò)傳輸安全 應(yīng)該是移動應(yīng)用安全最關(guān)鍵的內(nèi)容。
所謂的密鑰迂猴,簡單來說慕淡,可以認為是我們常用的 app key / secret / token 或數(shù)據(jù)加密的 key,這些 keys 就像我們寶庫的鑰匙沸毁,一旦泄露峰髓,就像大門被人撬開,什么安全都無從談起息尺。因此携兵,密鑰們的安全存儲、防竊取便顯得異常重要搂誉。我曾經(jīng)看過許多國內(nèi)外著名的應(yīng)用在使用自家 API 或 SaaS 服務(wù)的時候徐紧,在 Java 代碼或 SharePreferences 里明文記錄著 app key / secret / token,這樣的做法炭懊,就算使用了 proguard 對代碼進行混淆并级,也是非常容易被逆向獲得服務(wù)端接入密鑰,非常危險侮腹。
在這一方面嘲碧,Android 提供大量用來保護數(shù)據(jù)的加密算法,例如 Cipher 類中提供了 AES 和 RSA 算法凯旋,再例如安全隨機數(shù)生成器 SecureRandom 給 KeyGenerator 提供了更加可靠的初始化參數(shù)呀潭,避免離線攻擊等等。
而如果需要存儲密鑰以供重復(fù)使用至非,Android 提供了 KeyStore 等可以長期存儲和檢索加密密鑰的機制钠署,Android KeyStore 系統(tǒng)特別適合于存儲加密密鑰』耐郑”AndroidKeyStore” 是 KeyStore 的一個子集谐鼎,存進 AndroidKeyStore 的 key 將受到簽名保護,并且這些 key 是存在系統(tǒng)里的趣惠,而不是在 App 的 data 目錄下狸棍,依托于硬件的 KeyChain 存儲身害,可以做到 private key 一旦存入就無法取出,總之草戈,每個 App 自己創(chuàng)建的 key塌鸯,別的應(yīng)用是訪問不到的。
很多時候唐片,我們會需要將用戶的賬號密碼或 token 存儲下來丙猬,以做到下次打開免登的目的。
KeyStore 提供了兩個能力:
生成隨機加密密鑰
安全存儲和讀取數(shù)據(jù)
有了這兩個能力费韭,我們的密鑰保護就變得很容易了茧球,你只需要:
在應(yīng)用安裝后第一次運行時,生成一個隨機密鑰星持,并存入 KeyStore
當你想存儲一個數(shù)據(jù)抢埋,便從 KeyStore 中取出之前生成的隨機密鑰,對你的數(shù)據(jù)進行加密督暂,加密完成后揪垄,已完成加密的數(shù)據(jù)可以隨意存儲在任意地方,比如 SharePreferences损痰,此時即使它被他人讀取到福侈,也無法解密出你的原數(shù)據(jù),因為他人取不到你的密鑰
當你需要拿到你的原數(shù)據(jù)卢未,只需要從 SharePreferences 中讀取你加密后的數(shù)據(jù)肪凛,并從 KeyStore 取出加密密鑰,使用加密密鑰對 “加密后的數(shù)據(jù)” 進行解密即可
其中加密算法可以使用 Cipher AES 來保證安全性辽社,不要使用自己創(chuàng)造的加密算法伟墙。
這就是使用 KeyStore 的一整套流程,另外 KeyStore 還可以用來做數(shù)據(jù)簽名和簽名驗證滴铅,就像一個黑匣子一樣戳葵,具體可以自行搜索了解。
KeyStore 適用于生成和存儲密鑰汉匙,這些密鑰可以用來加密運行時獲取到的數(shù)據(jù)拱烁,比如運行時,用戶輸入的密碼噩翠,或者服務(wù)端傳下來的 token戏自。但對于需要預(yù)設(shè)在 App 內(nèi)的 API key / secret,因為 KeyStore 是運行時隨機生成加密密鑰伤锚,所以我們無法預(yù)估API key / secret 會被加密成什么樣擅笔,自然也就無法預(yù)先把加密后的 API key / secret 預(yù)埋在 App 內(nèi),因此對于這類需要預(yù)設(shè)的固定密鑰,我將介紹另外一種十分安全猛们、難破解的保護方式念脯。
首先我們需要思考,這個 key 應(yīng)該放到哪里才能夠最大限度提升其被逆向獲取的難度弯淘,放 Java 代碼里绿店?根本不安全。放文件或圖片像素里庐橙?頂多續(xù) 1 小時惯吕。放 so 庫里?可以怕午,so 庫能夠很大程度提升逆向破解難度,但如果別人把 so 庫文件拿出來淹魄,再直接調(diào)用這些 native 接口郁惜,便也可以獲取到你的 key,怎么辦甲锡?
我的做法是在 so 庫的 C 代碼里 JNI_OnLoad() 方法對 APK 簽名進行驗證兆蕉,如果簽名不對,直接 crash缤沦,這樣移植出去便和磚頭沒什么兩樣虎韵。而你的應(yīng)用又不得不依賴這個 so 庫進行獲取 API key / secret,因此它又不能直接剝離缸废,這就保證了不能沒有它包蓝,又不能移植它,換句話說就是:如果別人反編譯了你的代碼企量,發(fā)現(xiàn)你使用 so 進行簽名驗證测萎,便直接把這個 so 文件摘掉,這樣做的結(jié)果是届巩,App 獲取不到你存在 so 中的 secret 了硅瞧,便無法正常工作了;而如果別人對你的應(yīng)用進行修改和重新簽名恕汇,或移植你的 so 庫來讀取內(nèi)部的 secret腕唧,則會因為簽名驗證不通過直接自爆。
以上便是對于保護 key / secret 的一些有效舉措瘾英,再總結(jié)下就是枣接,使用 so 庫存儲預(yù)設(shè) key / secret,使用 Android KeyStore 存儲運行時動態(tài)獲取到的私密內(nèi)容方咆。