App安全性保障方案

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的身份驗證方法一般是這樣的:

  1. 客戶端使用用戶名跟密碼請求登錄
  2. 服務端收到請求贤壁,去驗證用戶名與密碼
  3. 驗證成功后寸潦,服務端會簽發(fā)一個 Token瑟慈,再把這個 Token 發(fā)送給客戶端
  4. 客戶端收到 Token 以后可以把它存儲起來,比如放在 Cookie 里或者 Local Storage 里
  5. 客戶端每次向服務端請求資源的時候需要帶著服務端簽發(fā)的 Token
  6. 服務端收到請求胀蛮,然后去驗證客戶端請求里面帶著的 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)

  1. 客戶端和服務端均保存有相同的key
  2. 客戶端發(fā)起請求前,參數(shù)+時間戳+key使用簽名算法生成簽名
  3. 客戶端發(fā)起請求,攜帶 參數(shù)+時間戳+signStr
  4. 服務端驗證時間戳是否在有效期內(nèi)(如2分鐘)
  5. 根據(jù)同樣的簽名算法計算簽名并驗證是否一致

簽名驗證解決的問題:

  • 避免請求數(shù)據(jù)被篡改;如果第三方修改其中任意參數(shù),則會導致簽名不同,后臺驗證后則返回調(diào)用失敗
  • 防止重放攻擊,避免數(shù)據(jù)庫被爬取;因為有時間戳驗證,同一個簽名在短時間內(nèi)就會無效,服務器直接攔截掉了過期的請求

注意點:

  • key的安全性很重要,建議采用服務端動態(tài)下發(fā)的方式(下發(fā)如何確保安全,也需要更多考慮),且事先約定好key的更新機制(用于key泄露時,服務端重新下發(fā))
  • 客戶端時間戳的準確性如何確保?建議每次客戶端打開時計算與服務端的差值,用于確保時間戳的一致性

對返回結(jié)果加密傳輸

因為我們的請求會被抓包工具輕易的抓取,所以請求返回的數(shù)據(jù)需要加密處理

  1. 客戶端正常發(fā)起請求
  2. 服務端進行業(yè)務處理,對response加密
  3. 客戶端對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ù)的重要性來決定是否需要加密存儲

  1. 文件盡量存放在Internal Storage而不是External Storage,該目錄下其他用戶無法查看(root可查看)
  2. 數(shù)據(jù)庫采用加密方案,例如Reaml數(shù)據(jù)可以便捷的加解密,基于Sqlite的數(shù)據(jù)庫也有很多開源加密方案,可自行選擇
  3. 采用通用加密方案,對sp漆诽、file、數(shù)據(jù)庫均支持加解密,例如FaceBook的開源項目concel
  4. 秘鑰盡量不要在本地保存(更不要直接寫在代碼中),如果一定要保存,需要多層加密,拆分保存

才疏學淺,還請大家及時指出博客中的問題,不慎感激

關于作者

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锣枝,一起剝皮案震驚了整個濱河市厢拭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌撇叁,老刑警劉巖供鸠,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異陨闹,居然都是意外死亡楞捂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門趋厉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寨闹,“玉大人,你說我怎么就攤上這事君账》北ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長椭蹄。 經(jīng)常有香客問我闻牡,道長,這世上最難降的妖魔是什么绳矩? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任罩润,我火速辦了婚禮,結(jié)果婚禮上翼馆,老公的妹妹穿的比我還像新娘割以。我一直安慰自己,他們只是感情好应媚,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布拳球。 她就那樣靜靜地躺著,像睡著了一般珍特。 火紅的嫁衣襯著肌膚如雪祝峻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天扎筒,我揣著相機與錄音莱找,去河邊找鬼。 笑死嗜桌,一個胖子當著我的面吹牛奥溺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播骨宠,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼浮定,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了层亿?” 一聲冷哼從身側(cè)響起桦卒,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匿又,沒想到半個月后方灾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡碌更,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年裕偿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痛单。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘿棘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出旭绒,到底是詐尸還是另有隱情鸟妙,我是刑警寧澤焦人,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站圆仔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蔫劣。R本人自食惡果不足惜坪郭,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脉幢。 院中可真熱鬧歪沃,春花似錦、人聲如沸嫌松。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萎羔。三九已至液走,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贾陷,已是汗流浹背缘眶。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留髓废,地道東北人巷懈。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像慌洪,于是被迫代替她去往敵國和親顶燕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348