權限

  1. 安全架構

  2. 應用簽名

  3. 用戶ID和文件訪問

  4. 使用權限

  5. 聲明及執行權限

    1. 在清單文件中執行權限

    2. 發送廣播時執行權限

    3. 其他權限執行

  6. URI權限


權限


本文描述了應用開發人員如何使用由Android提供的安全特性。Android開源項目提供一個比較綜合的Android安全概述


Android是一個權限分隔的操作系統,它裏面的每一個應用以不同的系統身份(Linux用戶ID和組ID)運行。系統部分也被分割成不同的身份。因此,linux把應用從彼此及系統中隔離了。


附加的細粒度安全特性是由“權限”機制提供的,它在一個特定進程可以執行的特定操作上施加限制,並且每個URI權限授予特設訪問指定的數據塊。



1.安全架構



Android安全架構的中心設計思想是,默認情況下,沒有應用有權對其他應用,操作系統,或者用戶造成不利影響。這包括讀或寫用戶的私有數據(比如通訊錄或郵件),讀或者寫其他應用的文件,執行網絡訪問,保持設備喚起等待。


因爲Android在應用彼此間用沙箱隔離了,所以應用必須顯示地共享資源和數據。它們通過聲明它們需要的權限來做到這一點,這些權限可以使它們具有基礎沙箱沒有提供的額外能力。應用程序靜態地聲明它們需要的權限,然後Android系統在程序被安裝時提示用戶許可。Android沒有動態(在運行時)授予權限的機制,因它使用戶體驗的安全性複雜化了。


應用沙箱不依賴用於構建應用程序的技術。在個別虛擬機內部沒有安全界限,因此任何應用可以運行本地代碼(詳見the Android NDK)。所有類型的應用— Java,本地,以及它們的混合— 都以同樣的方式被隔離在沙箱內,並且彼此間有相同的安全等級。



2. 應用簽名



所有Android應用程序(.apk文件)必須通過證書籤名,證書的密鑰由應用的開發者掌握着。該證書標示了應用的作者。證書不需要由證書發佈機構來設計:Android應用使用自簽名證書是完全允許的和典型的。在Android中,證書的目的是爲了區分應用的作者。這允許系統同意或拒絕應用訪問簽名級權限以及同意或拒絕應用作爲另一應用獲取相同的linux身份的請求。



3. 用戶ID和文件訪問



在安裝時,Android給每個包一個不同的Linux用戶ID。該ID在包的生命週期期間保持不變,相同的包可能有不同的UID(用戶ID);重要的是,在給定設備上每個包擁有不同的UID。


由於安全執行發生進程層面,因此任何兩個包的代碼不能正常地運行在相同的進程內,因爲它們需要作爲不同的Linux用戶來運行。可以在每個包的AndroidManifest.xml文件的manifest標籤上使用sharedUserId屬性來指定相同的用戶ID。通過這樣做,出於安全的目的,兩個包被視爲同一個應用,擁有相同的用戶ID和文件權限。注意,爲了維持安全,僅兩個被簽有相同簽名(及請求相同的shareUserId)的應用將被指定相同的用戶ID。


任何由應用存儲的數據將被指定了該應用的用戶ID,因此它對其他包來說一般是不可訪問的。當使用getSharedPreferences(String, int),openFileOutput(String, int),openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)創建一個新的文件時,可以使用MODE_WORLD_READABLE 和/或MODE_WORLD_WRITEABLE標誌來允許任何其他包讀/寫該文件。在設置這些標誌時,文件依然由你的應用擁有,但它的全局讀和/或寫權限已被恰當地設置,因此任何其他的應用都可以看到它。



4. 使用權限



默認情況下,一個基本的Android應用沒有權限與之關聯,這意味着它不能做可能明顯影響用戶體驗或設備上數據的任何事情。爲了利用設備的保護特性,必須在AndroidManifest.xml文件中包含一個或多個<uses-permission>標籤來聲明應用所需要的權限。


