android-簡介[系統權限]

Android 是一個權限分隔的操作系統,其中每個應用都有其獨特的系統標識(Linux 用戶 ID 和組 ID)。系統各部分也分隔爲不同的標識。Linux 據此將不同的應用以及應用與系統分隔開來。

其他更詳細的安全功能通過“權限”機制提供,此機制會限制特定進程可以執行的具體操作,並且根據 URI 權限授權臨時訪問特定的數據段。

本文檔介紹應用開發者可以如何使用 Android 提供的安全功能。一般性的 Android 安全性概覽在“Android 開源項目”中提供。

安全架構


Android 安全架構的中心設計點是:在默認情況下任何應用都沒有權限執行對其他應用、操作系統或用戶有不利影響的任何操作。這包括讀取或寫入用戶的私有數據(例如聯繫人或電子郵件)、讀取或寫入其他應用程序的文件、執行網絡訪問、使設備保持喚醒狀態等。

由於每個 Android 應用都是在進程沙盒中運行,因此應用必須顯式共享資源和數據。它們的方法是聲明需要哪些權限來獲取基本沙盒未提供的額外功能。應用以靜態方式聲明它們需要的權限,然後 Android 系統提示用戶同意。

應用沙盒不依賴用於開發應用的技術。特別是,Dalvik VM 不是安全邊界,任何應用都可運行原生代碼(請參閱 Android NDK)。各類應用 — Java、原生和混合 — 以同樣的方式放在沙盒中,彼此採用相同程度的安全防護。

應用簽署


所有 APK(.apk 文件)都必須使用證書籤署,其私鑰由開發者持有。此證書用於識別應用的作者。證書不需要由證書頒發機構簽署;Android 應用在理想情況下可以而且通常也是使用自簽名證書。證書在 Android 中的作用是識別應用的作者。這允許系統授予或拒絕應用對簽名級權限的訪問,以及授予或拒絕應用獲得與另一應用相同的 Linux 身份的請求

用戶 ID 和文件訪問


在安裝時,Android 爲每個軟件包提供唯一的 Linux 用戶 ID。此 ID 在軟件包在該設備上的使用壽命期間保持不變。在不同設備上,相同軟件包可能有不同的 UID;重要的是每個軟件包在指定設備上的 UID 是唯一的。

由於在進程級實施安全性,因此任何兩個軟件包的代碼通常都不能在同一進程中運行,因爲它們需要作爲不同的 Linux 用戶運行。您可以在每個軟件包的AndroidManifest.xml 的 manifest 標記中使用 sharedUserId 屬性,爲它們分配相同的用戶 ID。這樣做以後,出於安全目的,兩個軟件包將被視爲同一個應用,具有相同的用戶 ID 和文件權限。請注意,爲保持安全性,只有兩個簽署了相同簽名(並且請求相同的 sharedUserId)的應用才被分配同一用戶 ID。

應用存儲的任何數據都會被分配該應用的用戶 ID,並且其他軟件包通常無法訪問這些數據。使用 getSharedPreferences(String, int)openFileOutput(String, int) 或 openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory) 創建新文件時,可以使用MODE_WORLD_READABLE 和/或 MODE_WORLD_WRITEABLE 標記允許任何其他軟件包讀取/寫入文件。設置這些標記時,文件仍歸您的應用所有,但其全局讀取和/或寫入權限已適當設置,使任何其他應用都可看見它。

使用權限


基本 Android 應用默認情況下未關聯權限,這意味着它無法執行對用戶體驗或設備上任何數據產生不利影響的任何操作。要利用受保護的設備功能,必須在應用清單中包含一個或多個 <uses-permission> 標記。

例如,需要監控傳入的短信的應用要指定:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.app.myapp" >
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    ...
</manifest>

