Android應用安全和合規方案

最近遇到合規 關於應用整改有點心得 再次記錄一下
安全 就是讓我們的應用更安全 讓用戶數據更安全 提高用戶體驗 等等好處

下面說一下我的安全方案 以及主要目標
第一 防止動態注入
第二 防止被調試
第三 防止界面劫持
第四 防止被破解 二次打包
這幾個最主要的目標 當然在大牛面前 這些東西 可能不夠看 後期會繼續努力完善

關於當前主流方案 大部分是使用第三方加固 防止這些問題 例如360 阿里 騰訊 都有這樣面的服務 有興趣的可以去看看
當然價格也很美麗 所以我選擇我的方案 慢慢成長學習

版本是Java層面的版本 native 和 c 這一方面會更好 動態加載 和底層控制相結合 安全係數更高 目前研究中後續更新…
這是我的幾個工具

/**

  • Time ${DATA}

  • Author KuKa

  • Description: 檢查簽名工具 防止簽名被篡改
    */
    public class AppSignCheck {

     private Context context;
     private String cer = null;
     private String realCer = null;
     private static final String TAG = "AppSignCheck";
    
     public AppSignCheck(Context context) {
         this.context = context;
         this.cer = getCertificateSHA1Fingerprint();
     }
    
     public AppSignCheck(Context context, String realCer) {
         this.context = context;
         this.realCer = realCer;
         this.cer = getCertificateSHA1Fingerprint();
     }
    
     public String getRealCer() {
         return realCer;
     }
    
     /**
      * 設置正確的簽名
      *
      * @param realCer
      */
     public void setRealCer(String realCer) {
         this.realCer = realCer;
     }
    
     /**
      * 獲取應用的簽名
      *
      * @return
      */
     public String getCertificateSHA1Fingerprint() {
         //獲取包管理器
         PackageManager pm = context.getPackageManager();
    
         //獲取當前要獲取 SHA1 值的包名,也可以用其他的包名,但需要注意,
         //在用其他包名的前提是,此方法傳遞的參數 Context 應該是對應包的上下文。
         String packageName = context.getPackageName();
    
         //返回包括在包中的簽名信息
         int flags = PackageManager.GET_SIGNATURES;
    
         PackageInfo packageInfo = null;
    
         try {
             //獲得包的所有內容信息類
             packageInfo = pm.getPackageInfo(packageName, flags);
         } catch (PackageManager.NameNotFoundException e) {
             e.printStackTrace();
         }
    
         //簽名信息
         Signature[] signatures = packageInfo.signatures;
         byte[] cert = signatures[0].toByteArray();
    
         //將簽名轉換爲字節數組流
         InputStream input = new ByteArrayInputStream(cert);
    
         //證書工廠類,這個類實現了出廠合格證算法的功能
         CertificateFactory cf = null;
    
         try {
             cf = CertificateFactory.getInstance("X509");
         } catch (Exception e) {
             e.printStackTrace();
         }
    
         //X509 證書,X.509 是一種非常通用的證書格式
         X509Certificate c = null;
    
         try {
             c = (X509Certificate) cf.generateCertificate(input);
         } catch (Exception e) {
             e.printStackTrace();
         }
    
         String hexString = null;
    
         try {
             //加密算法的類,這裏的參數可以使 MD4,MD5 等加密算法
             MessageDigest md = MessageDigest.getInstance("SHA1");
    
             //獲得公鑰
             byte[] publicKey = md.digest(c.getEncoded());
    
             //字節到十六進制的格式轉換
             hexString = byte2HexFormatted(publicKey);
    
         } catch (NoSuchAlgorithmException e1) {
             e1.printStackTrace();
         } catch (CertificateEncodingException e) {
             e.printStackTrace();
         }
         LogUtils.d("sha1=",hexString);
         return hexString;
     }
    
     //這裏是將獲取到得編碼進行16 進制轉換
     private String byte2HexFormatted(byte[] arr) {
    
         StringBuilder str = new StringBuilder(arr.length * 2);
    
         for (int i = 0; i <arr.length; i++) {
             String h = Integer.toHexString(arr[i]);
             int l =h.length();
             if (l == 1)
                 h = "0" + h;
             if (l > 2)
                 h = h.substring(l - 2, l);
             str.append(h.toUpperCase());
             if (i < (arr.length - 1))
                 str.append(':');
         }
         return str.toString();
     }
    
     /**
      * 檢測簽名是否正確
      * @return true 簽名正常 false 簽名不正常
      */
     public boolean check() {
    
         if (this.realCer != null) {
             cer = cer.trim();
             realCer = realCer.trim();
             if (this.cer.equals(this.realCer)) {
                 return true;
             }
         }
         return false;
     }
    

}

