android安全機制——權限

系統權限

本文內容

  1. 安全架構
  2. 應用簽署
  3. 用戶 ID 和文件訪問
  4. 使用權限
  5. 正常權限和危險權限
    1. 權限組
  6. 定義和實施權限
    1. 自定義權限建議
    2. ...在 AndroidManifest.xml 中
    3. ...發送廣播時
    4. 其餘權限實施
  7. URI 權限

關鍵類

  1. Manifest.permission
  2. Manifest.permission_group

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

其餘更詳細的安全功能經過「權限」機制提供,此機制會限制特定進程能夠執行的具體操做,而且根據 URI 權限受權臨時訪問特定的數據段。java

本文檔介紹應用開發者能夠如何使用 Android 提供的安全功能。通常性的 Android 安全性概覽在「Android 開源項目」中提供。android

安全架構

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

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

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

 

應用簽署

全部 APK(.apk 文件)都必須使用證書籤署,其私鑰由開發者持有。此證書用於識別應用的做者。證書不須要由證書頒發機構簽署;Android 應用在理想狀況下能夠並且一般也是使用自簽名證書。證書在 Android 中的做用是識別應用的做者。這容許系統授予或拒絕應用對簽名級權限的訪問,以及授予或拒絕應用得到與另外一應用相同的 Linux 身份的請求安全

 

用戶 ID 和文件訪問

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

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

應用存儲的任何數據都會被分配該應用的用戶 ID,而且其餘軟件包一般沒法訪問這些數據。使用 getSharedPreferences(String, int)openFileOutput(String, int)openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory) 建立新文件時,可使用 MODE_WORLD_READABLE 和/或 MODE_WORLD_WRITEABLE 標記容許任何其餘軟件包讀取/寫入文件。設置這些標記時,文件仍歸您的應用全部,但其全局讀取和/或寫入權限已適當設置,使任何其餘應用均可看見它。app

使用權限

基本 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 文檔中查看各版本添加的權限。

正常權限和危險權限

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

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

特殊權限

有許多權限其行爲方式與正常權限及危險權限都不一樣。SYSTEM_ALERT_WINDOWWRITE_SETTINGS 特別敏感,所以大多數應用不該該使用它們。若是某應用須要其中一種權限,必須在清單中聲明該權限,而且發送請求用戶受權的 intent。系統將向用戶顯示詳細管理屏幕,以響應該 intent。

如需瞭解有關如何請求這些權限的詳情,請參閱 SYSTEM_ALERT_WINDOWWRITE_SETTINGS 參考條目。

權限組

全部危險的 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() 方法中能夠找到更多信息。

相關文章
相關標籤/搜索