天天看點

Android權限機制說明-Android 6.0開發者文檔一、系統權限

此部分内容翻譯自Android 6.0開發者文檔的System Permissions部分

  • 一、系統權限
    • 1.1 安全機制
    • 1.2 應用簽名
    • 1.3 使用者ID和檔案通路
    • 1.4 權限使用方法
    • 1.5 聲明權限和權限控制
      • 1.5.1 在manifest檔案中的權限限制
      • 1.5.2 發送廣播時的權限限制
      • 1.5.3 其他權限限制
    • 1.6 URI權限

一、系統權限

Android作業系統的權限是分開的,每個應用在運作時都伴随着一個系統ID(Linux使用者ID群組ID)。系統的部分功能也被拆分到ID中。Linux是以将應用跟其他應用和系統隔離開。

Android通過附加的安全特性:“權限”機制,限制某個程序可以進行的特殊操作,用URI權限限制其通路特殊的資料。

1.1 安全機制

Android安全機制的中心思想是,預設情況下,應用沒有權限進行可能影響其他應用、作業系統或使用者的操作,如讀寫使用者私有資料(如通訊錄或email)、讀寫其他應用的檔案、通路網絡、保持裝置喚醒等等。

由于每個Android應用都運作在程序沙箱中,是以應用必須指明其需要哪些資源和資料。應用可以通過聲明基礎沙箱不提供的功能的權限來做到這一點。應用預先聲明它們需要的權限,在應用安裝時,Android系統會提示使用者并由使用者選擇是否允許應用擷取這些權限。

應用沙箱不依賴于建構應用的技術。Dalvik虛拟機不是安全邊界,任何應用都可以運作native代碼(原句:In particular the Dalvik VM is not a security boundary, and any app can run native code)。所有類型的應用:java、native或混合型,都是同樣的方式運作在沙箱中,并且有相同的權限機制。

1.2 應用簽名

所有的APK都必須用證書簽名(證書的私鑰儲存在開發者那裡)。證書用來辨別應用的作者。證書不需要權威證書機構的認證,它可以是而且通常情況下是自簽名的證書。Android使用證書的目的是區分應用作者,這樣的話系統就可以确定是否給此應用通路某個其他應用簽名級操作的權限,還有是否給予此應用相同的Linux ID。

1.3 使用者ID和檔案通路

Android系統會在安裝應用時為它配置設定一個Linux使用者ID。這個ID在應用安裝在裝置上後是不變的。在不同的裝置上,相同應用可能有不同的使用者ID;在同一裝置上,每個應用的使用者ID都是不同的。

由于程序級别的安全政策,任意兩個應用的代碼不能運作在同一個程序上,它們作為不同的Linux使用者運作。你可以在manifest檔案的manifest标簽指定sharedUserId屬性,要求系統配置設定相同的使用者ID。然後,這兩個應用将被當做同一個應用,使用相同的使用者ID,并有相同的檔案權限。注意,為了保證安全,簽名相同且sharedUserId相同的兩個應用才會被配置設定相同的使用者ID。

系統會按照應用的使用者ID儲存應用儲存的資料,進而使其他應用不能輕易的通路這些資料。當使用getSharedPreferences(Stirng,int)、openFileOutput(String,int)、openOrCreateDatabase(Sting,int,SQLiteDatabase.CursorFactory)建立檔案時,你可以用MODE_WORLD_READABLE或MODE_WORLD_WRITEABLE這樣的flag允許其他應用讀寫你的檔案。設定了這些flag後,這些檔案仍然由你的應用擁有,但是它們的全局讀寫權限會發生變化,這樣其他應用就可以通路它們了。

1.4 權限使用方法

預設情況下Android應用沒有任何權限,這意味着應用不能做任何能夠影響使用者體驗或應用資料的操作。為了能夠使用裝置的一些特性,你必須在manifest檔案中添加相應的

<uses-permission>

在應用安裝時,package installer會根據應用的簽名檢查應用申請的權限,并向使用者顯示你需要的權限及可能的後果,由使用者決定是否給予你權限。在使用者使用過程中不會去檢查權限,也就是說要麼在安裝的時候就準許該權限,使應用可以使用對應特性;要麼就不準許,這樣應用任何對此特性的操作都會失敗,而且不會提示給使用者。

通常權限問題會以SecurityException的形式傳回給你的應用。然而,不能保證權限問題總是這樣提示你。例如,用sendBroadcast(Intent)方法會檢查每個receiver的權限,這是發生在方法傳回之後的,是以當權限問題發生時你不會捕獲到異常。大多數情況下,權限問題會列印到系統log中。

通常情況下,如果使用者不允許你申請的權限,你的應用會安裝失敗。是以你不需要擔心由于使用者不給權限而導緻運作時異常。

Android系統提供的權限可以在Manifest.permission找到。但是應用也可以定義自己的權限,是以Manifest.permission不能包含所有的權限。

在程式運作時,有以下場景會強制進行權限檢查:

  • 防止應用調用某些系統功能
  • 防止應用啟動其他應用的activity
  • 控制誰可以接收你的廣播,控制誰可以向你發送廣播
  • content provider操作
  • 綁定或start服務

注意:系統平台更新後,可能某些API以前不需要權限,而現在要了。由于要保證存量應用能正确執行,Android會自動在應用manifest中添權重限申請。Android系統是根據應用的targetSdkVersion屬性确定需要添加哪些權限的。如果這個值低于目前版本,那麼Android系統就會添加新增的權限。

例如,WRITE_EXTERNAL_STORAGE權限是在API 4添加的,以限制對于共享存儲空間的通路。如果你的targetSdkVersion是3或更低,那麼這個權限會自動的添加到manifest中。

是以需要小心,Google應用商店在顯示應用所需權限時,會顯示一些你的應用可能沒有申請的權限。

