第6章 權限(Permissions)
本文檔介紹了應用程序開發人員如何使用由Android提供的安全功能。在Android 開放源代碼項目AOSP(Android Open Source Project)中提供了更普通的Android安全性概述。Android是一種分權限的操作系統,在Android上運行的每個應用程序都具有各自獨立的系統標識(Linux用戶ID和組ID)。系統各部分有不同並明顯的標識。因此,Linux上運行的各個應用程序相互獨立且與系統無關。
Android的“permissions”機制通俗來說就是你程序就算實現了那個功能,如果沒申請權限的話,那個功能一樣運行部了。並且每個應用程序的權限都是獨立的。
6.1 安全性框架
Android安全體系架構設計的核心是在默認情況下不允許任何一個程序可以執行對其他程序、操作系統或者用戶有害的操作,包括讀寫用戶的隱私數據(例如聯系人或者電子郵件),讀寫其他程序的文件,進行網絡訪問或者喚醒設備等等。由於Android系統讓每個應用程序運行在獨立的沙箱(sandboxes)中,應用程序必須通過聲明所需要的權限來明確的分配資源和數據。Android沒有使用不利於安全的動態授權機制。應用程序靜態的聲明他們所需要的權限,在程序安裝時Android系統會提示用戶是否同意它們獲取這些權限。不同意的話就不要安裝,安裝了即同意。Dalvik虛擬機不是一個安全的邊界,任何的應用程序都能夠運行本地代碼(參照Android NDK)。所有類型的應用程序——java、native和兩者混合的——均用相同方式和相同程度的安全等級在沙盒中運行。
6.2 應用程序數字簽名
所有的Android應用程序(apk文件)都必須讓某個開發人員持有私鑰、用於識別應用程序作者的證書進行簽名。該證書要求很寬松,並不需要由專門的證書頒發機構進行簽名,Android應用程序可以使用自簽名的證書。Android證書的目的是區分應用程序的作者,可以允許操作系統授予或者拒絕應用程序使用簽名級別的權限和操作系統授予或者拒絕應用程序請求和其他應用程序相同的Linux身份。關於這一點,在調試階段的時候,如果你有在兩台電腦分別編譯程序,生成2個相同APK后,裝入手機的話,后面那個會提示“安裝未完成”或非法之類。這是由於你兩台電腦的調式模式下的簽名不一樣,在正式發布的時候,使用Eclipise可以給程序正式簽名,在不同的電腦上發布只要使用相同的正式簽名即可。當然調試階段不用這么麻煩。
6.3 用戶ID和文件訪問
在安裝的時候,Android會給每個程序分配一個不同的Linux用戶IU(UID)。軟件在設備上的生命周期中這個身份標識保持不變。在不同的設備上,相同的軟件可能會有一個不同的UID;重要的是在給定的設備上不同的包是不同的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標記允許其他應用程序來讀/寫文件。設置這些全局的讀寫權限后,該文件仍然由創建改文件的應用程序所擁有,但任何其他應用程序可以看到讀寫它。
6.4 使用權限
一個Android應用程序沒有任何權限,這意味着它不能做任何會對用戶體驗或設備上的任何數據造成不利影響的操作。要利用設備的保護功能,必須在AndroidManifest.xml文件中的一個或多個<uses-permission>標簽上聲明應用程序所需要的權限。例如,一個監控接收短信的應用程序需要聲明如下權限,如代碼清單6-1所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.app.myapp" > <uses-permission android:name="android.permission.RECEIVE_SMS" /> ... </manifest>
代碼清單 6-1
在安裝應用程序時,安裝者要基於應用程序的簽名在交互時對應用程序所需的權限進行授權。應用程序運行時不會進行權限檢查:它要么在安裝后被給予一個特殊的權限,並且可以使用它期望的權限,要么就不被授予權限,任何使用這些權限的操作都會在沒有用戶提示的情況下自動失敗。通常如果請求權限失敗應用程序會拋出一個SecurityException異常,但是也有特例。例如,sendBroadcast(Intent)函數在所有數據被投遞到接收者時檢查權限,當函數返回后,不會對數據的權限進行檢查,也不能接收到任何權限異常。約大多數情況下,權限異常會記錄在日志中。所有Android系統提供的權限可以在Manifest.permission中找到。任何應用程序也可以定義並執行其自己的權限,
6.5 聲明和實施權限
為了執行你自己的權限,你必須首先在你的AndroidManifest.xml中使用一個或多個<permission>標簽聲明它們。例如,一個應用程序想要控制誰能夠啟動它的一個Activity,可以這樣聲明,如代碼清單6-2所示:
<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>
代碼清單 6-2
<protectionLevel>屬性是必選的,它告訴系統當其它應用程序需要該權限時怎樣通知用戶或者誰可以使用該權限。<permissionGroup>屬性是可選的,用於幫助系統顯示權限給用戶。通常會將它設置為在android.Manifest.permission_group中的一個標准的系統組,在極少數情況下也可以由自己進行定義。最好是優先使用現有的組,便於簡化權限UI展示給用戶。請注意,這兩個標簽和描述應提供許可。用戶在查看的權限列表(android:label)或單個權限( android:description)的細節時,這些內容會被顯示。標簽應該簡潔的介紹權限保護的關鍵功能。用幾個簡單的句子描述擁有該權限可以做什么。慣例是用兩個句子,第一句描述權限,第二句警告用戶當授權該權限后會發生什么。
你也可以通過shell命令 adb shell pm list permissions來查看現在系統上的權限定義。特別地,-s選項可以用簡單的表格形式來顯示。如代碼清單6-3所示:
$ 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
...
代碼清單 6-3
1. 在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中的數據。當你第一次檢索Content Provider和當完成相關操作時會進行權限檢查。(假如沒有任何權限則會拋出SecurityException異常。)使用ContentResolver.query()需要持有讀權限;使用ContentResolver.insert(),ContentResolver.update(),ContentResolver.delete()需要寫權限。在所有這些情況下,沒有所需的權限將會導致拋出SecurityException異常。
2. 發送廣播執行權限
除了之前說過的權限(用於限制誰可以發送廣播給相應的BroadcastReceiver),還可以在發送廣播的時候指定一個權限。在調用Context.sendBroadcast()的時候使用一個權限字符串(permission string),你就可以要求接收的宿主程序必須有相應的權限。值得注意的是接收者和廣播都可以要求權限。當這種情況發生時,這兩種權限檢查都需要通過后才會將相應的intent發送給相關的目標。
3. 執行其他權限
在調用service的過程中可以設置更加細化的權限。這是通過Context.checkCallingPermission()方法來完成的。調用的時候使用一個想得到的權限字符串(permission string),返回給調用方一個整數判斷是否具有相關權限。需要注意的是這種情況只能發生在來自另一個進程的調用,通常是一個service發布的IDL接口或者是其他方式提供給其他的進程。Android提供了很多其他有效的方法用於檢查權限。如果有另一個進程的pid,可以通過Context.checkPermission(String, int, int)去檢查該進程的權限設置。如果有另一個應用程序的包名,可以直接用PackageManager.checkPermission(String, String)來確定該包是否已經擁有了相應的權限。
6.6 URI權限
到目前為止我們討論的標准的權限系統對於content provider來說是不夠的。一個Content Provider可能想保護它的讀寫權限,而同時與它對應的客戶端也需要將特定的URI傳遞給其它應用程序以便對該URI進行操作。一個典型的例子是郵件應用程序的附件。訪問郵件需要使用permission來保護,因為這些是敏感的用戶數據。然而,如果有一個指向圖片附件的URI需要傳遞給圖片瀏覽器,那個圖片瀏覽器是不會有訪問附件的權利的,因為它不可能擁有所有的郵件的訪問權限。針對這個問題的解決方案就是per-URI 權限:當啟動一個activity或者給一個activity返回結果的時候,調用方可以設置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或 Intent.FLAG_GRANT_WRITE_URI_PERMISSION。這授予接收activity訪問該Intent指定的URI的權限,而不管它是否有權限進入該Intent對應的content provider。這種機制允許一個通常的(capability-style)模型,以讓用戶交互(如打開一個附件, 從列表中選擇一個聯系人)並細化的權限。這是實現減少應用程序所需要的權限而只留下和程序行為直接相關的權限時很關鍵的一步。強烈建議在content provider中實現這種功能,並通過android:grantUriPermissions或者<grant-uri-permissions>標簽來聲明支持。
FAQ群213821767
