Android系統安全與權限

安全與權限

Android是一個權限分離的操作系統,每個應用以唯一的身份標識(Linux用戶ID和組ID)運行。系統的不同部分也分成不同的身份。因而Linux把應用之間以及應用與系統之間相互隔離起來。

附加細粒度的安全功能是通過一個“許可”的機制,限定特定的進程能夠執行指定的操作以及給予對每一個資源點對點的訪問的URI許可。

安全體系結構

Android安全體系結構設計中心是沒有任何一個應用程序在默認情況下可以執行對其他應用程序、操作系統或者用戶有害的操作。其中包括讀寫用戶的私有數據(例如聯繫人或者電子郵件),讀寫其他應用程序的文件,進行網絡訪問或者喚醒設備,等等。

       由於內核讓每個應用程序運行在獨立的沙盒中,應用程序必須明確的分配資源和數據。他們通過聲明他們所需要的但是沙盒沒有提供的權限來做這些。應用程序靜態的聲明他們所需要的權限,android系統在程序安裝時提示用戶同意它們獲取這些權限。Android沒有準許動態權限機制,因爲它會使用戶體驗複雜對安全不利。

       內核獨自地爲彼此的沙盒應用負責。特別Dalvik虛擬機不是一個安全的邊界,而且任何的應用程序都能夠運行本地代碼(查看Android NDK)。所有類型的應用程序——java、native和混合的——均用相同的方式置於砂箱中並且有着相同的安全等級。

應用程序簽名

所有的Android應用程序(apk文件)必須使用一個開發人員掌握私鑰的證書進行簽名。證書用於識別應用程序的作者。該證書並不需要由證書頒發機構進行簽名:它是非常寬鬆的,典型的Android應用程序使用自簽名的證書。Android證書的目的是區分應用程序的作者。這樣就可以允許操作系統授予或者拒絕應用程序使用簽名級別的權限和操作系統授予或者拒絕應用程序請求和其他應用程序相同的Linux身份。

用戶ID和文件訪問

在安裝的時候,Android給予每個程序包不同的Linux用戶ID。軟件包在設備上的生命週期中身份標識保持恆定不變。在不同的設備上,相同的軟件包可能會有一個不同的UID;重要的是每個包都有不同的身份標識在給定的設備上。

       因爲安全在進程級別上實現,兩個軟件包的代碼不能夠正常的運行在同一個進程中,因爲他們需要以不同的Linux用戶運行。可以使用每個程序包的AndroidManifest.xml中的manifest標籤中的shareUserId屬性將它們分配相同的用戶ID。這樣做是爲了安全,把兩個應用程序包看作同一個應用程序,擁有同樣的用戶ID和文件權限。既然爲了保持安全,爲了保持安全,只有具有相同簽名(並請求同樣的sharedUserId)的應用程序纔會分配通用的用戶ID。

       任何由應用程序存儲的數據將被賦予應用程序的用戶ID,正常情況不能被其它應用程序訪問。當使用getSharedPreferences(String, int), openFileOutput(String, int), 或 openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)創建一個新的文件時,你可以使用MODE_WORLD_READABLE或MODE_WORLD_WRITEABLE標記允許任何其他應用程序來讀/寫入文件。當設置這些標記,該文件仍然爲創建文件的應用程序所擁有,但是它全局的讀寫權限已經被設置,所以任何其他應用程序可以看到它。

使用權限