例如,一個需要監控接收SMS信息的應用將指定:


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


在應用安裝時,包安裝器授予了應用請求的權限,這是基於檢查聲明那些權限和/或與用戶交互的應用的簽名基礎之上的。當應用運行時,沒有用戶需要做的檢查了:它要麼在安裝時被授予了特定權限,然後可以隨意地使用那個特性,或者沒有被授予該權限並且任何使用該特性的嘗試都將會失敗,而不提示用戶。


很多時候,權限失敗將會拋給應用一個SecurityException異常。然而,這並不能保證在每個地方都會發生。例如,當數據正被遞送個每個接受者時,sendBroadcast(Intent)方法檢查權限,該方法調用返回後,如果有權限失敗將不會收到異常。幾乎所有情況下,權限失敗將被打印到系統log中。


Android系統提供的權限可以在Manifest.permission找到。任何應用也可以定義和執行它們自己的權限,因此這不是一個全部可能權限的綜合性清單。

特定的權限可能在程序執行期間的很多地方被執行:

A particular permission may be enforced at a number of places during yourprogram's operation:

  • 在系統調用時,阻止應用執行某一方法。
  • 當啓動活動時,阻止應用啓動其他應用的活動。
  • 發送和接收廣播時,控制誰可以接收你的廣播或者誰可以給你發送廣播。
  • 當訪問和操作內容提供者時。
  • 綁定到或者啓動服務時。


注意:隨着時間的推移,新的限制可能被添加到平臺中,爲了使用某些APIs,你的應用必須請求它最初不需要的權限。因爲現有的應用假定訪問那些APIs是自由獲得的,Android可能對應用的清單文件應用新的權限請求以避免在新的平臺版本上阻礙應用執行。至於應用是否可能需要權限,Android基於提供給targetSdkVersion屬性的值來做出決定。如果該值低於權限被添加的版本,那麼Android則添加該權限。


例如,WRITE_EXTERNAL_STORAGE權限被添加在API標準4中以限制訪問共享的存儲空間。如果你的targetSdkVersion是3或者更低,那麼該權限在新版的Android上被添加到你的應用中。


注意,如果這發生在你的應用中,你的應用列在Google Play上將顯示這些被需要的權限,即便是應用可能實際上並不需要它們。


爲避免這點以及移除默認不需要的權限,通常把targetSdkVersion更新爲儘可能高的值。可以在Build.VERSION_CODES 文檔中查看每個發佈的版本中添加了哪些權限。



5. 聲明及執行權限



爲了執行權限,首先必須在AndroidManifest.xml中使用一個或多個<permission>標籤聲明它們。


例如,想要控制誰可以啓動它的活動之一的應用程序可以爲此操作聲明權限,如下所示:


<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中有列出)或者在較爲少見的情況設置爲你自己定義的組。優先使用一個現有的組,因爲這簡化了顯示給用戶的權限界面。

