定義自訂應用程式權限

本文說明應用程式開發人員如何使用 Android 提供的安全防護功能來定義自己的權限。定義自訂權限後,應用程式就能與其他應用程式共用資源和功能。如要進一步瞭解權限,請參閱權限總覽

背景

Android 是一種權限分離的作業系統,每個應用程式會以不同的系統身分 (Linux 使用者 ID 和群組 ID) 執行。系統的各個部分也分為不同的身分。Linux 因而可以將各個應用程式隔離,並與系統隔離。

應用程式可以定義權限,讓其他應用程式可以提出要求使用其功能。也可以定義一些權限,自動提供給其他透過相同憑證簽署的應用程式使用。

應用程式簽署

所有的 APK 都必須使用由開發人員持有的私密金鑰的憑證簽署。憑證「不需要」經過憑證授權單位簽署。在 Android 應用程式中使用自行簽署的憑證,不僅完全沒問題,也是最典型的做法。Android 中的憑證是用來辨別應用程式的作者。如此一來,系統就能對簽章層級權限核准或拒絕應用程式存取,以及核准或拒絕應用程式想要如同其他應用程式要求獲得相同的 Linux 身分

在裝置製造時間之後授予簽章權限

自 Android 12 (API 級別 31) 起,您可以在宣告時期利用簽章層級權限的 knownCerts 屬性,指定已知的簽署憑證的摘要。

您可以宣告 knownCerts 屬性,並針對特定簽章層級的權限,使用應用程式的 protectionLevel 屬性中的 knownSigner 旗標。之後,如果提出要求的應用程式簽署譜系中的任何簽署者 (包括目前的簽署者) 符合以 knownCerts 屬性中之權限聲明的其中一個摘要,則系統便會將權限授予提出要求的應用程式。

knownSigner 旗標可以讓裝置和應用程式將簽章權限授予其他應用程式,而不需在裝置製造和運送時簽署該應用程式。

使用者 ID 和檔案存取

在安裝時,Android 會為每個套件提供專屬的 Linux 使用者 ID。只要套件存在於該裝置,此身分都會保持不變。同一個套件在其他裝置上可能會有不同的 UID;不過重要的是,每個套件在其裝置上都有不同的 UID。

由於安全性的強制執行程序是在程序層級進行,因此任兩個套件的程式碼通常無法在同一程序中執行,因為這些套件必須以不同的 Linux 使用者身分執行。

應用程式儲存的所有資料會被指定為與該應用程式相同的使用者 ID,而且通常其他套件不得存取。

若要進一步瞭解 Android 的安全模型,請參閱 Android 安全性總覽

定義及強制執行權限

若要強制執行自己的權限,您必須先在 AndroidManifest.xml 中使用一或多個 <permission> 元素進行宣告。

命名慣例

除非所有套件都是由同一個憑證簽署,否則系統不允許多個套件聲明名稱相同的權限。在套件已聲明一項權限的前提下,系統也不允許使用者安裝具有相同權限名稱的其他套件,但如果簽署這些套件時所用的憑證與第一個套件相同,則不在此限。

建議您使用反轉網域的命名方式,在權限前方加入應用程式套件名稱,後接 .permission.,然後是權限代表功能的說明,並以大寫 SNAKE_CASE 撰寫。例如 com.example.myapp.permission.ENGAGE_HYPERSPACE

按照此推薦方式命名,即可避免使用相同命名,也能清楚辨識自訂權限的擁有者和意圖。

範例

舉例來說,如果應用程式需要控管有哪些別的應用程式可以啟動其中一項活動,您可以按照以下方法為此作業聲明權限:

<manifest
  xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.myapp" >
    
    <permission
      android:name="com.example.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 屬性為必要屬性,用來指示系統如何告知使用者應用程式需要權限,或哪些應用程式可以擁有該項權限,如連結說明文件中所述。

android:permissionGroup 屬性則為選擇性,僅用於協助系統向使用者顯示權限。在多數情況下,您應將此值設為標準系統群組 (列在 android.Manifest.permission_group 中),不過您仍可自行定義群組,如下節所述。建議您使用現有群組,因為這樣可以簡化向使用者顯示的權限 UI。

您必須提供權限的標籤和說明。這些是字串資源,使用者可以在檢視權限清單 (android:label) 或單一權限 (android:description) 的詳細內容時看到這些資源。此標籤十分簡短,僅用幾個字來說明權限保護的重要功能。說明則為幾個句子,指出權限擁有者能夠執行的內容。我們的慣例是使用兩個句子,第一句用來說明權限,第二句則是警告使用者,讓他們知道應用程式獲得權限時可能會出錯的問題類型。

以下是 CALL_PHONE 權限的標籤和說明範例:

<string name="permlab_callPhone">directly call phone numbers</string>
<string name="permdesc_callPhone">Allows the app to call non-emergency
phone numbers without your intervention. Malicious apps may cause unexpected
calls on your phone bill.</string>

建立權限群組

如上一節所示,您可以使用 android:permissionGroup 屬性來協助系統向使用者描述權限。在多數情況下,您應將此值設為標準系統群組 (列在 android.Manifest.permission_group 中),不過您也可以使用 <permission-group> 自行定義您的群組。

<permission-group> 元素會為一組權限定義一個標籤,這組權限包括透過 <permission> 元素在資訊清單中聲明的內容,以及在其他地方宣告的項目。這只會影響權限在向使用者呈現時的分組方式。<permission-group> 元素不會指定屬於群組的權限,不過會為群組命名。

您可以在 <permission> 元素的 permissionGroup 屬性中指定群組名稱來為群組設定權限。

<permission-tree> 元素可用來聲明命名空間,供程式碼中定義的某一權限群組使用。

自訂權限建議

您可以定義自訂權限,並定義 <uses-permission> 元素,要求其他應用程式的自訂權限。不過,請務必審慎評估是否需要這麼做。

  • 如果要設計一套應用程式,讓應用程式可顯示彼此的功能,請嘗試將應用程式設計為每個權限只需定義一次。如果應用程式並非使用相同的憑證簽署,您就必須這麼做。即使應用程式都使用相同的憑證簽署,最好的做法仍是每項權限僅定義一次。
  • 如果該功能適用的應用程式,與提供該功能的應用程式都須以同一個簽章簽署,您或許可以檢查簽章,而不必定義自訂權限。當其中一個應用程式為另一個應用程式發出要求時,第二個應用程式可以先驗證兩個應用程式是否使用相同的憑證簽署,再發出該項要求。

如果需要自訂權限,請考慮是否只有相同開發人員 (與執行權限檢查的應用程式相同) 所簽署的應用程式,才需要存取該自訂權限,例如相同的開發人員在相同應用程式之間進行安全處理序間通訊時。如果是的話,建議您使用簽章權限。簽章權限的內容對使用者是透明的,並避免使用者確認的權限,以免混淆使用者。

繼續閱讀以下相關內容:

<uses-permission>
資訊清單標記的 API 參考資料,用於宣告應用程式所需的系統權限。

您可能也想瞭解以下主題:

Android 安全性總覽
針對 Android 平台安全性模型的詳細討論。