理解 Android 上的安全性

yangxi_001發表於2013-11-19

概述

Android 包括一個應用程式框架、幾個應用程式庫和一個基於 Dalvik 虛擬機器的執行時,所有這些都執行在 Linux® 核心之上。通過利用 Linux 核心的優勢,Android 得到了大量作業系統服務,包括程式和記憶體管理、網路堆疊、驅動程式、硬體抽象層以及與本文主題 —— 安全性 —— 相關的服務。

常用縮寫詞

  • ADT:Android 開發工具
  • API:應用程式程式設計介面
  • IDE:整合開發環境
  • JDK:Java 開發包
  • URL:統一資源識別符號
  • XML:可擴充套件標記語言
前提條件

要跟隨本文,需要具備以下技能和工具:

  • 基本瞭解 Java™ 技術和如何使用 Eclipse(或者您喜歡的 IDE)
  • Java Development Kit(需要版本 5 或 6)
  • Eclipse(版本 3.4 或 3.5)
  • Android SDK 和 ADT 外掛

有關下載和設定方面的資訊,請參見本文結尾的 參考資料 部分。

沙箱、程式和許可權

使用者 ID:Linux 與 Android

在 Linux 中,一個使用者 ID 識別一個給定使用者;在 Android 上,一個使用者 ID 識別一個應用程式。應用程式在安裝時被分配使用者 ID,應用程式在裝置上的存續期間內,使用者 ID 保持不變。許可權是關於允許或限制應用程式(而不是使用者)訪問裝置資源。

Android 使用沙箱的概念來實現應用程式之間的分離和許可權,以允許或拒絕一個應用程式訪問裝置的資源,比如說檔案和目錄、網路、感測器和 API。為此,Android 使用一些 Linux 實用工具(比如說程式級別的安全性、與應用程式相關的使用者和組 ID,以及許可權),來實現應用程式被允許執行的操作。

概念上講,沙箱可以表示為 圖 1 所示。


圖 1. 兩個 Android 應用程式,各自在其自己的基本沙箱或程式上
圖:兩個 Android 應用程式,各自在其自己的基本沙箱或程式上(具有不同的使用者 ID) 

Android 應用程式執行在它們自己的 Linux 程式上,並被分配一個惟一的使用者 ID。預設情況下,執行在基本沙箱程式中的應用程式沒有被分配許可權,因而防止了此類應用程式訪問系統或資源。但是 Android 應用程式可以通過應用程式的 manifest 檔案請求許可權。

通過做到以下兩點,Android 應用程式可以允許其他應用程式訪問它們的資源:

  • 宣告適當的 manifest 許可權
  • 與其他受信任的應用程式執行在同一程式中,從而共享對其資料和程式碼的訪問

後者演示在 圖 2 中。


圖 2. 兩個 Android 應用程式,執行在同一程式上
圖:兩個 Android 應用程式,執行在同一程式上(具有相同的數字簽名和相同的 Linux 使用者 ID) 

不同的應用程式可以執行在相同的程式中。對於此方法,首先必須使用相同的私鑰簽署這些應用程式,然後必須使用 manifest 檔案給它們分配相同的 Linux 使用者 ID,這通過用相同的值/名定義 manifest 屬性 android:sharedUserId 來做到。

開發人員用例

圖 3 演示了很多在開發 Android 應用程式時會發現的與安全性相關的用例


圖 3. 編寫 Android 應用程式時出現的安全領域
圖:編寫 Android 應用程式時出現的安全領域 
  • 應用程式或程式碼簽名是這樣一個過程,即生成私有、公共金鑰和公共金鑰證照,簽署和優化應用程式。
  • 許可權是 Android 平臺的一種安全機制,以允許或限制應用程式訪問受限的 API 和資源。預設情況下,Android 應用程式沒有被授予任何許可權,不允許它們訪問裝置上受保護的 API 或資源,從而保證了它們的安全。許可權必須被請求,定義了定製的許可權,檔案和內容提供者就可以受到保護。確保在執行時檢查、執行、授予和撤銷許可權。

