天天看點

Android開發指南-架構主題-安全和許可

概述:Android作業系統是一個安全便捷的Linux系統,遵循Linux系統機制,允許多程序。為了程序間的資料共享和互動共用,設計“權限”這個名詞,聲明權限代表可使用此權限,未聲明則一般不能使用。共用有系統級别的資料,也有程序間的資料,可以用在Sqlite,可以用在Android四大控件:Activity、Service、BroadCast

Receiver、ContentProvider,支援自定義權限,詳見:

Android自定義權限和使用權限

安全和許可Security and Permissions

    Android是一個多程序系統,每個應用程式(以及系統的部分)運作在它自己的程序裡。大多數程式和系統之間的安全性通過基礎的Linux機制在程序級别進行支援,如配置設定給應用程式的使用者和群組IDs。更多細化的安全特性通過“許可”機制來提供,它實施對一個程序能夠執行的特定操作方面的限制,和對于每個URI的特定資料段的特許通路的授權。

安全架構Security Architecture

    Android安全架構中的一個設計要點是在預設情況下應用程式沒有權限執行對其它應用程式、作業系統或使用者有害的操作。這些操作包括讀/寫使用者的隐私資料(例如聯系方式或e-mail),讀/寫其它應用程式的檔案,執行網絡通路,保持裝置激活,等等。

    應用程式的程序是一個安全的沙箱。它不能幹擾其它應用程式,除非明确聲明它需要額外的基本的沙箱不能提供的功能的許可權。這些許可權請求能夠被不同方式的操作所處理,常見的是基于證書和使用者提示的自動允許或禁止。應用程式的權限請求被聲明為靜态的,這樣後面在安裝時能夠知道它們而且不會被改變。

應用程式簽名Application Signing

    所有的Android應用程式(.apk檔案)必須用證書進行簽名認證,而這個證書的私鑰是由開發者保有的。該證書可以用以識别應用程式的作者。該證書也不需要被認證機構簽名。Android應用程式完全允許而且一般也都是使用自簽名(self-signed)證書。證書是用于在應用程式之間建立信任關系,而不是用于控制程式是否可以安裝。簽名影響安全性的最重要的方式是通過決定誰可以進入基于簽名的許可,以及誰可以共享使用者IDs。

使用者IDs和檔案通路User IDs and File Access

    每一個Android應用程式(.apk檔案)都會在安裝時就配置設定一個獨有的Linux使用者ID,這就為它建立了一個沙盒,使其不能與其他應用程式進行接觸(也不會讓其它應用程式接觸它)。這個使用者ID會在安裝時配置設定給它,并在該裝置上一直保持同一個數值。

    由于安全性限制措施是發生程序級,是以兩個package中的代碼不會運作在同一個程序當中,他們要作為不同的Linux使用者出現。我們可以通過使用AndroidManifest.xml檔案中的manifest标簽中的sharedUserId屬性,來使不同的package共用同一個使用者ID。通過這種方式,這兩個package就會被認為是同一個應用程式,擁有同一個使用者ID(實際不一定),并且擁有同樣的檔案存取權限。注意:為了保持安全,隻有當兩個應用程式被同一個簽名簽署的時候(并且請求了同一個sharedUserId)才會被配置設定同樣的使用者ID.

    所有存儲在應用程式中的資料都會賦予一個屬性-該應用程式的使用者ID,這使得其他package無法通路這些資料。當通過這些方法getSharedPreferences(String, int), openFileOutput(String, int), 或者openOrCreateDatabase(String,

int, SQLiteDatabase.CursorFactory)來建立一個新檔案時,你可以通過使用MODE_WORLD_READABLE與/或MODE_WORLD_WRITEABLE标志位來設定是否允許其他package來通路讀寫這個檔案。當設定這些标志位時,該檔案仍然屬于該應用程式,但是它的全局讀寫權限已經被設定,使得它對于其他任何應用程式都是可見的。

