聲明:html
本文由Gordon翻譯android
公佈於www.dlvoice.comshell
歡迎轉載,但請保留此聲明api
原文地址:http://developer.android.com/guide/topics/security/permissions.html瀏覽器
Android是一個特權分離的操做系統。執行在其上的應用都有一個特定的系統身份(Linux的用戶ID和組ID)。系統的部分也會被分爲特定的身份,Linux就是經過這個身份來差異各個應用的。安全
更加具體的安全特性是經過「權限」機制來控制一個進程的特定操做可否夠運行。網絡
它經過每一個URI的權限來來決定他們可否夠訪問特定的數據。架構
本文將會介紹開發人員怎樣使用Android提供的安全特性。更基礎的文章「Android Security Overview」可以在Android的開源項目中查看。app
Android安全架構設計的核心理念就是沒有一個應用可以破壞另一個應用。操做系統或者用戶。這包含讀寫用戶的私有數據(比方聯繫人和email)。讀寫另一個應用的文件,進行網絡訪問。保持設備一直醒着或者別的操做。ide
因爲每一個應用都是工做在進程封裝上。因此它必須明白地分享資源和數據。他們可以經過聲明他們需要的權限來實現資源和數據的共享。
應用靜態聲明他們的權限。而後系統在安裝應用的時候請求用戶容許應用得到這些權限。
應用的封裝並不是由編譯應用的技術來決定的,Dalvik虛擬機(VM)並不是一個特殊安全的界限,每一個應用都可以執行本地的代碼(參考Android NDK)。每一種類型的應用——Java,本地以及混合的——他們都是用相同的方式來進行封裝的,並且他們的安全等級也是同樣的。
所有的APK文件都必須進行簽名,而且簽名使用的是包括開發人員私有密鑰的證書。
這個證書指明瞭應用的做者。這個證書並不需要一個證書認證來進行簽名。
一般來講。Android的應用使用一個自簽名的證書就足夠了。
證書的目的就是爲了區分應用的做者。這就使得系統可以推斷應用可否夠訪問簽名級別的權限,以及是否贊成別的應用和這個應用使用相同的Linux身份(ID)。
在安裝的時候,Android給每個包一個固定的Linux用戶ID。同一設備上這個ID將會伴隨這個包一輩子。固然不一樣設備上,同一個應用包可能會有不一樣的用戶ID。
不管如何在特定的設備上每個包都有一個特定的UID。
因爲安全相關的操做都是在進程級進行執行的,不論什麼兩個應用包的代碼不能在同一進程執行,因爲他們需要在不一樣的Linux用戶上進行執行。若你想兩個應用使用相同的用戶ID執行,僅僅要設置AndroidManifest.xml文件的manifest標籤中的sharedUserId屬性相同就能夠。
這樣作了以後,在安全層面來看。這兩個應用將會被以爲是同一個應用,具備相同的用戶ID和文件權限。
注意,僅僅有兩個使用相同簽名的應用(固然SharedUserID也得相同)纔會給予相同的用戶ID。
應用存儲的不論什麼數據都應當指定爲這個應用的用戶ID,並且不能被別的應用包訪問。當使用getSharedPreferences(String, int), openFileOutput(String, int)或者openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)建立文件時,你可以使用MODE_WORLD_READABLE或者MODE_WORLD_WRITEABLE標誌來贊成別的應用對這個文件的讀寫。當這些標誌被設置以後,這個文件仍然是屬於你的應用,但是他的全局讀寫權限將會被設置。從而讓別的應用可以使用它。
一個主要的Android應用默認來講是沒有相關權限的。這也就意味着它不能作不論什麼破壞用戶體驗及設備數據的操做。
爲了保護設備的特性。你必須在AndroidManifest.xml文件裏聲明一個或者多個<uses-permission>標籤。
好比。一個需要監聽SMS信息的應用應當設定例如如下:
在應用安裝的時候,應用所請求的權限(用戶的簽名和聲明時肯定的)會在包安裝的時候讓用戶賦予給應用。應用在執行的時候,就不會再讓用戶進行檢查了,應用要不在安裝的時候被賦予特定的權限。從而可以使用對應的特性,要不就沒有使用相關特性的權限。
一般狀況下。如果權限失敗將會致使一個SecurityException發送給應用。但是並不是在不論什麼地方都會產生這個exception。舉例來講,sendBroadcast(Intent)方法因爲要把數據發送給每一個receiver,在這種方法返回的時候它會檢查他們的權限,但是如果有權限的失敗。你不會收到exception。
固然。差點兒所有的權限失敗的狀況。都會被打印到系統log中。
然而,正常用戶使用的狀況下(比方應用從Google應用商店裏安裝),如果用戶不一樣意應用申請的權限。這個應用就不會被成功安裝。因此。通常來講你沒有必要操心執行時缺乏權限。因爲其實你的應用在安裝的時候就已經得到了它想要的對應權限。
Android系統提供的權限可以參考Manifest.permission文件。不論什麼應用也都有可能定義並支持它本身的權限。因此這個列表也不能包括所有的可能權限。
注意:
隨着時間的推移,平臺可能會添加新的權限要求。因此爲了使用特定的API,你的應用需要請求一些以前不用請求的權限。因爲已經存在的應用可能以爲訪問這些API是直接可用的。Android可以在應用的manifest文件裏直接申請新的權限請求從而避免在新的平臺版本號上破壞原有的應用。
Android做出應用可能需要權限的描寫敘述是基於targetSdkVersion屬性的。假如這個的值小於權限假如的版本號。那麼Android就添加相應的權限。
舉例來講,WRITE_EXTERNAL_STORAGE權限是在API級別4中增長的。它是爲了防止訪問共享的存儲空間。
假如你的targetSdkVersion小於3,那麼在新的Android版本號中會把這個的權限增長到你的應用中。
需要注意的是,假如這樣的狀況在你的應用中發生,即便這些權限在你的應用中可能沒有真正地請求。Google應用商店也會在顯示你的應用的時候請求這些權限。
爲了不這樣的狀況發生,你需要及時把你的targetSdkVersion更新到足夠高的版本號。
你可以參考Build.VERSION_CODES文檔來看每次公佈都增長了哪些權限。
爲了實施你的權限,你需要首先在AndroidManifest.xml文件裏使用<permission>標籤來聲明他們。
好比,一個應用想要控制誰可以啓動它的activity,就應當爲這個操做聲明一個權限,詳細的方法例如如下:
<protectionLevel>屬性是需要的。它是用來告訴系統應用需要這個權限的時候是怎樣來通知用戶的,或者誰是被贊成獲得這個權限的。詳細的描寫敘述可以參考連接的文件。
(譯者注:後期翻譯ok會增長)。
<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>
你可以經過設置應用和shell命令「adb shell pm list permissions」來查看當前系統定義的權限。如果使用設置應用,可以在設置->應用如下進行查看。
選取一個用戶。向下滾動,可以看到應用使用的權限。
對開發人員來講,adb –s的選項將會以用戶看到的形式來顯示相應的權限:
高級別的權限會限制訪問系統的整個組件。應用可以經過AndroidManifest.xml文件得到。所有的這些請求都包括在相關組件的android:permission屬性中,它會命名控制訪問的權限名。
Activity權限(<activity>標籤)限制了誰可以訪問相應的activity。
這個權限是經過Context.startActivity()和Activity.startActivityForResult()進行檢查的。假如調用者沒有這個權限,那麼就會產生一個SecurityException。
Service權限(<service>標籤)限制了誰可以啓動和綁定相應的service。
這個權限是經過Context.startService()。Context.stopService()以及Context.bindService()來檢查的。假如調用者沒有這個權限,相同會產生一個SecurityException。
BroadcastReceiver權限(<receiver>標籤)限制了誰能向相關的receiver發送廣播。這個權限是在Context.sendBroadcast()以後檢查權限的,因爲這個時候系統會把廣播發送給特定的接受者。這樣一來,權限的失敗將不會產生一個exception返回給調用者,他僅僅會不發送intent而已。相同的。權限可以經過Context.registerReceiver()來控制誰可以廣播給一個經過程序註冊的receiver。還有一方面,當調用context.sendBoradcast()的時候也會提供權限來限制哪個BoradcastReceiver對象可以接收這個廣播。
(詳情見如下)
ContentProvider權限(<provider>標籤)限制了誰可以訪問ContentProvider的數據。(Content provider另外一個重要的額外可用的安全機制稱之爲URI權限,這個會在稍後進行介紹)。
和別的組件不一樣,這裏有兩個獨立的權限屬性可以設置:android:readPermission限制了誰可以讀這個provider,android:writePermission限制了誰可以寫它。
注意,provider是由讀寫權限分別保護的,得到些權限不意味着你可以讀。
這個權限是在你第一次檢索provider進行檢查的(假如你沒有權限,一個securityException將會拋出)。固然你在這個provider上進行操做的時候也會檢查對應的權限。
可以使用ContentResolver.query()函數來請求獲得讀的權限,使用ContentResolver.insert()。ContentResolver.update(), ContentResolver.delete()來請求寫的權限。在所有這些狀況中。如果沒有獲得對應權限都會拋出一個SecurityException。
除了上文提到的對於註冊過的BroadcastReceiver發送intent的時候運行權限以外,你還可以在發送廣播的時候請求權限。在調用Context.sendBroadcast()的時候。增長一個權限字符串,你就可以要求這個接收的應用必須有響應的權限才幹接收廣播。
注意,發送者和接收者都可以請求權限。
這樣的狀況下,在發送intent和接收端都需要進行對應的權限檢查。
更細的權限會在調用service時運行。這個是經過Contex.checkCallingPermission()方法來實現的。在調用這種方法的時候傳入一個權限字符串,就會返回一個整形值用來表示當前的進程是否已經得到了相應的權限。
注意,這個僅僅會在別的進程運行一個調用纔會被使用。一般來講是經過一個service的IDL接口或者提供給別的進程的其它方法來實現。
另外一些別的方式來檢查權限。假如你有另外進程的pid,你可以使用Context.checkPermission(String, int, int)來檢查相應的權限。
假如你有別的應用的包名字。你可以使用PackageManager.checkPermission(String, String)來檢查相應的包是否得到相應的權限。
到眼下爲止所描寫敘述的標準的權限系統對content providers的使用來講都不是很是好。
content provider可能想要保護它本身的讀寫權限,尤爲是當它的直接使用者對別的應用操做獲取特定的URI的時候。一個典型的樣例就是郵件應用中的附件。郵件的訪問需要權限進行設置。因爲畢竟用戶的數據是敏感的。然而,假如圖片瀏覽器得到了一個圖片附件的URI。這個圖片瀏覽器將不會有打開圖片的權限,因爲它沒有道理去得到訪問郵件的權限。
這個問題的解決方法就是每一個URI的權限:當啓動一個activity或者返回一個結果給activity的時候,調用者可以設置Intent.FLAG_GRANT_READ_URI_PERMISSION或者Intent.FLAG_GRANT_WRITE_URI_PERMISSION。這個贊成接收activity得到訪問intent中特殊數據URI的權限。而不用管他是否有權限訪問這個Intent提供者的數據權限。
這個機制贊成了一個用戶交互時(打開一個附件,選擇一個聯繫人等)對專用的更細的權限訪問的通用模型。
這將會是下降應用需要權限的一個關鍵所在。應用僅僅需要那些他們特性直接相關的權限就能夠。
然而對細粒度URI權限的獲取,需要這些URI的content provider也要作一些對應的操做。咱們推薦這些content provider實現這個功能,他們僅僅需要經過android:grantUriPermissions屬性或者<grant-uri-permissions>標籤來聲明就能夠。
不少其它的資訊請參考Context.grantUriPermission(),Context.revokeUriPermission()和Context.checkUriPermission()方法。
推薦您繼續閱讀下面內容:
Permission that Imply Feature Requirements
關於怎樣經過請求權限來限制你的應用僅僅在包括相應硬件或者軟件特性的設備上執行。
<uses-permission>
Manifest標籤下的API參考,他聲明瞭應用需要的系統權限。
Manifest.permission
所有系統權限的API參考。
下面內容你可能也感興趣:
關於Android在不一樣類型設備上執行的資訊,他會介紹怎樣針對不一樣設備進行優化你的應用以以及怎樣在不一樣設備上執行你的應用。
Android Security Overview
更具體的關於Android平臺的安全模式。