接下來,更加詳細地來看一下每個安全領域。

應用程式簽名

所有 Android 應用程式都必須被簽名。應用程式或程式碼簽名是一個這樣的過程,即使用私有金鑰數字地簽署一個給定的應用程式,以便:

  • 識別程式碼的作者
  • 檢測應用程式是否發生了改變
  • 在應用程式之間建立信任

基於這一信任關係,應用程式可以安全地共享程式碼和資料。

使用相同數字簽名簽署的兩個應用程式可以相互授予許可權來訪問基於簽名的 API,如果它們共享使用者 ID,那麼也可以執行在同一程式中,從而允許訪問對方的程式碼和資料。

應用程式簽名首先是生成一個私有、公共金鑰對和一個相關公共金鑰證照,簡稱為公共金鑰證照。

構建 Android 應用程式時可以採用除錯模式釋出模式

  • 使用 Android 構建工具(命令列和 Eclipse ADT)構建的應用程式是用一個除錯私有金鑰自動簽名的;這些應用程式被稱為除錯模式應用程式。除錯模式應用程式用於測試,不能夠釋出。注意,未簽名的或者使用除錯私有金鑰簽名的應用程式不能夠通過 Android Market 釋出。
  • 您準備釋出自己的應用程式時,必須構建一個釋出模式的版本,這意味著用私有金鑰簽署應用程式。

Android 中的程式碼簽名採用一種比其他移動平臺中要簡單得多的方式。在 Android 上,證照可以是自簽名的,這就是說,無需證照授權。這種方法簡化了釋出過程和相關的成本。

接下來,介紹如何從命令列以及通過使用 Eclipse ADT 手動簽署 Android 應用程式。本文中不介紹第三種方法,即使用 Ant。

手動建立私有、公共金鑰和公共金鑰證照

回想一下,除錯模式應用程式是使用除錯金鑰/證照由構建工具自動簽名的。要簽署一個釋出模式的應用程式,首先必須生成私有、公共金鑰對和公共金鑰證照。可以手動地或者通過使用 Eclipse ADT 簽署應用程式。兩種方法中都使用了 Java Developer Kit (JDK) keytool 金鑰和證照管理實用工具。

要手動生成私有、公共金鑰資訊,可以從命令列使用 keytool,如 清單 1 所示。


清單 1. 使用 keytool 生成私有/公共金鑰和證照
				
keytool -genkey -v -alias <alias_name> -keystore <keystore.name> 
-keyalg RSA -keysize 2048 -validity <number of days>

注意:清單 1 假設 JDK 已安裝在您的計算機上,並且 JAVA_HOME 路徑被正確定義為指向您的 JDK 目錄(參見 參考資料,獲得下載和設定資訊)。

在 清單 1 中,-genkey 表示一個公共、私有金鑰對項,以及一個 X.509 v1 自簽署的單個元素證照鏈,其中包含生成的公共金鑰。-v表示冗長模式。-alias 是用於 keystore 項的別名,keystore 儲存生成的私有金鑰和證照。-keystore 表示使用的金鑰倉庫的名稱。-keyalg 是用來生成金鑰對的演算法。-keysize 是生成的金鑰大小,其中預設大小是 1024,但是推薦大小是 2048。-validity 是有效天數;推薦採用大於 1000 的值。

注意:生成金鑰之後,一定要保證金鑰的安全。不要共享私有金鑰,也不要在命令列或指令碼中指定金鑰;注意,keytool 和 jarsigner 會提示輸入密碼。關於這一技巧和其他技巧,請參考 Android Developers 網站的 “Securing Your Private Key”(參見 參考資料 中的連結)。

Keytool 提示您輸入名和姓、公司、城市、州、國家,從這些資訊生成一個 X.500 Distinguished Name(更多資訊請參見 參考資料),還要輸入保護私有金鑰和金鑰倉庫本身的密碼。

