App安全性保障方案
之前晉級考核的時候認識到自己在app安全領域存在薄弱環(huán)節(jié),所以這段時間研究了Android應用的安全防護,結(jié)合公司的項目特點做出記錄和總結(jié);主要包擴兩個方面:http接口安全和App防逆向破解
一 Http接口安全
Http接口安全是為了保證客戶端與服務端進行數(shù)據(jù)交互時的數(shù)據(jù)安全,防止數(shù)據(jù)庫被爬取及數(shù)據(jù)被篡改
動態(tài)token
token是一個與用戶身份關聯(lián)的字符串,作為客戶端與服務端交互時的令牌,可用于唯一確定客戶端用戶身份.
基于token的身份驗證方法一般是這樣的:
- 客戶端使用用戶名跟密碼請求登錄
- 服務端收到請求贤壁,去驗證用戶名與密碼
- 驗證成功后寸潦,服務端會簽發(fā)一個 Token瑟慈,再把這個 Token 發(fā)送給客戶端
- 客戶端收到 Token 以后可以把它存儲起來,比如放在 Cookie 里或者 Local Storage 里
- 客戶端每次向服務端請求資源的時候需要帶著服務端簽發(fā)的 Token
- 服務端收到請求胀蛮,然后去驗證客戶端請求里面帶著的 Token,如果驗證成功糯钙,就向客戶端返回請求的數(shù)據(jù)
token一般有個有效期,當token失效時,客戶端會使用獲取token的接口來更新token,這樣能夠保證客戶端的token有效且是動態(tài)的,第三方截取到舊的token也無法發(fā)起有效請求,并且刷新token這個過程對于用戶是無感知的.
簽名驗證
如果不對請求做簽名驗證,則可以簡單通過抓包工具拿到請求參數(shù),篡改后提交且大規(guī)模調(diào)用,導致我們的核心業(yè)務數(shù)據(jù)被篡改且系統(tǒng)資源被大量消耗.簽名驗證也是第三方開放平臺確保接口安全的標準做法:
-
微信支付
微信支付開放平臺簽名算法 -
支付寶支付
支付寶支付簽名算法 -
春雨醫(yī)生開放平臺
春雨開放平臺簽名
簽名算法:signStr=signature(params&timetamp&SIGN_KEY)
- 客戶端和服務端均保存有相同的key
- 客戶端發(fā)起請求前,參數(shù)+時間戳+key使用簽名算法生成簽名
- 客戶端發(fā)起請求,攜帶 參數(shù)+時間戳+signStr
- 服務端驗證時間戳是否在有效期內(nèi)(如2分鐘)
- 根據(jù)同樣的簽名算法計算簽名并驗證是否一致
簽名驗證解決的問題:
- 避免請求數(shù)據(jù)被篡改;如果第三方修改其中任意參數(shù),則會導致簽名不同,后臺驗證后則返回調(diào)用失敗
- 防止重放攻擊,避免數(shù)據(jù)庫被爬取;因為有時間戳驗證,同一個簽名在短時間內(nèi)就會無效,服務器直接攔截掉了過期的請求
注意點:
- key的安全性很重要,建議采用服務端動態(tài)下發(fā)的方式(下發(fā)如何確保安全,也需要更多考慮),且事先約定好key的更新機制(用于key泄露時,服務端重新下發(fā))
- 客戶端時間戳的準確性如何確保?建議每次客戶端打開時計算與服務端的差值,用于確保時間戳的一致性
對返回結(jié)果加密傳輸
因為我們的請求會被抓包工具輕易的抓取,所以請求返回的數(shù)據(jù)需要加密處理
- 客戶端正常發(fā)起請求
- 服務端進行業(yè)務處理,對response加密
- 客戶端對response解密
解決的問題:
- 對Response加密之后,即使第三方抓取到了我們的數(shù)據(jù),也無法解密,保證了我們的數(shù)據(jù)安全
注意:
- 這里需要使用可逆算法,因為需要加解密,如AES粪狼、RSA等;
- 同樣我們的秘鑰也需要確保安全,且事先約定好更新機制,用于應對秘鑰泄露;
- 可以對整個response加密,也可以只對真正的業(yè)務數(shù)據(jù)部分加密,可以跟業(yè)務特點自行選擇;
- 因為存在一些接口是不能加密的(如初始化接口等),可以增加一個是否加密字段,便于客戶端兼容所有接口
在Android下,可以這樣簡單實現(xiàn)
數(shù)據(jù)結(jié)構(gòu)
public class CodeEntity {
public int code;
public String msg;
//業(yè)務數(shù)據(jù)
public Object data;
//當前是否需要解密,默認不需要
public boolean encrypted = false;
}
CommonGsonResponseBodyConverter對返回結(jié)果解密
public class CommonGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
...
@Override
public T convert(ResponseBody value) throws IOException {
String response = value.string();
...
/**
* AES解密
* 如果該接口有加密,則先解密
*/
if (entity.encrypted) {
response = AES.getInstance().decode("key",response);
}
...
}
}
AES加解密:
public class AES implements IDecode, IEncode {
private static final String CBC_PKCS5_PADDING = "AES/ECB/PKCS5Padding";//AES是加密方式 CBC是工作模式 PKCS5Padding是填充模式
private static final String AES = "AES";//AES 加密
private static final Charset CHARSET = Charset.forName("UTF-8");
private volatile static AES sAES = null;
private AES() {
}
public static AES getInstance() {
if (sAES == null)
synchronized (AES.class) {
if (sAES == null)
sAES = new AES();
}
return sAES;
}
@Override
public String encode(String key, String plaintext) {
try {
byte[] raw = key.getBytes(CHARSET);
SecretKeySpec spec = new SecretKeySpec(raw, AES);
Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
cipher.init(Cipher.DECRYPT_MODE, spec);
//解密
byte[] input = cipher.doFinal(plaintext.getBytes(CHARSET));
//Base64轉(zhuǎn)碼
return Base64.encodeToString(input, Base64.DEFAULT);
} catch (Exception e) {
Logger.e("加密出錯,直接返回明文==>明文為:" + plaintext);
return plaintext;
}
}
@Override
public String decode(String key, String ciphetext) {
try {
byte[] raw = key.getBytes(CHARSET);
SecretKeySpec spec = new SecretKeySpec(raw, AES);
Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
cipher.init(Cipher.DECRYPT_MODE, spec);
//將密文先使用Base64解碼
byte[] decoder = Base64.decode(ciphetext.getBytes(CHARSET), Base64.DEFAULT);
//解密
byte[] bytes = cipher.doFinal(decoder);
return new String(bytes, CHARSET);
} catch (Exception e) {
Logger.e("解密出錯,認為無需解密==>密文為:" + ciphetext);
return ciphetext;
}
}
}
二 App防逆向破解
逆向工程一直都是App安全的潛在威脅,如果App沒有采取防逆向措施,則你的App能夠被輕易破解,包括源代碼,資源及本地數(shù)據(jù);App逆向工程和反逆向一直都是與時俱進的,這里總結(jié)App開發(fā)中常用的反逆向方案,往往都是多重方案結(jié)合起來使用,以確保App安全
代碼混淆
這是最基本的防護方法,也是使用最廣泛最成熟的,一般App都會使用;開啟混淆后,源代碼中的類名、方法名任岸、變量名會變成隨機字母,使代碼難以閱讀但卻不影響正常運行,這樣App被反編譯后代碼邏輯也不會暴露,另外混淆還有減少包體積的作用
資源混淆
代碼混淆只混淆了我們的代碼,apk中的資源在解壓之后一目了然,這樣導致布局再榄、樣式、圖片資源等完全處于暴露狀態(tài),第三方通過簡單的操作就可以拿到,所以資源文件也采取混淆操作.
推薦使用騰訊開源的AndResGuard項目進行資源壓縮,不僅可以最大程度保護我們的資源安全,且可以減小APK體積
加固加殼
我們在加固的過程中需要三個對象:
1享潜、需要加密的Apk(源Apk)
2困鸥、殼程序Apk(負責解密Apk工作)
3、加密工具(將源Apk進行加密和殼Dex合并成新的Dex)
主要步驟:
我們拿到需要加密的Apk和自己的殼程序Apk剑按,然后用加密算法對源Apk進行加密在將殼Apk進行合并得到新的Dex文件疾就,最后替換殼程序中的dex文件即可,得到新的Apk,那么這個新的Apk我們也叫作脫殼程序Apk.他已經(jīng)不是一個完整意義上的Apk程序了艺蝴,他的主要工作是:負責解密源Apk.然后加載Apk,讓其正常運行起來猬腰。
Apk加固之后是比較安全的(相對),現(xiàn)在市場是有很多加固平臺,可自行選擇使用
本地數(shù)據(jù)安全
App有一些數(shù)據(jù)是存在設備中的,包括緩存、配置猜敢、數(shù)據(jù)庫等,如果這些數(shù)據(jù)不加密,則會存在泄漏的風險,所以對于本地數(shù)據(jù)根據(jù)數(shù)據(jù)的重要性來決定是否需要加密存儲
- 文件盡量存放在Internal Storage而不是External Storage,該目錄下其他用戶無法查看(root可查看)
- 數(shù)據(jù)庫采用加密方案,例如Reaml數(shù)據(jù)可以便捷的加解密,基于Sqlite的數(shù)據(jù)庫也有很多開源加密方案,可自行選擇
- 采用通用加密方案,對sp漆诽、file、數(shù)據(jù)庫均支持加解密,例如FaceBook的開源項目concel
- 秘鑰盡量不要在本地保存(更不要直接寫在代碼中),如果一定要保存,需要多層加密,拆分保存
才疏學淺,還請大家及時指出博客中的問題,不慎感激