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进行签名和反调试检查会陆续更新。。。

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