對於有效期,請確保使用超出應用程式本身和相關應用程式預期生命期的時期。如果您是在 Android Market 上釋出應用程式,那麼有效期必須晚於 2033 年 10 月 22 日結束;否則不能上載。此外,擁有長壽命的證照讓升級應用程式更為容易。幸運的是,Android Market 強制採用長壽命的證照,以幫助您避免此類問題。

手動簽署應用程式

接下來,使用 jarsigner 工具(它是 JDK 的一部分)簽署未簽名的應用程式:

jarsigner -verbose -keystore <keystore.name> <my_application.apk> <alias_name>

在上述程式碼中,-verbose 表示冗長模式,-keystore 表示使用的金鑰倉庫的名稱。接下來是應用程式的名稱 (.apk),最後是用於私有金鑰的別名。

Jarsigner 提示您輸入使用金鑰倉庫和私有金鑰時的密碼。

應用程式可以使用不同的金鑰進行多次簽名,用相同私有金鑰簽名的應用程式之間可以建立一種信任關係,並且可以執行在同一程式中,共享程式碼和資料。

手動優化應用程式

簽署過程的最後一步是優化應用程式,以便資料邊界與檔案的開始是記憶體對齊的,這種技術有助於改善執行時效能和記憶體利用率。要簽署應用程式,可以使用 zipalign

zipalign -v 4 your_project_name-unaligned.apk your_project_name.apk

在前面的程式碼中,-v 表示冗長輸出。數字 4 表示使用四位元組對齊(總是使用四位元組)。下一個引數是輸入已簽署應用程式的檔名 (.apk),它必須用您的私有金鑰簽署。最後一個引數是輸出檔名;如果覆蓋現有應用程式,則新增一個 -f

手動驗證應用程式已經簽署

要驗證應用程式已經簽署,可以使用 Jarsigner,這次傳遞 -verify 標誌:

jarsigner -verify -verbose -certs my_application.apk

在前面的程式碼中,-verify 表示驗證應用程式;-verbose 表示冗長模式;-certs 表示展示建立金鑰的 CN 欄位,最後一個引數是要驗證的 Android 應用程式包的名稱。

注意:如果 CN 讀入 "Android Debug",那麼意味著應用程式是用除錯金鑰簽署的,這表明不能釋出;如果您計劃在 Android Market 上釋出您的應用程式,一定要記得使用私有金鑰。

剛才學習瞭如何手動建立私有、公共金鑰,以及簽署和優化應用程式。接下來,瞭解如何使用 Eclipse ADT 自動建立私有、公共金鑰,以及簽署和優化應用程式。

使用 Eclipse ADT 建立金鑰和證照,以及簽署和優化應用程式

要使用 Eclipse ADT 生成金鑰,必須匯出應用程式。有兩種方法從 Eclipse 匯出應用程式:

  • 匯出您必須手動簽署的應用程式的未簽署 版本
  • 匯出應用程式的已簽署 版本,其中所有步驟都由 ADT 為您代勞

匯出未簽署的應用程式

您可以匯出您必須手動簽署的應用程式的未簽署版本。就是說,您需要手動執行 keytool(如前所述,是為了生成金鑰)和 Jarsigner(為了簽署應用程式),並使用 zipalign 工具優化應用程式,跟前面解釋的那樣。

要使用 ADT 匯出應用程式的未簽署版本,可以右鍵單擊專案並選擇 Android Tools>Export Unsigned Application Package(參見圖 4)。


圖 4. 匯出未簽署的應用程式
匯出未簽署的應用程式的螢幕截圖 

選中之後,ADT 提示您選擇將未簽署應用程式匯出到的目錄。記住,一旦應用程式被匯出,您就必須手動簽署和優化應用程式,跟前面介紹的那樣。

匯出已簽署的應用程式

利用 Eclipse ADT,您可以匯出應用程式的已簽署版本。使用這種方法,ADT 提示您輸入以下內容:

  • 使用現有 KeyStore 或者建立新的受保護 KeyStore 所需的資訊
  • 建立受保護私有金鑰所需的資訊
  • 生成公共金鑰證照所需的資訊

