開頭不得不吐槽下:簡書的markdown編輯器居然不支援目錄索引,非常的坑.
安卓的安全和權限架構
先來看下google官方文檔中對android安全架構的介紹:
Android 安全架構的中心設計點是:在預設情況下任何應用都沒有權限執行對其他應用、作業系統或使用者有不利影響的任何操作。這包括讀取或寫入使用者的私有資料(例如聯系人或電子郵件)、讀取或寫入其他應用程式的檔案、執行網絡通路、使裝置保持喚醒狀态等。
由于每個 Android 應用都是在程序沙盒中運作,是以應用必須顯式共享資源和資料。它們的方法是聲明需要哪些權限來擷取基本沙盒未提供的額外功能。應用以靜态方式聲明它們需要的權限,然後 Android 系統提示使用者同意。
應用沙盒不依賴用于開發應用的技術。特别是,Dalvik VM 不是安全邊界,任何應用都可運作原生代碼(請參閱 [Android NDK](https://developer.android.google.cn/tools/sdk/ndk/index.html))。各類應用 — Java、原生和混合 — 以同樣的方式放在沙盒中,彼此采用相同程度的安全防護。
可以看出 android安全架構的兩個中心點就是 權限 和 沙盒模型.
沙盒模型 屬于程序級别的安全防護,它設定程序級别的隔離帶來保證程序間互不影響.而權限 則屬于開發者可以在應用層控制的屬性,google設計權限機制 就是為了讓開發者帶上腳鍊跳舞,在授予應用開發者申請權限的能力的同時,又極力限制開發者對權限的濫用,避免app的惡意行為.
permission的三個重要性級别
為了後面便于分析permission的重要性級别與 android:permission 之間産生的化學反應,首先列出permission的三個重要性級别
protectionlevel.png
permission的危險級别由 android:protectionlevel 這項屬性來決定.它一共有四個級别:
normal
The default value. A lower-risk permission that gives requesting applications access to isolated application-level features, with minimal risk to other applications, the system, or the user. The system automatically grants this type of permission to a requesting application at installation, without asking for the user's explicit approval (though the user always has the option to review these permissions before installing).
正常級别的權限屬于最低危險等級的權限級别.這類權限不需要使用者通過對話框顯式地同意,即隻要開發者在manifest裡申請了權限,系統就會自動授予app這些權限.
dangerous
A higher-risk permission that would give a requesting application access to private user data or control over the device that can negatively impact the user. Because this type of permission introduces potential risk, the system may not automatically grant it to the requesting application. For example, any dangerous permissions requested by an application may be displayed to the user and require confirmation before proceeding, or some other approach may be taken to avoid the user automatically allowing the use of such facilities.
危險級别的權限通常具有通路使用者隐私資料的屬性(例如讀取手機狀态資訊,收發短信,撥打電話),是以這類權限通常不會被自動授予app.
signature
A permission that the system grants only if the requesting application is signed with the same certificate as the application that declared the permission. If the certificates match, the system automatically grants the permission without notifying the user or asking for the user's explicit approval.
簽名級别的權限:文檔裡是這樣解釋的: 隻有當請求該權限的application具有與聲明權限的application相同的證書時,這項權限才會被授予.如果證書相同,這項權限會被自動授予而不用顯式地通知使用者或征求使用者同意.
和标簽
Paste_Image.png
先來看看 标簽的定義, 它用于定義權限,主要屬性有兩個,除了 protectionLevel 這個屬性以外,還有就是 permissionGroup 這個屬性,它将不同權限定義在一個權限組中.關于權限組,需要知道的一點是當某個權限組中包含了某個 Dangerous-Permission 的時候,如果使用者同意了使用該危險權限,那麼app會自動被授予使用該使用者組中其它權限的權力.
public static final class permission_group {
public static final String CALENDAR = "android.permission-group.CALENDAR";
public static final String CAMERA = "android.permission-group.CAMERA";
public static final String CONTACTS = "android.permission-group.CONTACTS";
public static final String LOCATION = "android.permission-group.LOCATION";
public static final String MICROPHONE = "android.permission-group.MICROPHONE";
public static final String PHONE = "android.permission-group.PHONE";
public static final String SENSORS = "android.permission-group.SENSORS";
public static final String SMS = "android.permission-group.SMS";
public static final String STORAGE = "android.permission-group.STORAGE";
public permission_group() {
throw new RuntimeException("Stub!");
}
}
這裡把安卓中所有的使用者組列出來,類别有月曆,相機,聯系人,定位,MicroPhone,通話,傳感器,短信,存儲 這幾個類别,都是涉及使用者隐私的相當敏感的權限.
這個标簽用的比較多了,就是在application标簽下聲明要擷取某項權限.
這個标簽用的特别少,,我在google上搜尋了一圈也沒見到有詳細的介紹
,大概的用法是: 可以通過PackgeManager.addPermission來動态添加某個權限加入到 permission-tree 中,前提是在相同的package中,也就是包名相同,這個方法的具體實作在 ApplicationPackageManager中,查閱其它資料了解到這個方法應該是給系統級别的操作使用的,日常開發app應該基本用不到.
Android 4.4 , 5.0 , 6.0對權限的處理方法
終于講到安卓各版本對申請權限的不同處理辦法了.總的來說android對權限的控制是越來越強的.
在4.4系統上,隻要開發者在 manifest 裡面申請對應的權限,apk在安裝到手機上後,都會彈出權限清單展示給使用者該app将要擷取到哪些權限,隻要使用者同意了安裝這些權限就會自動被授予給app.
5.0系統對權限的處理與4.4基本沒有差别,但一些第三方廠商的rom在應用管理中提供可關閉權限的開關,這樣做就造成了很大的适配困擾,等下後面再講.
6.0以上的系統在處理申請權限時,會彈出dialog來詢問使用者是否授予該權限,除了同意和拒絕dialog還會提供不再詢問這個選項.
主動申請權限的最佳實踐和碎片化處理
在 Android 6.0 以上主動申請權限時,谷歌的官方文檔和視訊給出了最佳實踐 :
使用者如果勾選了不再詢問選項,則後續申請該權限時将不再彈出對話框,如果使用者拒絕過該權限的申請但沒有勾選不再詢問,那麼ActivityCompat.shouldShowRequestPermissionRationale()将傳回true,開發者可在此時展示一個界面來介紹為何應用需要使用該權限,來争取使用者授予權限
Paste_Image.png
上圖是官方文檔中對于權限申請的最佳實踐代碼.
12月13号:補充shouldShowRequestPermissionRationale()的原理
shouldShowRequest.png
可以看到 shouldShowRequestPermissionRationale 這個方法隻有在API 23 上才會生效.
image.png
最終調用的函數的邏輯如下: 首先判斷權限如果被授予了就會直接傳回false,然後會判斷該權限是否不含 SYSTEM,POLICY,USER 這三個标志位并且含有 USER_SET這個标志位.
FLAG.png
SYSTEM.png
再看這幾個标志位的含義:大意就是權限的授予狀态是否被FIXED了也就是鎖死了,而鎖死的原因可能是使用者點選了不再詢問, 也有可能是因為該app是系統app。
Paste_Image.png
處理的結果會在 onRequestPermissionResult() 中傳回,開發者可在這個回調中擷取每一項權限具體的申請情況,然後提示對應的資訊給使用者。
在Flyme5.1 上測試,發現targetsdk<23 使用 checkSelfPermission仍然失效,并且會有動态權限申請的彈框出現,這說明flyme在5.0上也做了動态權限的邏輯.
在Android 5.0上,上述的 CheckPermission(),requestPermission()方法統統不能使用,因為這些api都是在6.0之後才加入的,但偏偏某些第三方rom加入了權限的開關界面,這樣一來,如果使用者在安裝apk後進入應用管理界面手動關閉了權限,那麼必定會對app的正常邏輯造成影響,而此時又沒有api可以主動擷取權限的擷取狀況.
關于這點,我參閱了 騰訊bugly的一篇文章http://blog.csdn.net/tencent_bugly/article/details/77085531,這篇文章給出的解決方案是通過反射 AppOpsManager 這個類并調用 checkOp(int op, int uid,String packageName) 方法來檢查是否擷取了權限,之後再跳轉到每個rom具體的應用管理開關界面來,然後主動提示使用者打開開關.文章中提到可以通過 adb shell dumpsys activity activities 這個指令來擷取各個row的權限開關界面,然後再通過顯式intent的方法跳轉到具體界面,但經過我的測試, checkOp(int op,int uid,String PackageName)方法無法保證100%能正常運作,這恐怕也是因為第三方rom對這個類的修改.
是以在 加入權限開關的5.0第三方rom系統,暫時找不到一個完美的解決方案,當然這也可能是因為我測試的範圍不夠大也不夠精細,是以如果有哪位閱讀了本文的開發者有完美的解決方案,請一定在我的文章下留言幫我解惑.
對于 signature 權限的一點想法
在介紹 protectionLevel 的時候,有這麼一個級别 signature ,關于它的解釋是:隻有證書也就是keystore相同的應用才能被授予該項權限,而我們又知道 activity等元件是可以聲明android:permission這項屬性的,結合自定義權限使用就可以保證隻有擁有了特定權限才能使用該元件.但我們知道 manifest中聲明的自定義權限是可以被輕易的看到的,某些惡意應用和開發者在manifest中找到這些自定義權限後完全可以通過嗅探來攻擊四大元件.
你可能會問隻要把四大元件的 exported屬性聲明為false不就可以了麼,但是這樣一來,某些被應用信任的程序或插件運作的程序就無法通路到該元件了.
我想,解決的辦法就是使用為自定義權限聲明 signature_ 危險級别,這樣一來隻有擁有相同證書的apk跑起來的程序可以使用該權限,在很大程度上保證了app的安全.
不過這隻是我結合官方文檔的合理推論,并沒有實際實踐過,如果有哪位大神實踐過,請一定幫我指正錯誤之處.
參考文章連結