注意,應該爲權限提供標註和描述。它們是string資源,可以在用戶查看一組權限(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

...


5.1 在AndroidManifest.xml中執行權限


通過在AndroidMainfest.xml文件中申請限制訪問系統或應用全部組件的高級權限。這需要的全部就是在期望的組件上包含一個android:permission屬性,並對將要被用來控制訪問它的權限命名。


Activity權限(應用於<activity>標籤)限制誰可以啓動該活動。在Context.startActivity()Activity.startActivityForResult()調用期間檢查該權限;如果調用者沒有必需的權限,那麼該調用會拋出SecurityException異常。


Service權限(應用於<service>標籤)限制誰可以啓動或綁定到該服務。在Context.startService(),Context.stopService() andContext.bindService()調用期間檢查該權限;如果調用者沒有必需的權限,那麼該調用會拋出SecurityException異常。


BroadcastReceiver權限(應用於<receiver>標籤)限制誰可以向該接收器發送廣播。在Context.sendBroadcast()調用返回後檢查該權限,然後系統嘗試向指定的接收器遞送被提交的廣播。作爲結果,權限失敗不會導致把一個異常拋回給調用者;它只是不遞送意圖而已。以同樣的方式,權限可以提供給Context.registerReceiver()來控制誰可以向以編程方式註冊的接收者廣播。相反,可以給Context.sendBroadcast()提供權限來控制哪個BroadcastReceiver對象被允許接收該廣播(見下文)。


ContentProvider權限(應用於<provider>標籤)限制誰可以訪問ContentProvider內的數據。(內容提供者擁有一個重要的附加安全設施被稱爲URI權限,稍後介紹它。)不像其他組件,有兩個單獨的權限屬性可供設置:android:readPermission限制誰可以從提供者讀取數據,以及android:writePermission限制誰可以向其寫入數據。注意,如果提供者用讀和寫權限做了保護,僅持有寫權限不意味着可以從提供者讀取數據。當第一次檢索提供者時檢查這兩個權限(如果你沒有兩個權限中的任何一個,將會拋出SecurityException異常),然後在提供者上執行操作。


ContentResolver.query()要求持有的讀權限;


ContentResolver.insert(),ContentResolver.update(),ContentResolver.delete()要求持有寫權限。在所以這些情況下,不持有必要權限將會導致從調用中拋出異常。


5.2 發送廣播時執行權限


權限除了限制誰可以給一個註冊的BroadcastReceiver對象發送意圖以外(如上所述),你還可以在發送廣播時指定一個必要的權限。通過調用Context.sendBroadcast()攜帶一個權限字符串,來要求接受者應用必須持有那個權限以接收你的廣播。


注意,接收者和廣播者它們兩個可以要求權限。當這種情況發生時,兩個權限檢查必須都通過的意圖將被遞送給關聯目標。


5.3 其他權限執行


任意細化的權限可以在任何對服務的調用時被執行。這是通過Context.checkCallingPermission()方法完成的。用一個期望的權限字符串調用它,然後將返回一個表明那個權限是否被授予給當前調用進程的整數值。注意,這僅在你正在執行一個來自另一進程的調用時使用,一般是通過服務發佈的一個IDL接口或者是以以些其他指定另一進程的方式。


有很多其他有的方法來檢查權限。如果有另一進程的進程pid,可以使用Context方法Context.checkPermission(String, int, int)來對該pid檢查權限。如果有另一應用的包名,可以直接使用PackageManager方法PackageManager.checkPermission(String, String)來查看特定包是否被授予了指定的權限。



6. URI權限



當用於內容提供器時,目前系統所描述的標準權限往往是不足夠的。內容提供器可能打算用讀和寫權限來保護其自己,而它的直接客戶端還需要把指定的URL傳遞給其他應用以便讓它們對其操作。典型的例子就是郵件應用中的附件。訪問郵件應用應該由權限保護,因爲這是用戶的敏感數據。然而,如果把一個圖片附件的URI給了圖片查看器,那個查看器將不會有權限打開該附件,因爲它沒有理由持有訪問全部郵件的權限。


此問題的解決辦法是每個URI權限:當啓動或把結果返回給活動時,調用者可以設置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或Intent.FLAG_GRANT_WRITE_URI_PERMISSION。這賦予了接收活動訪問Intent內指定的數據URI的權限,不管它是否有權限訪問與Intent對應的內容提供器內的數據。


此機制允許普通的用戶交互功能風格來驅動點對點的細粒度權限的授予。這可以是一個關鍵的設施爲把由應用所需要的權限減少至僅與應用行爲有直接關係的那些。


然而,細粒度URI權限的授予需要一些持有那些URIs的內容提供器的配合,強烈推薦內容提供器實現這設施,並通過android:grantUriPermissions屬性或<grant-uri-permissions>標籤聲明它們支持該設施。


更多信息可查看Context.grantUriPermission(),Context.revokeUriPermission(),Context.checkUriPermission()方法。




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