要匯出已簽署的應用程式,可以右鍵單擊專案,但是這一次選擇選單項 Android Tools->Export Signed Application Package,如圖 5 所示。


圖 5. 匯出已簽署的應用程式
匯出已簽署的應用程式的螢幕截圖 

此時,Export Wizard 執行,如 圖 6 所示。


圖 6. Export Wizard
Export Wizard 的螢幕截圖 

在 圖 7 中,選擇一個現有的金鑰倉庫(或者建立一個新的)和證照。


圖 7. Export Wizard:金鑰倉庫選擇
Export Wizard:金鑰倉庫選擇的螢幕截圖 

在 圖 8 中,輸入資訊以建立私有金鑰和數字證照。


圖 8. Export Wizard:建立私有金鑰和數字證照
Export Wizard:建立私有金鑰和數字證照的螢幕截圖 

在 圖 9 中,輸入目標檔案的路徑和名稱,並驗證有效期間。


圖 9. 輸入目標檔案的路徑和名稱
輸入目標檔案的路徑和名稱的螢幕截圖 

完成時,您就有了一個釋出模式的已簽署和已優化的應用程式,您可以釋出它。

另外,您也可以使用 Android Manifest 工具呼叫 Export Wizard,如 圖 10 所示。


圖 10. 使用 Android Manifest 工具呼叫 Export Wizard
使用 Android Manifest 工具呼叫 Export Wizard 的螢幕截圖 

應用程式簽署之後,下一步由您在 manifest 中定義應用程式需要的許可權。接下來將描述這一過程。

注意,Android Developer 網站有非常好的關於應用程式簽署的文件,當有 Android 平臺的新版本可用時,這些文件都會更新(參見參考資料,瞭解更多資訊)。

使用許可權

許可權是一種 Android 平臺安全機制,旨在允許或限制應用程式訪問受限的 API 和資源。預設情況下,Android 應用程式沒有被授予許可權,這通過不允許它們訪問裝置上的受保護 API 或資源,確保了它們的安全。許可權在安裝期間通過 manifest 檔案由應用程式請求,由使用者授予或不授予。

Android 定義長長的一系列 manifest 許可權,以保護系統或其他應用程式的各個方面。要請求許可權,可以在 manifest 檔案中宣告一個<user-permission> 屬性:

<uses-permission android:name="string" />

其中 android:name 指定許可權的名稱。

要得到所有 Android 定義的 manifest 許可權的列表,請參見 Manifest.permisson 頁面。清單 2 是一個 manifest 檔案的例子,它請求使用 Internet 的許可權和寫到外部儲存器的許可權:


清單 2. 宣告(請求)許可權
				
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      android:versionCode="1"
      android:versionName="1.0"
      package="com.cenriqueortiz.tutorials.datastore" 
      android:installLocation="auto">

    <application 
        :
        :
        :
    </application>

    <uses-permission 
        android:name="android.permission.INTERNET"/>
    <uses-permission 
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>

應用程式可以定義它們自己的定製許可權,以保護應用程式資源。其他應用程式想要訪問一個應用程式的受保護資源,就必須通過它們自己的 manifest 檔案請求適當的許可權。清單 3 展示了一個如何定義許可權的例子。


清單 3. 宣告定製許可權
				
<permission
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:name="com.cenriqueortiz.android.ACCESS_FRIENDS_LIST"
        android:description="@string/permission_description"
        android:label="@string/permission_label"
        android:protectionLevel="normal"
    >
</permission>

在 清單 3 中,通過指定最少的屬性,即 namedescriptionlabel 和 protectionLevel,定義了一個定製許可權。也可以定義其他屬性,但是這裡沒做介紹。