/**

  • Time ${DATA}

  • Author KuKa

  • Description: 運行時 監控工具
    */

    public class RunTimeUtil implements DialogUtils.OnceToastEventLisenter {
    public boolean appRunMonitor(Context context){
    checkRootAdivce(context);
    checkAppDebug(context);
    checkSign(context);
    if (checkRootAdivce(context) || checkSign(context)) {
    return true;
    }
    return false;
    }

     // 簽名檢查 發現被修改 自殺進程
     public boolean checkSign(Context context){
         // 防止簽名被修改
         AppSignCheck signCheck = new AppSignCheck(context, BaseConstant.sign);
         if (!signCheck.check()) {
             DialogUtils.onceToastEvent(context,"請前往官網下載正版app",this);
             return true;
         }
         return false;
     }
     // 檢查設備信息是否被root 若被root 自殺進程
     public boolean checkRootAdivce(Context context){
         // 防止簽名被修改
         if (CheckRootPathSU() || checkRootWhichSU()) {
             DialogUtils.onceToastEvent(context,"請前往官網下載正版app",this);
             return true;
         }
         return false;
     }
    
     // 檢查應用是否運行在debug 環境 反調試檢查
     public boolean checkAppDebug(Context context){
         if (Debug.isDebuggerConnected()) {
             System.exit(0);
             return true;
         }
         if (isUnderTraced()) {
             System.exit(0);
             return true;
         }
         if (checkDeviceDebuggable()) {
             System.exit(0);
             return true;
         }
         if (0!=(context.getApplicationInfo().flags&= ApplicationInfo.FLAG_DEBUGGABLE)) {
             System.exit(0);
             return true;
         }
         return false;
     }
    
    
     /**
      * @return
      */
     public boolean isUnderTraced() {
     String processStatusFilePath = String.format(Locale.US, "/proc/%d/status", android.os.Process.myPid());
     File procInfoFile = new File(processStatusFilePath);
         try {
             BufferedReader b = new BufferedReader(new FileReader(procInfoFile));
             String readLine;
             while ((readLine = b.readLine()) != null) {
                 if(readLine.contains("TracerPid")) {
                     String[] arrays = readLine.split(":");
                         if(arrays.length == 2) {
                             int tracerPid = Integer.parseInt(arrays[1].trim());
                             if(tracerPid != 0) {
                                 return true;
                             }
                         }
                 }
             }
             b.close();
         }  catch (Exception e) {
             e.printStackTrace();
         }
         return false;
    

    }

     public boolean checkDeviceDebuggable(){
         String buildTags = android.os.Build.TAGS;
         if (buildTags != null && buildTags.contains("test-keys")) {
             return true;
         }
         return false;
     }
    
    
     /**
      * 通過檢測指定目錄下是否存在su程序來檢測運行環境是否爲Root設備
      * @return 返回值 爲true時 禁止啓動
      */
     public boolean CheckRootPathSU() {
         File f = null;
         final String kSuSearchPaths[] = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/"};
         try {
             for (int i = 0; i < kSuSearchPaths.length; i++) {
                 f = new File(kSuSearchPaths[i] + "su");
                 if (f != null && f.exists()) {
                     return true;
                 }
             }
         } catch (Exception e) {
             e.printStackTrace();
         }
         return false;
     }
    
     /**
      * 通過which命令檢測系統PATH變量指定的路徑下是否存在su程序來檢測 運行環境是否爲Root設備
      *
      * @return
      */
     public boolean checkRootWhichSU() {
    
         String[] strCmd = new String[] {"/system/xbin/which","su"};
         ArrayList<String> execResult = executeCommand(strCmd);
         if (execResult != null){
             return true;
         }else{
             return false;
         }
     }
    
     public static ArrayList<String> executeCommand(String[] shellCmd){
         String line = null;
         ArrayList<String> fullResponse = new ArrayList<String>();
         Process localProcess = null;
         try {
             localProcess = Runtime.getRuntime().exec(shellCmd);
         } catch (Exception e) {
             return null;
         }
         BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));
         BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));
         try {
             while ((line = in.readLine()) != null) {
                 fullResponse.add(line);
             }
         } catch (Exception e) {
             e.printStackTrace();
         }
         return fullResponse;
     }
    
    
     @Override
     public void event(boolean b) {
         System.exit(0);
     }
    

    }

包含 設備換環境 簽名檢查 防調試檢查 防debug調試

在清單文件中 application 節點 要添加
android:allowBackup=“false”
android:debuggable=“false”
這兩個屬性 一個是防止數據備份 一個是防止debug調試

關於運行時檢查工具 我的使用方案是 應用啓動時 進行攔截檢查 在運行中 可以開啓一個守護線程 對應用進行覈查 是否安全 不全按退出應用 後期的動態加載 和 .so進行簽名和反調試檢查會陸續更新。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章