為了避免這種情況,你需要将你的targetSdkVersion更新到盡可能高。

1.5 聲明權限和權限控制

在manifest檔案中添加

<permission>

标簽可以為你的應用添加自定義權限。

例如,你的應用需要控制哪些應用可以啟動你的activity,你可以按照如下方式聲明一個權限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.me.app.myapp" >
    <permission android:name="com.me.app.myapp.permission.DEADLY_ACTIVITY"
        android:label="@string/permlab_deadlyActivity"
        android:description="@string/permdesc_deadlyActivity"
        android:permissionGroup="android.permission-group.COST_MONEY"
        android:protectionLevel="dangerous" />
    ...
</manifest>
           

<protectionLevel>

屬性可以告訴系統,應該以哪種方式通知使用者,和誰可以擁有這些權限。

<permissionGroup>

屬性是可選的,它可以幫助系統向使用者顯示權限資訊。通常情況下,你可以将它設定為标準系統組(詳見android.Manifest.permission_group)或自定義的組。盡量使用已經存在的組,以簡化向使用者顯示的權限資訊。

<label>

<description>

屬性是string類型,用于向使用者展示權限的詳細資訊。label應該盡量簡短,簡要描述這個權限保護的功能是什麼即可。description适當可以長些,描述這個選項可以讓權限擁有者做什麼事情。我們提倡用兩行文字表示description,一行描述權限,另一行警告使用者如果擷取了這個權限可能發生什麼壞事情。

你可以通過系統設定或shell指令“adb shell pm list permissions”檢視目前系統存在的權限。對于開發者,在adb指令中添加“-s”選項可以檢視權限詳細資訊。

1.5.1 在manifest檔案中的權限限制

限制系統元件通路或應用通路的高等級權限可以在manifest檔案中申請。

Activity權限限制了誰可以啟動此activity。這權限在調用Context.startActivity()或Activity.startActivityForResult()方法時進行檢查,如果調用方沒有申請此權限,方法調用會抛出SecurityException。

Service權限限制了誰可以bind此service。這權限在調用Context.startService()、Context.stopService()、Context.bindService時進行檢查,如果調用方沒有申請此權限,方法調用會抛出SecurityException。

BroadcastReceiver權限限制了誰可以向這個receiver發送廣播。這個權限在調用Context.sendBroadcast()方法并傳回後檢查,是以即使此receiver沒有權限,也不會向調用方抛出異常,它隻是跳過這個receiver。同樣的,Context.registerReceiver()方法會控制誰可以向此注冊的receiver發送廣播。還有一種形式是,調用Context.sendBroadcast()時限制哪個BroadcastReceiver對象可以接收廣播(詳細過程見下文)。

ContentProvider權限限制了誰可以通路ContentProvider中的資料。

ContentProvider有一個額外的安全方案,叫做URI權限,後面會講到。不像其他元件,ContentProvider有兩個跟權限有關的屬性:“android:readPermission”限制了誰可以從這個provider中讀資料,“android:writePermission”限制了誰可以向它寫資料。注意,如果某個provider被讀寫權限同時保護,那麼你擁有寫權限不意味着你可以從provider讀資料。這個權限在我們第一次搜尋provider時進行檢查(如果你讀寫權限都沒有,會觸發SecurityException)。使用ContentResolver.query()要求讀權限,ContentResolver.insert()、ContentResolver.update()、ContentResolver.delete()要求寫權限。

1.5.2 發送廣播時的權限限制

為了限制誰可以向某個已注冊的BroadcastReceiver發送intent,你可以指定在發送廣播時需要的權限。通過在調用Context.sendBroadcast()時添加表示權限的string,就可以限制receiver所在應用必須有這個權限才能收到你的廣播。

注意receiver和broadcast都可以聲明權限要求,這時必須兩邊的權限檢查都通過了才能正确收到廣播。

1.5.3 其他權限限制

如果想要對service的調用進行更細緻的檢查,可以調用Context.checkCallingPermission()方法檢查調用方程序是否有指定權限。注意,這個方法隻針對從其他程序的調用,通常是通過某個服務暴露的IDL接口的調用(或者其他跨程序調用)。

如果你有其他程序的pid,你可以使用Context.checkPermission(String,int,int)檢查此程序是否有指定權限。如果你有其他應用的包名,可以使用PackageManager.checkPermissin(String,String)檢查這個應用是否擁有某個權限。

1.6 URI權限

标準權限系統通常對于content provider是不夠用的。content provider希望保護自己的讀寫權限,但是它也得向調用方提供特定的URI。以郵箱應用的附件功能為例。通路郵箱需要權限,因為這是敏感的使用者資料,然而,如果有個圖檔浏覽器需要打開辨別圖檔附件的URI,将會提示沒有相關權限。

要解決這個問題,Android引入了pre-URI權限機制:當啟動某個activity或者從某個activity傳回結果時,調用方可以設定Intent.FLAG_GRANT_READ_URI_PERMISSION或Intent.FLAG_GRANT_WRITE_URI_PERMISSION,這個flag可以給予被調用的activity通路intent中的特殊資料URI的權限,而不管被調用的activity本身是否有權限通路這些資料。

這個機制提供了一個給予操作臨時權限(如打開附件、從清單中選擇通訊錄等)的通用的能力-類型模型。它可以減少應用需要的權限,讓應用可以隻申請跟自己行為相關的權限。

擷取URI權限的前提是跟持有這些URI的content provider互動,這就要求content provider實作了這個機制(通過android:grantUriPermissions屬性或者

<grant-uri-permissions>

标簽聲明它支援這個機制)

更多資訊請看Context.grantUriPermission()、Context.revokeUriPermission()、Context.checkUriPermission()方法。