如果您的應用在其清單中列出正常權限(即,不會對用戶隱私或設備操作造成很大風險的權限),系統會自動授予這些權限。如果您的應用在其清單中列出危險權限(即,可能影響用戶隱私或設備正常操作的權限),系統會要求用戶明確授予這些權限。Android 發出請求的方式取決於系統版本,而系統版本是應用的目標:

  • 如果設備運行的是 Android 6.0(API 級別 23)或更高版本,並且應用的 targetSdkVersion 是 23 或更高版本,則應用在運行時向用戶請求權限。用戶可隨時調用權限,因此應用在每次運行時均需檢查自身是否具備所需的權限。如需瞭解有關在應用中請求權限的詳細信息,請參閱使用系統權限培訓指南。
  • 如果設備運行的是 Android 5.1(API 級別 22)或更低版本,並且應用的 targetSdkVersion 是 22 或更低版本,則系統會在用戶安裝應用時要求用戶授予權限。如果將新權限添加到更新的應用版本,系統會在用戶更新應用時要求授予該權限。用戶一旦安裝應用,他們撤銷權限的唯一方式是卸載應用。

通常,權限失效會導致 SecurityException 被扔回應用。但不能保證每個地方都是這樣。例如,sendBroadcast(Intent) 方法在數據傳遞到每個接收者時會檢查權限,在方法調用返回後,即使權限失效,您也不會收到異常。但在幾乎所有情況下,權限失效會記入系統日誌。

Android 系統提供的權限請參閱 Manifest.permission。此外,任何應用都可定義並實施自己的權限,因此這不是所有可能權限的詳盡列表。

可能在程序運行期間的多個位置實施特定權限:

  • 在調用系統時,防止應用執行某些功能。
  • 在啓動 Activity 時,防止應用啓動其他應用的 Activity。
  • 在發送和接收廣播時,控制誰可以接收您的廣播,誰可以向您發送廣播。
  • 在訪問和操作內容提供程序時。
  • 綁定至服務或啓動服務。

自動權限調整

隨着時間的推移,平臺中可能會加入新的限制,要想使用特定 API,您的應用可能必須請求之前不需要的權限。因爲現有應用假設可隨意獲取這些 API 應用的訪問權限,所以 Android 可能會將新的權限請求應用到應用清單,以免在新平臺版本上中斷應用。Android 將根據爲 targetSdkVersion 屬性提供的值決定應用是否需要權限。如果該值低於在其中添加權限的版本,則 Android 會添加該權限。

例如,API 級別 4 中加入了 WRITE_EXTERNAL_STORAGE 權限,用以限制訪問共享存儲空間。如果您的 targetSdkVersion 爲 3 或更低版本,則會向更新 Android 版本設備上的應用添加此權限。

注意:如果某權限自動添加到應用,則即使您的應用可能實際並不需要這些附加權限,Google Play 上的應用列表也會列出它們。

爲避免這種情況,並且刪除您不需要的默認權限,請始終將 targetSdkVersion 更新至最高版本。可在 Build.VERSION_CODES 文檔中查看各版本添加的權限。

正常權限和危險權限


系統權限分爲幾個保護級別。需要了解的兩個最重要保護級別是正常權限和危險權限:

  • 正常權限涵蓋應用需要訪問其沙盒外部數據或資源,但對用戶隱私或其他應用操作風險很小的區域。例如,設置時區的權限就是正常權限。如果應用聲明其需要正常權限,系統會自動向應用授予該權限。如需當前正常權限的完整列表,請參閱正常權限
  • 危險權限涵蓋應用需要涉及用戶隱私信息的數據或資源,或者可能對用戶存儲的數據或其他應用的操作產生影響的區域。例如,能夠讀取用戶的聯繫人屬於危險權限。如果應用聲明其需要危險權限,則用戶必須明確嚮應用授予該權限。

權限組

所有危險的 Android 系統權限都屬於權限組。如果設備運行的是 Android 6.0(API 級別 23),並且應用的targetSdkVersion 是 23 或更高版本,則當用戶請求危險權限時系統會發生以下行爲:

  • 如果應用請求其清單中列出的危險權限,而應用目前在權限組中沒有任何權限,則系統會向用戶顯示一個對話框,描述應用要訪問的權限組。對話框不描述該組內的具體權限。例如,如果應用請求READ_CONTACTS 權限,系統對話框只說明該應用需要訪問設備的聯繫信息。如果用戶批准,系統將嚮應用授予其請求的權限。
  • 如果應用請求其清單中列出的危險權限,而應用在同一權限組中已有另一項危險權限,則系統會立即授予該權限,而無需與用戶進行任何交互。例如,如果某應用已經請求並且被授予了 READ_CONTACTS 權限,然後它又請求 WRITE_CONTACTS,系統將立即授予該權限。