一個Android應用程序沒有任何權限,這意味着它不能做任何會對用戶體驗或設備上的任何數據造成不利影響的操作。利用該設備的保護功能,必須在AndroidManifest.xmlone文件中的一個或多個<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>

       在應用程序安裝時,應用程序所需的權限被安裝者基於簽名的應用程序所聲明的權限交互的進行授權。應用程序運行時不會進行權限檢查:它要麼在安裝後被給予一個特殊的權限,並且可以使用它期望的權限,要麼就不被授予權限,並且任何試圖使用這些權限將會失敗並沒有用戶提示。

       通常一個請求權限的失敗將導致應用程序拋出一個SecurityException異常。但是並不能夠保證一定會發生。例如,sendBroadcast(Intent)方法在所有數據被投遞到接收者,當方法返回後纔會進行對於數據的權限檢查,所以假如權限異常也不能接收到任何權限異常。幾乎所有情況下,權限異常將會記錄在日誌中。

       將在所有Android系統提供的權限可以在Manifest.permission中找到。任何應用程序也可以定義並執行其自己的權限,所以這不是一個對於所有可能的權限的全面的清單。

任何應用程序也可以定義並執行其自己的權限:

l  當調用系統調用時,阻止應用程序執行特定的功能。

l  當啓動一個Activity時,阻止應用程序啓動其他應用程序的Activity。

l  在發送或接受廣播時,控制誰可以接受你的廣播或者誰可以向你發送廣播。

l  誰可以訪問或操作一個特定的內容提供者。

l  綁定或者啓動一項服務。

聲明和實施許可

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

例如,一個應用程序想要控制誰能夠開始它的一個Activity,可以聲明此操作的權限如下:

<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中)或者在更多罕見的情況下由自己進行定義。它優先使用現有的組,用於簡化權限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來查看現在系統上的權限定義。特別地,-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

...

在AndroidManifest.xml中實施許可

用於限制進入系統或應用程序的組件的高級別許可可以在AndroidManifest.xml中實現。所有這些都可以通過在相應的組件中包含 android:permission 屬性,命名該許可以使其被用以控制進入的權限。

Activity許可(應用於<activity>標籤)用於限制誰纔可以啓動相關的Activity。該權限在Context.startActivity()和Activity.startActivityForResult()期間被檢查;如果調用方不具有這個必需的權限,那麼將會從此次調用中拋出SecurityException。

Service許可(應用於<service>標籤)用於限制誰纔可以啓動或綁定該service。在Context.startService() , Context.stopService()和Context.bindService()調用的時候會進行權限檢查。如果調用方沒有所需的權限,則會拋出一個SecurityException。

BroadcastReceiver許可(應用於<receiver>標籤)用於限制誰纔可以向相關的接收器發送廣播。權限檢查會在Context.sendBroadcast() 返回時進行,由系統去發送已經提交的廣播給相應的Receiver。最終,一個permission failure不會再返回給調用方一個異常;它只是不會去實現該Intent而已。同樣地,Context.registerReceiver()也可以有自己permission用於限制誰纔可以向一個在程序中註冊的receiver發送廣播。另一種方式是,一個permission也可以提供給Context.sendBroadcast() 用以限制哪一個BroadcastReceiver纔可以接收該廣播。(見下文)

ContentProvider許可(應用於<provider>標籤)用於限制誰纔可以訪問ContentProvider提供的數據。(Content providers有一套額外的安全機制叫做URI permissions,這些在稍後討論)不像其他組件,它有兩個單獨的權限屬性,你可以設置:android:readPermission由於限制誰才能夠讀,android:writePermission用於限制誰才能夠寫。需要注意的是如果provider同時被讀寫許可,如果這時只有寫許可並不意味着你就可以讀取provider中的數據了。當你第一次檢索內容提供者時,(假如你沒有任何權限時,拋出SecurityException),和當你完成操作時權限被檢查。使用ContentResolver.query()需要持有讀許可;使用ContentResolver.insert(),ContentResolver.update(),ContentResolver.delete()需要寫許可。在所有這些情況下,沒有所需的權限將會導致拋出SecurityException。

發送廣播時實施許可

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

       值得注意的是接收器和廣播都可以要求許可。當這種情況發生時,這兩種permission檢查都需要通過後纔會將相應的intent發送給相關的目的地。

其他權限實施

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

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

URI許可

到目前爲止我們討論的標準的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()方法。


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