特別有趣的是 android:protectionLevel 屬性,它表示系統向一個請求許可權的應用程式授予(或不授予)給定的許可權時應該遵循的方法。保護級別有普通 和危險。前者自動授予許可權(儘管使用者在安裝之前總是可以重審),基於簽名授予許可權(就是說,如果請求許可權的應用程式是用同一證照籤署的);後者表示許可權給予私有資料的訪問權,或者具有另一個潛在的負面影響。有關 <permission>manifest 屬性的更多資訊,請參見 <permission> 頁面(參見 參考資料)。

應用程式可以限制對應用程式及其使用的系統元件(比如 Activity、Service、Content Provider 和 Broadcast Receiver)的訪問。通過像 清單 4 中那樣定義 android:permission 屬性,很容易實現這種限制。這種級別的保護讓應用程式允許或限制其他應用程式訪問系統資源。


清單 4. 定義一個活動的許可權
				
<activity 
    android:name=".FriendsListActivity" 
    android:label="Friends List">
    android:permission="com.cenriqueortiz.android.ACCESS_FRIENDS_LIST"
    <intent-filter>
        :
        :
    </intent-filter>        
</activity>  

內容提供者和檔案許可權

內容提供者暴露一個公共 URI,用於惟一地識別它們的資料(參見 參考資料)。要保護此內容提供者,當開始時或者從活動返回結果時,呼叫者可以設定 Intent.FLAG_GRANT_READ_URI_PERMISSION 和 Intent.FLAG_GRANT_WRITE_URI_PERMISSION,以便授予接收活動許可權,以訪問特定的資料 URI。

應用程式檔案預設是受保護的。檔案基於使用者 ID 受保護,因而只對所有者應用程式是可訪問的(此應用程式具有相同的使用者 ID)。正如前面介紹的,共享相同使用者 ID(並使用相同數字證照籤署)的應用程式執行在相同程式上,因而共享對它們的應用程式的訪問。

應用程式可以允許其他應用程式或程式訪問它們的檔案。這種允許是通過指定適當的 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE操作模式(以便允許對檔案的讀或寫訪問)或 MODE_PRIVATE(以便以私有模式開啟檔案)而做到的。您可以在建立或開啟檔案時利用以下方法指定操作模式:

  • getSharedPreferences(filename, operatingMode)
  • openFileOutput(filename, operatingMode)
  • openOrCreateDatabase(filename, operatingMode, SQLiteDatabase.CursorFactory)

執行時 Permission API

Android 提供各種 API 來在執行時檢查、執行、授予和撤銷許可權。這些 API 是 android.content.Context 類的一部分,這個類提供有關應用程式環境的全域性資訊。例如,假設您想要優雅地處理許可權,您可以確定您的應用程式是否被授予了訪問 Internet 的許可權(參見確定 5)。


清單 5. 使用執行時 Permission API 在執行時檢查許可權
				
if (context.checkCallingOrSelfPermission(Manifest.permission.INTERNET)
        != PackageManager.PERMISSION_GRANTED) {
            // The Application requires permission to access the  
            // Internet");
} else {
    // OK to access the Internet
}

要了解其他在執行時檢查、執行、授予和撤銷許可權的許可權 API,請參考上下文類。

結束語

本文介紹了 Android 平臺上的安全性,包括沙箱、應用程式簽名、應用程式許可權,以及檔案和內容提供者許可權。閱讀完這篇介紹性文章之後,您將能夠使用 Eclipse 手動建立數字證照,請求應用程式許可權,以及允許或不允許應用程式訪問檔案和內容提供者。此外,您還簡要了解了許可權執行時 API,這些 API 允許您在執行時檢查、執行、授予和撤銷許可權。


參考資料