任何權限都可屬於一個權限組,包括正常權限和應用定義的權限。但權限組僅當權限危險時才影響用戶體驗。可以忽略正常權限的權限組。

如果設備運行的是 Android 5.1(API 級別 22)或更低版本,並且應用的 targetSdkVersion 是 22 或更低版本,則系統會在安裝時要求用戶授予權限。再次強調,系統只告訴用戶應用需要的權限,而不告知具體權限。

表 1. 危險權限和權限組。

權限組 權限
CALENDAR
CAMERA
CONTACTS
LOCATION
MICROPHONE
PHONE
SENSORS
SMS
STORAGE

定義和實施權限


要實施您自己的權限,必須先使用一個或多個 <permission> 元素在 AndroidManifest.xml 中聲明它們。

例如,想要控制誰可以開始其中一個 Activity 的應用可如下所示聲明此操作的權限:

<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>

:系統不允許多個軟件包使用同一名稱聲明權限,除非所有軟件包都使用同一證書籤署。如果軟件包聲明權限,則系統不允許用戶安裝具有相同權限名稱的其他軟件包,除非這些軟件包使用與第一個軟件包相同的證書籤署。爲避免命名衝突,建議對自定義權限使用相反域名樣式命名,例如com.example.myapp.ENGAGE_HYPERSPACE

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 application to call
    phone numbers without your intervention. Malicious applications may
    cause unexpected calls on your phone bill. Note that this does not
    allow the application to call emergency numbers.</string>

您可以使用 Settings 應用和 shell 命令 adb shell pm list permissions 查看系統中當前定義的權限。要使用 Settings 應用,請轉到 Settings >Applications。選擇一個應用並向下滾動查看該應用使用的權限。對於開發者,adb '-s' 選項以類似於用戶將會看到的形式顯示權限:

$ adb shell pm list permissions -s
All Permissions:

Network communication: view Wi-Fi state, create Bluetooth connections, full
Internet access, view network state

Your location: access extra location provider commands, fine (GPS) location,
mock location sources for testing, coarse (network-based) location

Services that cost you money: send SMS messages, directly call phone numbers

...  

自定義權限建議

應用可以定義自己的自定義權限,並通過定義 <uses-permission> 元素請求其他應用的自定義權限。不過,您應該仔細評估您的應用是否有必要這樣做。

  • 如果要設計一套向彼此顯示功能的應用,請儘可能將應用設計爲每個權限只定義一次。如果所有應用並非使用同一證書籤署,則必須這樣做。即使所有應用使用同一證書籤署,最佳做法也是每個權限只定義一次。
  • 如果功能僅適用於使用與提供應用相同的簽名所簽署的應用,您可能可以使用簽名檢查避免定義自定義權限。當一個應用向另一個應用發出請求時,第二個應用可在遵從該請求之前驗證這兩個應用是否使用同一證書籤署。
  • 如果您要開發一套只在您自己的設備上運行的應用,則應開發並安裝管理該套件中所有應用權限的軟件包。此軟件包本身無需提供任何服務。它只是聲明所有權限,然後套件中的其他應用通過 <uses-permission> 元素請求這些權限。

實施 AndroidManifest.xml 中的權限

您可以通過 AndroidManifest.xml 應用高級權限,限制訪問系統或應用的全部組件。要執行此操作,在所需的組件上包含 android:permission 屬性,爲用於控制訪問它的權限命名。

Activity 權限(應用於 <activity> 標記)限制誰可以啓動相關的 Activity。在 Context.startActivity() 和 Activity.startActivityForResult() 時會檢查權限;如果調用方沒有所需的權限,則調用會拋出 SecurityException

Service 權限(應用於 <service> 標記)限制誰可以啓動或綁定到相關的服務。在 Context.startService()Context.stopService() 和Context.bindService() 時會檢查權限;如果調用方沒有所需的權限,則調用會拋出 SecurityException

