Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> App安全(一) Android防止升級過程被劫持和換包

App安全(一) Android防止升級過程被劫持和換包

編輯:關於Android編程

前言

APP 安全一直是開發者頭痛的事情,越來越多的安全漏洞,使得開發者

越來越重視app安全,目前app安全主要有由以下幾部分

APP組件安全

Android 包括四大組件:Activitie、Service、Content Provider、Broadband Receiver,
它們每一個都可以通過外面隱式的Intent方式打開,
android組件對外開放 就會被其他程序劫持,因此必須在manifest裡面聲明
exported為false,禁止其他程序訪問改組件,
對於要和外部交互的組件,應當添加一下訪問權限的控制, 在有權限後外部程序才可以開啟,
還需要要對傳遞的數據進行安全的校驗。不符合規則的一律不處理。

Webview 安全漏洞

Android API 4.4以前,谷歌的webview存在安全漏洞,網站可以通過js注入就可以隨便拿到客戶端的重要信息,
甚至輕而易舉的調用本地代碼進行流氓行為,谷歌後來發現有此漏洞後
,在API 4.4以後增加了防御措施,如果用js調用本地代碼,開發者必須在代碼申明JavascriptInterface,
列如在4.0之前我們要使得webView加載js只需如下代碼:

mWebView.addJavascriptInterface(new JsToJava(), “myjsfunction”);

4.4之後使用需要在調用Java方法加入@JavascriptInterface注解,
如果代碼無此申明,那麼也就無法使得js生效,也就是說這樣就可以避免惡意網頁利用js對客戶端的進行竊取和攻擊。

APP反編譯

app被反編後,源碼暴露,不僅對數據造成隱私,而且對一些接口造成攻擊的潛在風險。
我們必須對apk進行源碼混淆,也可以進行apk加固

APP二次打包

即反編譯後重新加入惡意的代碼邏輯,或置入新病毒重新生成一個新APK文件。
二次的目的一般都是是盈利廣告和病毒結合,對正版apk進行解包,插入惡意病毒後重新打包並發布,
因此偽裝性很強。截住app重打包就一定程度上防止了病毒的傳播。因此app加固是防止二次打包的重要措施。

APP進程劫持

一般我們稱為進程注入,也就動態注入技術,hook技術目前主流的進程注入方式,通過對linux進行so注入,達到掛鉤遠程函數實現監控遠程進程的目的。

APP DNS劫持

DNS劫持俗稱抓包。通過對url的二次劫持,修改參數和返回值,進行對app的web數據偽裝,實現注入廣告和假數據,甚至導流用戶的作用,嚴重的可以
通過對登錄APi的劫持可以獲取用戶密碼,也可以對app升級做劫持,下載病毒apk等目的,解決方法一般用https進行傳輸數據。

APP APi接口驗簽

一般服務端的接口也會被攻擊,雖然是服務端安全問題,但還是屬於App系統維護體系,如果app後端掛了,app也不叫app了,一般會被
惡意程序頻繁請求,導致訂單等重復注入,嚴重的甚至拖垮app.

今天先看下APP升級過程被劫持的問題

我們做app版本升級時一般流程是采用請求升級接口,如果有升級,服務端返回下一個下載地址,下載好Apk後,再點擊安裝。
其實這個過程中有三個地方會被劫持。 請求升級時,下載文件時,安裝時。

升級APi

升級Api建議用https,防止被惡意程序劫持,結果是惡意返回下載地址,這樣就把偽裝apk下載到本地,結果你應該懂的

下載API:

那如果升級api你做了加固,下載api沒做加過,還是徒勞,惡意程序也可以給你返回惡意文件或者apk,直到被你錯誤的安裝在
手機上。

安裝過程;

假設你的以上兩個過程都做了加固,但是在安裝apk的時候,本地文件path被錯誤修改了,仍然可以安裝錯誤的apk,這不僅
會對用戶體驗產生不利,甚至會威脅手機安全。

解決方案:

升級api加入https 這個肯定不用再過多介紹,看了我介紹的retrofit 的https就明白了

下載Api也需加入https,也不用介紹,這裡著重強調的是你的服務端需要對文件進行文件Hash值校驗,防止文件被篡改,
通過對文件hash值,還要對服務端返回的key的進行校驗驗簽,防止不是自己服務器返回錯誤的文件

安裝過程也必須對Apk文件進行包名和簽名驗證,防止Apk被惡意植入木馬,或替換。

假設我的升級bean為;

public class UpgradeModel {

 private int code;
 private String msg;

 private DataBean data;

 public int getCode() {
    return code;
 }

 public void setCode(int code) {
    this.code = code;
 }

public String getMsg() {
    return msg;
}

public void setMsg(String msg) {
    this.msg = msg;
}

public DataBean getData() {
    return data;
}

public void setData(DataBean data) {
    this.data = data;
}

public static class DataBean {
    private String description;
    private String downUrl;
    private String version;
    private String hashcod;
    private String key;
    private String isForce;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getDownUrl() {
        return downUrl;
    }

    public void setDownUrl(String downUrl) {
        this.downUrl = downUrl;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getHashcod() {
        return hashcod;
    }

    public void setHashcod(String hashcod) {
        this.hashcod = hashcod;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getIsForce() {
        return isForce;
    }

    public void setIsForce(String isForce) {
        this.isForce = isForce;
    }
}

}

通過一次請求到服務端數據後,如果有版本更新 我們應該先驗證
Url和key是不是我們和服務端協商好的Url和key

