1. 程式人生 > >《Android攻防實戰》讀書筆記——保護App安全

《Android攻防實戰》讀書筆記——保護App安全

保護app元件

  • 保護app元件的途徑有兩條:1.正確使用AndroidMenifest.xml檔案 2.在程式碼層面上強制進行許可權檢查

AndroidManifest 檔案加固

  • AndroidManifest檔案中的 android:exported 定義是否允許元件被其它app呼叫,如果app中的元件不需要被其它app呼叫,或者需要明確的與android系統其它部分的元件的互動隔離開的話,應該在AndroidManifest檔案中加入如下屬性:
<[元件名]android:exported = false></元件名>

原理

  • 通過AndroidManifest.xml檔案進行許可權檢查對於不同型別的app元件,作用是不同的,因為它們是通過不同的程序間通訊(IPC)機制進行互動的,不同的app元件,android:permission的作用是不同的

效果

  • Activity:使得外部app中的元件,不能成功使用startActivity或startActivityForResult執行相應的Activity
  • Service:使外部app中的元件不能繫結(通過呼叫bindService())或啟動(通過呼叫startService())相應的Service
  • Receiver 限制能向該receiver傳送廣播intent的外部app元件的數量
  • Provider 對能通過該content provider訪問的資料進行訪問限制

通過定製許可權保護元件

Android平臺定義了一系列用於保護系統服務和app元件的預設許可權,可以對許可權進行調整和定製

做法

  • 在string.xml中宣告表示permission標籤的字串
<string name= "custom_permission_label">Custom Permission</string>
  • 在app中新增保護級別為normal的定製許可權
在AndroidManifest.xml中
<permission android:name = "android.permission.CUSTOM_PERMISSION"
android:protextionLevel = "normal"
android:description = "My custom permission"
android:label = "@string/custom_permission_label" >
  • 在元件中使用,為了讓這個許可權能和其他許可權一樣工作,需要將它新增到app中某個元件的android:permission屬性中去
    • activity

      <activity ...
      android:permission = "android.permission.CUSTOM_PERMISSION">
      </activity>
    • content provider

      <provider ...
      android:permission = "android.permission.CUSTOM_PERMISSION">
      </provider>

      *service

      <service ...
      android:permission = "android.permission.CUSTOM_PERMISSION">
      </service>
  • 也可以通過宣告user-permission的方式讓其它app也能請求這一許可權
<uses-permission android:name="android.permission.CUSTOM_PERMISSION"/>

定義許可權組

  • 定製的許可權還可以在邏輯上分組,可以通過分配語法,告知請求一個給定許可權的app或者請求某幾個許可權的元件,

做法

  1. 新增一個表示許可權組的標籤(label)的字串,可以在string.xml中新增
<string name = "my_permissions_group_label">Personal Data Access</string>

2.在AndroidManifest.xml中新增

<permission-group
android:name = "android.permissions.personal_data_access_group"
android:label ="@string/my_permissions_group_label"
android:description = "Permissions that allow access to personaldata"
>
  1. 分配定義的許可權到組中
<permission ...
android:permissionGroup = "android.permission.personal_data_acess_group"/>

標籤解釋

  • android:name 定義名稱
  • androidprotectionLevel:定義許可權的保護級別
    • normal:定義非危險級別
    • dagerous:定義那些會使使用者暴露在財務、名譽、或法律風險下許可權
    • signature:自動賦予那些由定義該許可權的app相同簽名的app
    • signatureOrSystem:該許可權會被自動賦予那些作為系統映象的一部分或者有定義該許可權的app相同簽名的app

保護content provider路徑

  • 確保已經為之註冊了適當許可權

做法

  • 需要設定一個用於管理所有與你的認證相關路徑的讀和寫許可權的permission,在AndroidManifest 新增provider元素
<provider android:enabled = "true" android:exported = "true" android:authorities = "com.android.myAuthority android:name = "com.myapp.provider"
android:permission="[permission name]"
>
  • permission name 是其他app在讀或寫任何content provider路徑時必須擁有的許可權。把相關許可權新增在這一級別
可以要求提供讀寫許可權
<provider android:writePermission = "[write permission name]"
    android:readPermission = "[read permission name]"
>
</provider>
  • 還可以加入元素
<provider...>
<path-permission android:path = "/[path name]"
android:permission = "[read/write permission name]"
</provider>
  • 可以使用URI授權
<provider...>
<grant-uri-permission android:path="[path name]"/>
</provider>

防禦SQL注入攻擊

  • 避免使用SQLiteDatabase.rawQuery() 改用一個引數化的語句,將引數轉換
INSERT VALUES INTO [table name](?,?,?,?,....)

驗證App簽名 (防篡改)

*通過獲取keytool工具簽名時的證書指紋 SHA1,與app中讀取出來的指紋進行比較。

* 獲取當前執行的app的指紋程式碼:
 public String getCertificateSHA1Fingerprint(Context context) {
        //獲取包管理器
        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();
        }
        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();
    }

通過檢測安裝程式、模擬器、除錯標誌位反逆向

  • 檢查釋出渠道是否正常
  • 檢查自己是不是執行在一個模擬器中
  • 檢查app的可調式標識位是否被開啟

用ProGuard 刪除所有日誌資訊

  • ProGuard是一個開源的java程式碼混淆器,Android SDK自帶這個工具
  • Android studio 環境中設定
  minifyEnabled true
            proguardFile file('proguard-rules.pro')
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

用GexGuard進行高階程式碼混淆

  • GexGuard要比ProGuard的可維護性和可測試性更多,編譯和輸出則是經過優化和加固的。使用GexGuard可以獲得更多的安全性,同時,它還具有:API隱藏和字串加密的特性

    • API隱藏:使用API的反射機制對敏感API和程式碼的呼叫。
    • 字串加密:把原始碼中的字串加密,增加逆向的難度
  • DexGuard是收費的,但是對於一個公司來說是可以承受的

  • DexGuard要比Proguard要強大,提供資原始檔的混淆,和程式碼修改的檢測