使用許可Using Permissions

    一個基本的Android程式通常是沒有任何許可與之關聯的,這就是說它不能做任何擾亂使用者或破壞資料的勾當。那麼為了使用裝置被保護的特性,我們就必須在AndroidManifest.xml添加一個或多個<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>      

   在應用程式安裝時,該應用程式請求的權限許可是通過package installer來授予的。package installer是通過檢查該應用程式的簽名和/或使用者的交換結果來确定是否給予該程式request的權限。在使用者使用過程中不會去檢查權限,也就是說要麼在安裝的時候就準許該權限,使其按照設計可以使用該權限;要麼就不準許,這樣使用者也就根本無法使用該feature,也不會有任何提示告知使用者嘗試失敗。

    很多時候, 一個許可失敗會導緻一個SecurityException被抛回該應用程式. 但是Android并不保證這種情況會處處發生。例如,當資料被deliver到每一個receiver的時候,sendBroadcast(Intent) 方法會去檢查permissions,在這個方法調用傳回之後,你也不會收到任何exception。幾乎絕大多數情況,一個permission

failure都會列印到log當中。(注意檢視Logcat)

    Android系統定義的權限可以在Manifest.permission類中找到。任何一個程式都可以定義并強制執行自己獨有的permissions,是以Manifest.permission中定義的permissions并不是一個完整的清單(即有肯能有自定義的permissions)。

一個特定的許可可能會在你的程式操作過程中的很多地方都被實施:

  • 當系統有來電的時候,用以阻止程式執行其它功能。
  • 當啟動一個活動(activity)的時候,會阻止應用程式啟動其它應用程式的Acitivity。
  • 在發送和接收廣播的時候,去控制誰可以接收你的廣播或誰可以發送廣播給你。
  • 當進入并操作一個内容提供器(content provider)的時候
  • 當綁定或起動一個服務(service)的時候

聲明和實施許可Declaring and Enforcing Permissions

    為了實施你自己的permissions,你必須首先在AndroidManifest.xml檔案中聲明該permissions.通常我們通過使用一到多個<permission> tag來進行聲明。

 例如,一個應用程式想要控制誰能啟動它的活動,可以為該操作聲明許可如下:

<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>屬性是可選的,隻是用于幫助系統顯示許可permission給使用者(實際是告知系統該許可是屬于哪個許可組permission group的)。你通常會選擇使用标準的system group來設定該屬性,或者更為少見的用你自己定義的group。推薦使用一個已經存在的group,因為這樣UI給使用者顯示許可的時候會更簡單。

需要注意的是标簽(label)和描述(description)都是需要為許可提供的。這些都是字元串資源,當使用者去看許可清單(android:label)或者某個許可的詳細資訊(android:description)時,這些字元串資源就可以顯示給使用者。label應當盡量簡短,之需要告知使用者該許可是在保護什麼功能就行。而description可以用于具體描述擷取該許可的程式可以做哪些事情,實際上讓使用者可以知道如果他們同意程式擷取該權限的話,該程式可以做什麼。我們通常用兩句話來描述許可,第一句描述該許可,第二句警告使用者如果準許該權限會可能有什麼不好的事情發生。下面是一個描述CALL_PHONE

許可的label和description的例子:

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

你可以通過shell指令 adb shell pm list permissions 來檢視目前系統已有的permissions. 特别的,"-s"選項會以一種使用者會看到的基本相同的格式來顯示這些permissions。

 在清單檔案裡實施許可Enforcing Permissions in AndroidManifest.xml

    用于限制進入系統或應用程式的Components的進階别許可可以再AndroidManifest.xml中實作.所有這些都可以通過在相應的component中包含 android:permission 屬性,命名該permission以使其被用以控制進入的權限。

    Activity許可(應用于<activity>标簽)限制了誰才可以啟動相應的活動。permission會在Context.startActivity()和Activity.startActivityForResult()的時候進行檢查。如果caller沒有所需的權限,則會抛出一個SecurityException。

    Service許可(應用于<service>标簽)用于限制誰才可以start或bind該service。在Context.startService() , Context.stopService()和Context.bindService()調用的時候會進行權限檢查。如果caller沒有所需的權限,則會抛出一個SecurityException。

    BroadcastReceiver許可(應用于<receiver>标簽)用于限制誰才可以向該receiver發送廣播。權限檢查會在Context.sendBroadcast() 傳回時進行,由系統去發送已經送出的廣播給相應的Receiver。最終,一個permission failure不會再傳回給Caller一個exception;它隻是不會去deliver該Intent而已。同樣地,Context.registerReceiver()也可以有自己permission用于限制誰才可以向一個在程式中注冊的receiver發送廣播。另一種方式是,一個permission也可以提供給Context.sendBroadcast()