 UpgradeModel  aResult = xxxx//解析服務器返回的後數據

 if (aResult != null && aResult.getData() != null ) {

        String url = aResult.getData().getDownUrl();

        if (url == null || !TextUtils.equals(url, "這裡是你知道的下載地址: 也可以只驗證hostUrl")) {

          // 如果符合,就不去下載

        }

接著我們驗證下載url是你自己app的服務器地址,然後再請求下載Api時用DownLoadModel接受請求頭 ,下載好apk到本地後,繼續判斷文件的hash和升級api返回的hashcode,加之key是否是和下載服務器返回的key,如果不一致,就不安裝

      File file = DownUtils.getFile(url);
            // 監測是否要重新下載
     if (file.exists() &&   TextUtils.equals(aResult.getData().getHashCode(), EncryptUtils.Md5File(file))) {
      && TextUtils.equals(aResult.getData().getKey(), DownLoadModel.getData()..getKey())

      // 如果符合,就去安裝 不符合重新下載 刪除惡意文件

   }

等我們驗證下載文件的地址是我們服務器提供的,驗證沒問題就只剩安裝了。接著還要對apk文件進行包名和簽名校驗,

/** installApK
 * @param context
 * @param path
 * @param name
 */
public static void installApK(Context context, final String path, final String name ) {

    if (!SafetyUtils.checkFile(path + name, context)) {
        return;
    }

    if (!SafetyUtils.checkPagakgeName(context, path + name)) {
        Toast.makeText(context, "升級包被惡意軟件篡改 請重新升級下載安裝", Toast.LENGTH_SHORT ).show();
        DLUtils.deleteFile(path + name);
        ((Activity)context).finish();
        return;
    }

    switch (SafetyUtils.checkPagakgeSign(context, path + name)) {

        case SafetyUtils.SUCCESS:
            DLUtils.openFile(path + name, context);
            break;

        case SafetyUtils.SIGNATURES_INVALIDATE:

            Toast.makeText(context, "升級包安全校驗失敗 請重新升級", Toast.LENGTH_SHORT ).show();
            ((Activity)context).finish();

            break;

        case SafetyUtils.VERIFY_SIGNATURES_FAIL:

            Toast.makeText(context, "升級包為盜版應用 請重新升級", Toast.LENGTH_SHORT ).show();
            ((Activity)context).finish();
            break;

        default:
            break;
    }

}

SafetyUtils安全類如下:

/**
* 安全校驗
* Created by LIUYONGKUI on 2016-04-21.
*/
public class SafetyUtils {

    /** install sucess */
    protected static final int SUCCESS = 0;
    /** SIGNATURES_INVALIDATE */
    protected static final int SIGNATURES_INVALIDATE = 3;
    /** SIGNATURES_NOT_SAME */
    protected static final int VERIFY_SIGNATURES_FAIL = 4;
    /** is needcheck */
    private static final boolean NEED_VERIFY_CERT = true;

/**
 * checkPagakgeSigns.
 */
public static int checkPagakgeSign(Context context, String srcPluginFile) {

    PackageInfo PackageInfo = context.getPackageManager().getPackageArchiveInfo(srcPluginFile, 0);
    //Signature[] pluginSignatures = PackageInfo.signatures;
    Signature[] pluginSignatures = PackageVerifyer.collectCertificates(srcPluginFile, false);
    boolean isDebugable = (0 != (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));
    if (pluginSignatures == null) {
        PaLog.e("簽名驗證失敗", srcPluginFile);
        new File(srcPluginFile).delete();
        return SIGNATURES_INVALIDATE;
    } else if (NEED_VERIFY_CERT && !isDebugable) {
        //可選步驟,驗證APK證書是否和現在程序證書相同。
        Signature[] mainSignatures = null;
        try {
            PackageInfo pkgInfo = context.getPackageManager().getPackageInfo(
                    context.getPackageName(), PackageManager.GET_SIGNATURES);
            mainSignatures = pkgInfo.signatures;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        if (!PackageVerifyer.isSignaturesSame(mainSignatures, pluginSignatures)) {
            PaLog.e("升級包證書和舊版本證書不一致", srcPluginFile);
            new File(srcPluginFile).delete();
            return VERIFY_SIGNATURES_FAIL;
        }
    }
    return SUCCESS;
}

/**
 * checkPagakgeName
 * @param context
 * @param srcNewFile
 * @return
 */
public static boolean checkPagakgeName (Context context, String srcNewFile) {
    PackageInfo packageInfo = context.getPackageManager().getPackageArchiveInfo(srcNewFile, PackageManager.GET_ACTIVITIES);

    if (packageInfo != null) {

       return TextUtils.equals(context.getPackageName(), packageInfo.packageName);
    }

    return false;
}

/**
 * checkFile
 *
 * @param aPath
 *            文件路徑
 * @param context
 *            context
 */
public static boolean checkFile(String aPath, Context context) {
    File aFile = new File(aPath);
    if (aFile == null || !aFile.exists()) {
        Toast.makeText(context, "安裝包已被惡意軟件刪除", Toast.LENGTH_SHORT).show();
        return false;
    }
    if  (context == null)  {
         Toast.makeText(context, "安裝包異常", Toast.LENGTH_SHORT).show();
        return false;
     }

     return true;
 }
}

後續

這樣我們的對升級流程的安全以及做到很安全細微了,很難被惡意程序輕易劫持,其他js注入,hook注入下期接講解。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved