Android是一個特權分隔的操做系統,每個應用程序運行在不一樣的系統身份中(Linux的user ID和group ID)。系統部分和不一樣的身份被隔離開來。所以,Linux隔離了應用程序(與其它程序隔離,與系統隔離)。html
經過權限(permission)機制提供了附加的安全功能。權限機制強迫限制特定操做,好比操做一個特定進程執行和每一個URI權限容許點對點地訪問特定數據塊。java
更多通用的Android Security Overview被提供在Android開源項目中。android
Android安全體系結構的核心就是——默認狀況下,程序沒有權限執行下面三種操做:對其餘程序不利的操做、系統操做、用戶操做。包括讀寫用戶數據(聯繫人或者郵件之類),讀寫其它程序中的文件,執行網絡操做等等。sql
因爲Android程序運行在一個沙箱中,程序必須顯示地分享資源和數據(聲明權限,注權限是附加的功能並非由沙箱提供的)。程序靜態地聲明它們須要的權限,而且在安裝程序時,Android系統提示用戶贊成權限。Android沒有動態(run—time)贊成權限機制,由於這會使用戶體驗複雜,同時會破壞安全。shell
程序沙箱不依賴於程序編譯技術。特別是Dalvik VM不是一個安全邊界,而且全部的app都能運行native代碼。java、native和hybird,以上全部的這些程序類型都是以一樣的方式被沙箱化的,而且都有一樣的安全等級。瀏覽器
全部的APK必需要被一個證書籤名,證書的私鑰被開發者持有,證書籤名不須要證書權限。在Android中,證書的目的是用來區分開發者程序的,容許系統贊成或拒絕程序訪問簽名級別權限和贊成或拒絕一個程序訪問其它有相同的Linux身份的程序。安全
在安裝的時候,Android會給每一個包一個不一樣的Linux用戶ID。在同個設備的包的聲明週期中,這個ID常量會始終保持。在不一樣的設備中,同一個包可能會有不一樣的UID。不管如何,在一個給定的設備中每一個包都有不一樣的UID。網絡
因爲安全機制的執行發生在進程級別,任何兩個包的代碼通常是不能運行在同一個進程的,它們運行在兩個不一樣的Linux用戶中。可是,能夠在他們包的manifest中使用shareUserId屬性來給他們分配同一個用戶ID。經過這樣作了以後,兩個包被就被看成了一個程序,它們擁有同樣的用戶ID和文件權限。注意,爲了保持安全,這兩個程序必須擁有相同簽名而且請求同一個sharedUserId,不然是不會被給與相同的用戶ID。app
任何的數據被程序存儲,程序被分配一個用戶ID,這樣程序就不能訪問其它的包。經過getSharedPreferences(String, int)
, openFileOutput(String, int)
, or openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)
建立一個新文件時,可使用MODE_WORLD_READABLE
and/or MODE_WORLD_WRITEABLE
標記容許其它程序讀寫文件。設置了這些標記,儘管文件一直是屬於一個程序的,可是這類文件的全局讀寫權限仍是能讓其它程序看的到這些文件。異步
一個基本的Android程序默認是沒有權限的,意味着它不能執行任何事情或者設備上數據,這樣會影響用戶體驗。爲了使用設備被保護的功能,必須在manifest中經過一個或多個
標記來聲明程序須要的權限。好比接受短信:<uses-permission>
1 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 2 package="com.android.app.myapp" > 3 <uses-permission android:name="android.permission.RECEIVE_SMS" /> 4 ... 5 </manifest>
在程序安裝的時候,程序權限的授予是經過package installer進行的。package installer檢查簽名程序聲明的權限,而後與用戶交互是否授予程序權限。運行時,程序的權限檢查工做是不會進行的;安裝時,app既能夠被授予某個權限(能使用指望的功能),也能夠被不授予權限(使用這功能會失敗同時不會提示用戶)。
一般一個權限失敗,會致使一個 SecurityException
拋回給應用程序。可是,Android並不保證這種狀況會到處發生。好比,將數據分發給廣播接收器, sendBroadcast(Intent)
(異步方法,會檢查權限)方法調用返回後,若是有權限失敗,也不會收到一個異常。幾乎絕大多數狀況,一個權限失敗會被打印到系統log當中。
若是app須要的全部權限都沒被用戶授予,那麼app是不能被安裝的。
Android系統定義的權限能夠在Manifest.permission中找到。任何一個程序均可以定義並強制執行本身獨有的權限(<permission>),所以Manifest.permission中定義的權限並非一個完整的列表(即有可能有自定義的權限<permission>)。
在你的程序操做過程當中,一個特定的權限可能會在不少地方都被執行,好比:
爲了執行自定義權限,首先必需要在AndroidManifest.xml文件中聲明該權限.一般咱們經過使用一到多個<permission>標籤來進行聲明。
例如,一個應用程序想要控制誰能啓動它的Activity,能夠爲該操做聲明自定義權限以下:
1 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 2 package="com.me.app.myapp" > 3 <permission android:name="com.me.app.myapp.permission.DEADLY_ACTIVITY" 4 android:label="@string/permlab_deadlyActivity" 5 android:description="@string/permdesc_deadlyActivity" 6 android:permissionGroup="android.permission-group.COST_MONEY" 7 android:protectionLevel="dangerous" /> 8 ... 9 </manifest>
<protectionLevel>屬性是必需的,該屬性告訴系統如何去通知用戶哪些應用程序須要這個許可,或者誰能夠擁有該許可。
<permissionGroup>屬性是可選的,只是用於幫助系統顯示權限給用戶(實際是告知系統該許但是屬於哪一個許可組permission 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>
在系統中,能夠經過設置app(Settings app )和adb shell命令(adb shell pm list permissions
)看到定義的權限。
使用設置app(Settings app ):Settings > Applications,選中一個app進入信息頁面,就能看到app使用的權限狀況。
adb shell命令(adb shell pm list permissions
):"-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 ...
限制進入系統或程序的組件的高級別權限能夠在manifest中實現。在組件中包含一個android:permission屬性,命名該權限用來控制訪問組件。
Activity權限(應用於<activity>標籤),限制誰能夠啓動相應的Activity。這個權限檢查發生在Context.startActivity()和Activity.startActivityForResult()
,若是調用者沒有申請相應權限,會致使SecurityException被拋回給調用者。
Service權限(應用於<service>標籤),限制誰能夠啓動或者綁定相應的Service。這個權限檢查發生在 Context.startService()
、Context.stopService()和 Context.bindService()
,若是調用者沒有申請相應權限,會致使SecurityException被拋回給調用者。
BroadcastReceiver權限(應用於<receiver>標籤),限制誰才能夠給廣播接收器發送廣播。這個權限檢查發生在Context.sendBroadcast()調用返回以後,系統會嘗試發送廣播給接收器。(注:Context.sendBroadcast()是一個異步調用,調用返回是當即發生的。)最終,權限若是失敗了,也不會返回一個異常給調用者,只是沒有發送Intent罷了。一樣的,在動態註冊廣播(代碼中使用Context.registerReceiver()
)中也能夠實現一個權限控制。另外一種方式(具體內容見下面)是,當調用Context.sendBroadcast()時,權限用於限制哪個BroadcastReceiver才能夠接收該廣播。
ContentProvider權限(應用於<provider>標籤),限制誰能訪問ContentProvider的數據。(ContentProvider還有另一套重要的安全機制——URI permissions,之後會談到。)不像其它組件,ContentProvider有兩個單獨的權限屬性:android:readPermission(限制誰能夠從provider中讀取數據)和android:writePermission(限制誰能夠將數據寫入provider)。須要注意的是provider同時被讀寫權限保護的狀況下,若是你這時只有寫權限並不意味着能從provider讀。這個權限檢查發生在第一次檢索provider時,而且這時對provider作了某些操做。使用ContentResolver.query()須要讀權限,使用
ContentResolver.insert()
,ContentResolver.update()
, ContentResolver.delete()
須要寫權限。在全部這些狀況下,沒有所需的權限將會致使SecurityException被拋出。
在發送廣播的時候,指定一個須要的權限。在調用Context.sendBroadcast()的時候使用一個權限字符串,你就能夠要求接收器的宿主程序必須有相應的權限,才能接收你的廣播。
須要注意的是Receiver和broadcaster均可以要求權限。當這種狀況發生時,這兩種permission檢查都須要經過後纔會將相應的intent發送給相關的目標。
在Service裏面,能夠經過Context.checkCallingPermission()
方法來執行任意細化的權限。調用的時候使用一個指望的權限字符串,它會返回一個整數來代表這個權限是否被授予當前調用進程。須要注意的是這種狀況只能發生在來自另外一個進程的呼叫,一般是一個service發佈的IDL接口或者是以其餘方式提供給其餘的進程。
Android提供了其餘幾個有用的方式用於檢查permissions。若是你有另外一個進程的pid,你就能夠經過Context.checkPermission(String, int, int)去針對那個pid去檢查permission。若是你有另外一個應用程序的包名,你能夠直接用PackageManager.checkPermission(String, String)來肯定該包是否已經擁有了相應的權限。
目前爲止,咱們所討論的標準權限系統對ContentProvider來講是不夠的。一個ContentProvider可能想要經過讀寫權限保護本身,然而它的直屬客戶端也須要將特定的URI傳遞給其它程序,以便對該URI進行操做。一個典型的例子是郵件程序的附件。訪問的郵件須要被權限保護,由於這些是敏感的用戶數據。然而,若是一個指向圖片附件的URI傳遞給一個沒有訪問郵件權限的圖片瀏覽器,那麼圖片瀏覽器是沒有權限去打開附件的。
針對這個問題的解決方案是每一個URI權限(per-URI permissions):當啓動一個Activity或者從一個Activity返回結果時,調用者能夠設置Intent.FLAG_GRANT_READ_URI_PERMISSION
and/or Intent.FLAG_GRANT_WRITE_URI_PERMISSION。不論是否有權限經過Intent訪問在ContentProvider中的數據,這都會授予接收的Activity權限去訪問Intent中URI指定的數據。
這種機制使得常見的能力-風格模型(capability-style)得以實現,用戶交互操做(打開附件,從列表中選擇聯繫人等)驅動點對點的授予細化權限。這是一個減小程序權限的關鍵能力,只留下和程序行爲相關的權限。
然而,對於細化URI權限的授予,須要那些持有該URI的ContentProvider配合。強烈建議在ContentProvider中實現這種能力,而且經過android:grantUriPermissions屬性或者
<grant-uri-permissions>
標籤來聲名
ContentProvider支持這一個特性。
更多的信息能夠參考下面幾個方法 Context.grantUriPermission()
, Context.revokeUriPermission()
, 和Context.checkUriPermission()。
關於有些權限請求隱含者硬件或者軟件限制。
<uses-permission>
manifest的一個應用API標籤,用於聲明app請求的系統權限。
全部系統權限的API參考。