用以限制哪一個BroadcastReceiver才可以接收該廣播。

    ContentProvider許可(應用于<provider>标簽)用于限制誰才可以通路ContentProvider提供的資料。(Content providers有一套而外的安全機制叫做URI permissions,這些在稍後讨論)不同于其它的Components,這裡有兩種不同的permission屬性可以設定:

android:readPermission 用于限制誰可以讀取provider中的資料,而 android:writePermission 用于限制誰才可以向provider中寫入資料。需要注意的是如果provider同時被讀寫許可,如果這時隻有寫許可并不意味着你就可以讀取provider中的資料了。當你第一次擷取provider的時候就要進行權限檢查(如果你沒有任何permission,則會抛出SecurityException), 并且這時你對provider執行了某些操作。當使用ContentResolver.query()時需要讀權限,而當使用ContentResolver.insert(),

ContentResolver.update(), ContentResolver.delete()時需要寫權限。在所有這些情況下,沒有所需的permission将會導緻SecurityException被抛出。

發送廣播時實施許可Enforcing Permissions when Sending Broadcasts

    除了之前說過的Permission(用于限制誰才可以發送廣播給相應的BroadcastReceiver),你還可以在發送廣播的時候指定一個permission。在調用Context.sendBroadcast()的時候使用一個permission string,你就可以要求receiver的宿主程式必須有相應的permission。

    值得注意的是Receiver和broadcaster都可以要求permission。當這種情況發生時,這兩種permission檢查都需要通過後才會将相應的intent發送給相關的目的地。

其他權限實施Other Permission Enforcement

    在調用service的過程中可以設定任意細化的許可。這是通過Context.checkCallingPermission()方法來完成的。呼叫的時候使用一個想得到的permission string,并且當該權限獲批的時候可以傳回給呼叫方一個Integer(沒有獲批也會傳回一個Integer)。需要注意的是這種情況隻能發生在來自另一個程序的呼叫,通常是一個service釋出的IDL接口或者是其他方式提供給其他的程序。

    Android提供了很多其他的方式用于檢查permissions。如果你有另一個程序的pid,你就可以通過Context.checkPermission(String, int, int)去針對那個pid去檢查permission。如果你有另一個應用程式的package name,你可以直接用PackageManager的方法PackageManager.checkPermission(String, String)來确定該package是否已經擁有了相應的權限。

URI許可URI Permissions

    到目前為止我們讨論的标準的permission系統對于内容提供器(content provider)來說是不夠的。一個内容提供器可能想保護它的讀寫權限,而同時與它對應的直屬用戶端也需要将特定的URI傳遞給其它應用程式,以便對該URI進行操作。一個典型的例子是郵件應用程式的附件。通路郵件需要使用permission來保護,因為這些是敏感的使用者資料。然而,如果有一個指向圖檔附件的URI需要傳遞給圖檔浏覽器,那個圖檔浏覽器是不會有通路附件的權利的,因為它不可能擁有所有的郵件的通路權限。

   針對這個問題的解決方案就是per-URI permission: 當啟動一個activity或者給一個activity傳回結果的時候,呼叫方可以設定Intent.FLAG_GRANT_READ_URI_PERMISSION和/或 Intent.FLAG_GRANT_WRITE_URI_PERMISSION。這賦予接收活動(activity)通路該意圖(Intent)指定的URI的權限,而不論它是否有權限進入該意圖對應的内容提供器。

    這種機制允許一個通常的能力-風格(capability-style)模型,以使用者互動(如打開一個附件, 從清單中選擇一個聯系人)來驅動細化的特别授權。這是一個很關鍵的能力,可以減少應用程式所需要的權限,隻留下和程式行為直接相關的權限。

    這些URI permission的擷取需要内容提供器(包含那些URI)的配合。強烈推薦在内容提供器中實作這種能力,并通過android:grantUriPermissions或者<grant-uri-permissions>标簽來聲明支援。

    更多的資訊可以參考Context.grantUriPermission(), Context.revokeUriPermission()和Context.checkUriPermission()方法。

轉載于

http://blog.csdn.net/iefreer/archive/2009/09/10/4537371.aspx

官方文檔原文

http://androidappdocs.appspot.com/guide/topics/security/security.html

繼續閱讀