學習

  • What is Android?:在 Android Developers 網站閱讀此概述性文章。

  • 用 Eclipse 開發 Android 應用程式(Frank Ableson,developerWorks,2008 年 2 月):在這篇教程中全面瞭解開發 Android 應用程式最容易的方法是使用 Eclipse。

  • Android 開發簡介(Frank Ableson,developerWorks,2009 年 5 月):獲得對 Android 平臺的介紹,並學習如何編寫基本的 Android 應用程式。

  • X.509 Certificates:閱讀 X.509 標準,它定義了哪些資訊可以進入證照,並描述瞭如何將之寫下來(資料格式)。

  • 擴充套件的許可權:如果您的應用程式需要訪問使用者 profile 的其他部分(可能是私有的),或者需要代表使用者將內容釋出到 Facebook,那麼就為它請求擴充套件的許可權。

  • Securing Your Private Key:在 Android Developers 網站上學習如何維護私有金鑰的安全性。

  • Signing Your Applications:從 Android Developers 網站上學習,如何在為移動裝置使用者釋出之前簽署 Android 應用程式。

  • Manifest.permission 頁面:要檢視所有 Android 定義的 Manifest 許可權列表,請訪問 Android Developers 網站。

  • Content Providers:學習如何儲存和檢索資料,以及使之對所有應用程式可訪問。

  • Context 類:利用這個到 Android Developers 網站上描述的全域性資訊的介面,允許訪問特定於應用程式的資源和類。

  • 使用 Android 實現聯網(Frank Ableson,developerWorks,2009 年 6 月):探究 Android 的聯網功能。

  • 在 Android 上使用 XML(Michael Galpin,developerWorks,2009 年 6 月):瞭解關於在 Android 上處理 XML 的不同選項以及如何使用它們來構建自己的 Android 應用程式。

  • Under the Hood of Native Web Apps for Android:瞭解 Android 中的混合應用程式。

  • Unlocking Android(Frank Ableson,Manning Publications,2010):本書中介紹了 Android 開發所有方面的內容。

  • Mobile Design and Development(Brian Fling,O'Reilly Media,2009):在本書中瞭解構建移動產品的實踐指南、標準、技巧和最佳實踐。

  • 閱讀 Data Storage Dev Guide:選擇正確的方案儲存官方 Android Developer 網站上著名的持久應用程式資料 s。

  • Intents:從 Android Developer 網站了解一個將被執行的操作的抽象描述。

  • Android SDK 文件:得到 Android API 參考中的最新資訊。

  • The Open Handset Alliance:訪問 Android 的發起者。

  • developerWorks XML 專區:在 XML 專區獲取提高您的專業技能所需的資源。

  • My developerWorks 中文社群:個性化您的 developerWorks 體驗。

  • IBM XML 認證:瞭解如何才能成為一名 IBM 認證的 XML 和相關技術的開發人員。

  • XML 技術庫:訪問 developerWorks XML 專區,獲得廣泛的技術文章和技巧、教程、標準和 IBM 紅皮書。此外,閱讀更多的XML 技巧

  • developerWorks 技術活動 和 網路廣播:隨時關注這些活動中的技術。

  • developerWorks 播客:收聽面向軟體開發人員的有趣訪談和討論。

  • developerWorks 按需演示:觀看從面向初學者的產品安裝和設定到為經驗豐富的開發人員提供的高階功能的演示。

獲得產品和技術

  • <permission> 頁面:找到關於 <permission> Manifest 頁面的更多資訊。

  • keytool:嘗試這個金鑰和證照管理工具。

  • jarsigner:得到這個 JAR 簽名和驗證工具,以便為 Java ARchive (JAR) 檔案生成簽名,以及驗證已簽署 JAR 檔案的簽名。

  • Android SDK:從官方 Android Developers 網站下載 SDK、訪問 API 參考和得到關於 Android 的最新新聞。版本 1.5 以及更新的版本可以工作。

  • Android Open Source Project:找到構建 Android 相容的裝置所需的開源資訊和原始碼。

  • JDK 6 Update 21:得到 Java Platform, Standard Edition。

  • Eclipse:獲得最新的 Eclipse IDE。

  • IBM 產品評估試用版軟體:下載或 線上試用 IBM SOA Sandbox,並開始使用來自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的應用程式開發工具和中介軟體產品。 

相關文章