BroadcastReceiver 權限(應用於 <receiver> 標記)限制誰可以發送廣播給相關的接收方。在 Context.sendBroadcast() 返回後檢查權限,因爲系統會嘗試將提交的廣播傳遞到指定的接收方。因此,權限失效不會導致向調用方拋回異常;只是不會傳遞該 intent。同樣,可以向 Context.registerReceiver()提供權限來控制誰可以廣播到以編程方式註冊的接收方。另一方面,可以在調用 Context.sendBroadcast() 時提供權限來限制允許哪些 BroadcastReceiver 對象接收廣播(請參閱下文)。

ContentProvider 權限(應用於 <provider> 標記)限制誰可以訪問 ContentProvider 中的數據。(內容提供程序有重要的附加安全工具可用,稱爲 URI 權限,將在後面介紹。)與其他組件不同,您可以設置兩個單獨的權限屬性:android:readPermission 限制誰可以讀取提供程序,android:writePermission 限制誰可以寫入提供程序。請注意,如果提供程序有讀取和寫入權限保護,僅擁有寫入權限並不表示您可以讀取提供程序。第一次檢索提供程序時將會檢查權限(如果沒有任何權限,將會拋出 SecurityException),對提供程序執行操作時也會檢查權限。使用ContentResolver.query() 需要擁有讀取權限;使用 ContentResolver.insert()ContentResolver.update()ContentResolver.delete() 需要寫入權限。在所有這些情況下,沒有所需的權限將導致調用拋出 SecurityException

發送廣播時實施權限

除了實施誰可以向註冊的 BroadcastReceiver 發送 intent 的權限(如上所述),您還可以指定在發送廣播時需要的權限。通過使用權限字符串調用Context.sendBroadcast(),您可以要求接收方的應用必須擁有該權限纔可接收您的廣播。

請注意,接收者和廣播者可能需要權限。此時,這兩項權限檢查都必須通過後方可將 intent 傳遞到相關的目標。

其他權限實施

可對任何服務調用實施任意細化的權限。這可通過 Context.checkCallingPermission() 方法完成。使用所需的權限字符串調用,它將返回一個整數,表示權限是否已授予當前的調用進程。請注意,僅在執行從另一個進程傳入的調用(通常是通過從服務發佈的 IDL 界面或者指定給另一進程的某種其他方式完成)時纔可使用此方法。

檢查權限還有許多其他有用的方法。如果您有另一個進程的 pid,可以使用 Context 方法 Context.checkPermission(String, int, int) 檢查針對該 pid 的權限。如果您有另一個應用的軟件包名稱,可以使用直接的 PackageManager 方法 PackageManager.checkPermission(String, String) 瞭解是否已爲特定軟件包授予特定權限。

URI 權限


到目前爲止所述的是標準權限系統,內容提供程序僅僅使用此係統通常是不夠的。內容提供程序可能需要通過讀取和寫入權限保護自己,而其直接客戶端也需要將特定 URI 傳給其他應用以便於它們運行。郵件應用中的附件是一個典型的示例。應通過權限保護對郵件的訪問,因爲這是敏感的用戶數據。但是,如果將圖像附件的 URI 提供給圖像查看程序,該圖像查看程序不會有打開附件的權限,因爲它沒有理由擁有訪問所有電子郵件的權限。

此問題的解決方法是採用 per-URI 權限機制:在啓動 Activity 或返回結果給 Activity 時,調用方可以設置 Intent.FLAG_GRANT_READ_URI_PERMISSION 和/或Intent.FLAG_GRANT_WRITE_URI_PERMISSION。這將授予接收 Activity 權限訪問 intent 中的特定數據 URI,而不管它是否具有訪問 intent 對應的內容提供程序中數據的任何權限。

此機制支持常見的能力式模型,其中用戶交互(打開附件、從列表中選擇聯繫人等)驅動臨時授予細化的權限。這是一項關鍵功能,可將應用所需的權限縮小至只與其行爲直接相關的權限。

但授予細化的 URI 權限需要與擁有這些 URI 的內容提供程序進行一定的合作。強烈建議內容提供程序實施此功能,並且通過android:grantUriPermissions 屬性或 <grant-uri-permissions> 標記聲明支持此功能。

在 Context.grantUriPermission()Context.revokeUriPermission() 和 Context.checkUriPermission() 方法中可以找到更多信息。

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