安全性指南

Android 內建安全性功能,可大幅降低應用程式安全性問題發生的頻率及造成的影響。這套作業系統經過精心設計,可讓您使用預設的系統和檔案權限來設計應用程式,不必費心處理複雜的安全性設定工作。

下列核心安全性功能可協助您打造安全的應用程式:

  • Android 應用程式沙箱。它會將您的應用程式資料和程式碼執行與其他應用程式隔離開來。
  • 應用程式架構可提供完善的常見安全性應用功能,例如密碼編譯、權限和安全的處理序間通訊 (IPC)。
  • 採用 位址空間配置隨機載入 (ASLR)不執行 (NX)、ProPolice、safe_iopOpenBSD dlmalloccalloc,以及 mmap_min_addr 等技術,即可降低與常見記憶體管理錯誤相關的風險。
  • 由使用者授予的權限,可限制系統功能和使用者資料的存取權。
  • 根據應用程式設定的權限,針對個別應用程式管理其資料。

請務必熟悉本文所述的 Android 安全性最佳做法。只要把這些做法融入編碼習慣,就能減少因疏忽造成的安全性問題,避免對使用者帶來負面影響。

資料儲存

對 Android 應用程式而言,使用者最關切的安全顧慮是其他應用程式能否存取裝置上儲存的資料。將資料儲存在裝置的基本方式有以下三種:

  • 內部儲存空間
  • 外部儲存空間
  • 內容供應器
以下各節會說明與每項方法相關的安全性問題。

內部儲存空間

根據預設,您在內部儲存空間建立的檔案只有您的應用程式可以存取。Android 導入了這項保護機制,適用於大多數應用程式。

請避免避免對處理序間通訊 (IPC) 檔案使用已淘汰的 MODE_WORLD_WRITEABLEMODE_WORLD_READABLE 模式,因為這些模式並未提供限制特定應用程式資料存取權的功能,也沒有任何控管資料格式的功能如果您想與其他應用程式處理程序共用資料,則建議您改用內容供應器,以便為其他應用程式提供讀取和寫入權限,並依照個別情況授予動態權限。

外部儲存空間

您在外部儲存空間 (例如 SD 卡) 建立的檔案可供任何人讀取及寫入。由於外部儲存空間可能遭使用者移除,或是受到任何應用程式修改,因此請只使用外部儲存空間保存非私密資訊。

就像處理來源不受信任的資料一樣,處理來自外部儲存空間的資料時,您必須執行輸入驗證。請勿在動態載入前,將可執行的檔案或類別檔案保存至外部儲存空間。如果您的應用程式從外部儲存空間擷取到可執行的檔案,這類檔案必須先經過簽署及密碼編譯驗證才能動態載入。

內容供應器

內容供應器提供結構化儲存機制,可限制僅供您的應用程式存取,或是匯出以供其他應用程式存取。如果您不想將 ContentProvider 存取權授予其他應用程式,請在應用程式資訊清單中將其標示為 android:exported=false。否則,請將 android:exported 屬性設為 true,讓其他應用程式存取已儲存的資料。

建立要匯出供其他應用程式使用的 ContentProvider 時,您可以指定單一的讀取和寫入權限,或是針對讀取和寫入行為指定不同的權限。請僅將權限授予完成手邊工作所需的應用程式。提醒您,與其因為移除權限而影響到現有使用者,不如日後新功能推出時再新增權限,後者通常更容易。

如果您使用的內容供應器只有和您自己的應用程式共用資料,建議將 android:protectionLevel 屬性設為 signature 保護層級。設定 signature 權限後,系統就不需要使用者進行確認。因此,如果存取內容提供器資料的應用程式都是使用相同金鑰簽署,使用者即可獲得更好的使用體驗,並且更有效管理權限。

內容供應器也可以提供更詳細的權限設定,只要在啟動元件的 Intent 物件中宣告 android:grantUriPermissions 屬性,並使用 FLAG_GRANT_READ_URI_PERMISSIONFLAG_GRANT_WRITE_URI_PERMISSION 標記即可。您還可以使用 <grant-uri-permission> 元素進一步限制這些權限的範圍。

存取內容供應器時,請使用參數化查詢方法 (例如 query()update()delete()),以防範來自不受信任來源的 SQL 插入式攻擊。請注意,如果以串連使用者資料的方式建立 selection 引數,再提交至參數化方法,則光用參數化方法仍不夠安全。

請不要誤以為提供寫入權限很安全,因為寫入權限允許使用 SQL 陳述式,所以任何人只要使用自訂 WHERE 子句並剖析結果,即可確認部分資料。舉例來說,如果攻擊者想知道通話記錄內是否包含特定電話號碼,那麼只要該號碼確實存在,他就能透過修改通話記錄的一列來達成目的;如果內容供應器採用可預測的資料結構,提供寫入權限就等於同時提供了讀取和寫入權限。

權限

由於 Android 會使用沙箱隔離應用程式,因此應用程式必須明確共用資源和資料。為達到上述目的,應用程式必須針對基本沙箱未提供的功能,宣告執行這些功能所需的權限,包括相機等裝置功能的存取權。

權限要求

請盡量減少應用程式要求的權限數量。限制機密權限的存取權可降低不慎誤用這類權限的風險、提升使用者的接受度,並讓應用程式較不容易遭受攻擊。一般來說,如果應用程式不需要某項權限也能運作,就不該要求此權限。請參閱「評估應用程式是否需要宣告權限」。

請盡可能以不需要任何權限的方式設計應用程式。以建立專屬 ID 為例,您不必讓應用程式要求存取裝置資訊,只要為應用程式建立 UUID 即可 (請參閱「使用者資料」一節)。同樣地,儲存資料不必使用外部儲存空間 (需要取得權限),有內部儲存空間就行了。

除了要求權限以外,應用程式也可以使用 <permission> 來保護安全敏感且會暴露給其他應用程式的資訊,例如 ContentProvider。一般而言,建議您盡量不要使用需使用者確認的權限,因為使用者可能對此感到困惑。比方說,對於同一開發人員提供的應用程式之間的 IPC 通訊權限,可以使用 signature 保護層級

請勿洩漏受權限保護的資料。如果發生這種情形,表示您的應用程式具備該資料的存取權,且在 IPC 上洩漏了資料。應用程式 IPC 介面的用戶端可能沒有相同的資料存取權。如想進一步瞭解這項問題的發生頻率與可能影響,請參閱在 USENIX 發布的研究論文「Permission Re-Delegation: Attacks and Defenses」(重新委派權限:攻擊與防禦)。

權限定義

請定義符合安全性需求的最低權限組合。對於大部分應用程式來說,建立新權限較為少見,因為系統定義的權限已足夠因應各種情況,必要時再使用現有權限執行存取檢查即可。

如果您需要建立新權限,請先評估使用 signature 保護層級是否可以達成目的。簽章權限的內容對使用者是透明的,只有執行權限檢查的開發人員所簽署的應用程式可以存取。

如果仍需建立新權限,請使用 <permission> 元素,在應用程式資訊清單中宣告權限。如要參照使用新權限的應用程式,可以在資訊清單檔案中加入 <uses-permission> 元素。您也可以使用 addPermission() 方法動態新增權限。

如果您要建立 dangerous 保護層級的權限,則必須考慮一些較為複雜的層面:

  • 權限必須包含明確清楚的字串,告知使用者需要做的安全性決定。
  • 權限字串必須翻譯成多種不同的語言。
  • 使用者可能因為認為權限設定太過複雜或有風險,選擇不安裝應用程式。
  • 應用程式可能在權限建立者尚未安裝的情況下要求權限。

以上幾點不僅對開發人員是重大的非技術性難題,也可能令使用者感到困惑,所以我們不建議使用 dangerous 權限層級。

網路

網路交易本身就有安全性風險,因為其中可能涉及傳輸使用者的私人資料。越來越多人逐漸意識到行動裝置存在的隱私問題,尤其在透過行動裝置進行網路交易時更是如此。因此,為了全程維護使用者的資料安全,請務必為應用程式採取所有最佳做法。

IP 網路

Android 的網路功能和 Linux 等其他平台的差異不大,關鍵在於確保對私密資料使用正確的通訊協定,比如針對加密網路流量使用 HttpsURLConnection。在伺服器支援 HTTPS 的情況下,請一律使用 HTTPS 取代 HTTP,因為行動裝置經常連線到不安全的網路 (例如公共 Wi-Fi 熱點)。

使用 SSLSocket 類別,即可輕鬆在通訊端層級進行驗證加密的通訊。考慮到 Android 裝置時常經由 Wi-Fi 連線到不安全無線網路,我們強烈鼓勵所有需使用網路通訊的應用程式,盡量使用安全的網路連線。

有些應用程式會使用 localhost 網路通訊埠處理敏感的處理序間通訊 (IPC)。請勿使用這個方法,因為裝置上的其他應用程式也可以存取這些介面。請改用 Android IPC 機制,以便透過 Service 等方法進行驗證。繫結至非特定 IP 位址 INADDR_ANY 比使用回送還糟,因為這麼做可讓應用程式接收來自任何 IP 位址的要求。

請確保您不信任透過 HTTP 或其他不安全通訊協定下載的資料,包括在 WebView 中進行輸入驗證,以及針對 HTTP 發出意圖所接收的任何回應。

電話網路

簡訊 (SMS) 通訊協定主要是為服務使用者之間的通訊而設,並不適合應用程式用來傳輸資料。由於簡訊的先天限制,建議您使用 Firebase 雲端通訊 (FCM) 和 IP 網路,從網路伺服器將資料訊息傳送到您在使用者裝置上的應用程式。

請注意,簡訊既沒有為網路和裝置加密,也沒有加強安全性的驗證機制。更重要的是,任何簡訊接收器都可能收到惡意人士傳送至應用程式的簡訊,因此請勿憑藉未經驗證的簡訊資料執行機密指令。另請注意,簡訊可能會在網路上遭到假冒及/或攔截。在 Android 裝置上,簡訊是以廣播意圖的形式傳遞,因此其他擁有 READ_SMS 權限的應用程式可能讀取或擷取其內容。

輸入驗證

不管在何種平台,輸入驗證功能不足都是應用程式最常見的安全性問題。Android 為各平台提供了專門的對策,可避免應用程式出現輸入驗證問題,建議您善用這些機制。此外,也建議您使用類型安全的語言,降低發生輸入驗證問題的可能性。

如果您使用了機器碼,那麼無論應用程式是從檔案讀取資料,還是透過網路或 IPC 接收資料,都可能造成安全性問題。最常見的問題是緩衝區溢位釋放後使用差一錯誤。雖然 Android 提供的 ASLR 和資料執行防止 (DEP) 等多項技術能夠降低這類錯誤被人利用的機會,但無法解決根本問題。不過,請務必謹慎處理指標及管理緩衝區,藉此防範這些安全漏洞。

使用字串的動態語言 (例如 JavaScript 和 SQL) 也可能因為逸出字元和插入指令碼而出現輸入驗證問題。

如果您使用提交至 SQL 資料庫或內容提供器的查詢資料,也可能造成 SQL 插入問題。最好的預防方式是使用參數化查詢 (請參閱上節所述的「內容供應器」)。限制應用程式只有讀取或寫入權限,也可以減少 SQL 插入問題可能造成的損害。

如果您無法使用本節所述的安全防護功能,請務必使用完善的結構化資料格式,並確認資料是否符合預期格式。雖然封鎖特定字元或替換字元或許是一種有效做法,但這些技巧在實作時很容易出錯,建議您盡量避免使用。

使用者資料

一般來說,保護使用者資料安全性的最佳做法,就是盡量避免使用需要存取使用者機密資料或個人資料的 API。如果您可以存取使用者資料,請盡可能避免儲存或傳輸資料。請評估您的應用程式邏輯是否能改採雜湊或無法復原的資料形式執行。舉例來說,您的應用程式或許可以使用電子郵件地址的雜湊做為主鍵,避免傳輸或儲存電子郵件地址。這種做法可以減少資料不慎外洩的風險,也能降低您的應用程式遭人用於發動攻擊的機會。

如果您的應用程式可以存取個人資訊 (例如密碼或使用者名稱),請注意,部分司法管轄區會要求您提供隱私權政策,解釋您如何使用及儲存這類資料。按照安全性最佳做法可盡量減少存取使用者資料,還能同時簡化法規遵循作業。

此外,您也應該思考,自己的應用程式是否可能不經意地將個人資訊洩漏給其他對象,比如第三方廣告元件或應用程式所用的第三方服務。如果您不知道某個元件或服務為何需要取得個人資訊,請不要向它們提供資料。讓應用程式減少存取個人資訊,通常也能減少這方面問題發生的機會。

如果應用程式需要存取私密資料,請評估是否需要將資料傳輸至伺服器,或是能否在用戶端執行作業。建議您在用戶端執行會用到私密資料的程式碼,以免傳輸使用者資料。同時,請確保應用程式不會經由過於寬鬆的處理序間通訊 (IPC)、可寫入的檔案或網路通訊端,在不經意間洩漏使用者資料給其他應用程式。過於寬鬆的處理序間通訊 (IPC) 是權限保護資料外洩的特別案例,詳情請參閱「權限要求」一節。

如果需要全域專屬 ID (GUID),請建立較長的專屬號碼並予以儲存,但切勿將與個人資訊相關的電話識別碼 (如電話號碼或 IMEI) 設為密碼。如要進一步瞭解這個主題,請參閱「專屬 ID 最佳做法」頁面。

寫入裝置端記錄時,請務必留意。記錄是 Android 中的共用資源,任何擁有 READ_LOGS 權限的應用程式皆可存取。即使手機記錄資料是暫時性的,在重新啟動後就會刪除,但是不適當記錄使用者資訊,仍可能導致應用程式在不經意間,將使用者資料洩漏給其他應用程式。除了不要記錄 PII 外,也請限制正式版應用程式的記錄使用情形。如要輕鬆實作,請使用偵錯標記並自訂 Log 類別,並採用可輕鬆設定的記錄層級。

WebView

由於 WebView 能夠識別的網頁內容包含 HTML 和 JavaScript,所以使用不當可能帶來幾種常見的網路安全性問題,如跨網站指令碼攻擊 (JavaScript 插入攻擊)。Android 內建大量用來減少這類潛在問題的機制,包括限制 WebView 只用應用程式所需的最少功能。

如果您的應用程式沒有直接在 WebView 中使用 JavaScript,請「不要」呼叫 setJavaScriptEnabled()。有些程式碼範例會使用這個方法;如果您在正式版應用程式中將使用此方法的程式碼範例改用於其他用途,若該方法呼叫並非必要,請將其移除。根據預設,WebView 不會執行 JavaScript,因此不可能出現跨網站指令碼攻擊。

請特別留意 addJavaScriptInterface() 的使用方式,因為它會使 JavaScript 叫用通常為 Android 應用程式保留的作業。如要使用 addJavaScriptInterface(),請務必只對所有輸入來源皆可信的網頁開放。若是允許了不受信任的輸入來源,不受信任的 JavaScript 可能就可以叫用您應用程式中的 Android 方法。一般而言,我們建議只對您應用程式 APK 中的 JavaScript 開放 addJavaScriptInterface()

如果您的應用程式會透過 WebView 存取私密資料,請考慮使用 clearCache() 方法刪除儲存在本機的所有檔案。您也可以使用伺服器端標頭 (例如 no-store),指出應用程式不應快取特定內容。

如果裝置搭載 Android 4.4 (API 級別 19) 以下版本,則使用的 webkit 版本會有多項安全性問題。為解決問題,如果應用程式在這類裝置上執行,必須確認 WebView 物件只顯示可信任的內容。為確保您的應用程式不會暴露在安全資料傳輸層 (SSL) 中的潛在安全漏洞之下,請按照「更新安全性提供者防範安全資料傳輸層 (SSL) 漏洞」一文所述,使用可更新的安全性 Provider 物件。如果您的應用程式必須轉譯來自開放網路的內容,建議您提供自己的轉譯器,方便您持續保有最新的安全性修補程式。

憑證要求

如果想讓網路釣魚攻擊更顯而易見,並降低攻擊成功機率,請將要求使用者憑證的頻率減至最低,改為使用並更新授權權杖。

請盡可能避免在裝置上儲存使用者名稱和密碼,而是透過使用者提供的使用者名稱和密碼進行初始驗證,然後使用特定服務專屬的短期授權權杖。

建議您使用 AccountManager 連線至多個應用程式可存取的服務。可以的話,最好是用 AccountManager 類別叫用雲端服務,不要把密碼儲存在裝置上。

使用 AccountManager 擷取 Account 後,請先使用 CREATOR 再傳入任何憑證,以免您不小心將憑證傳遞至錯誤的應用程式。

如果只有您建立的應用程式會使用憑證,您可以使用 checkSignatures() 驗證能存取 AccountManager 的應用程式。如果只有一個應用程式會使用憑證,則可以用 KeyStore 儲存憑證。

密碼編譯

Android 不僅提供資料隔離、支援完整檔案系統加密、提供安全通訊管道,也提供大量使用密碼編譯保護資料的演算法。

瞭解軟體使用的 Java Cryptography Architecture (JCA) 安全性提供者。請嘗試根據您的使用情況,採用已實作的現有最高層級架構。在適用情況下,請按照 Google 指定的順序使用 Google 提供的安全性提供者。

如果您需要從已知網路位置安全擷取檔案,也許只要用簡單的 HTTPS URI 就夠了,不一定要有密碼編譯方面的知識。如果需要安全通道,建議您使用 HttpsURLConnectionSSLSocket,不要自行撰寫通訊協定。如果您使用的是 SSLSocket,請注意,它不會執行主機名稱驗證。請參閱「直接使用 SSLSocket 的相關警告」。

如果需要自己建置通訊協定,請不要採用自行設計的密碼編譯演算法。您可以使用現有的密碼編譯演算法,比如 Cipher 類別中實作的 AES 或 RSA。此外,請按照下列最佳做法操作:

  • 將 256 位元 AES 用於商業用途 (如果無法使用,請改用 128 位元 AES)。
  • 使用 224 位元或 256 位元公開金鑰大小,進行橢圓曲線 (EC) 密碼編譯。
  • 瞭解使用 CBC、CTR 或 GCM 區塊模式的時機。
  • 在 CTR 模式中避免重複使用 IV/計數器。請務必以隨機方式加密。
  • 使用加密功能時,請以 CBC 或 CTR 模式,搭配下列任一函式來實作完整性:
    • HMAC-SHA1
    • HMAC-SHA-256
    • HMAC-SHA-512
    • GCM 模式

使用安全的隨機號碼產生器 SecureRandom,即可初始化任何加密編譯金鑰 KeyGenerator。如果使用的不是這種方式所產生的金鑰,演算法的強度就會大幅降低,產生允許離線攻擊的漏洞。

如果需要儲存金鑰以便重複使用,請使用 KeyStore 之類的機制,以便長期儲存及擷取加密編譯金鑰。

處理序間通訊

有些應用程式會嘗試用傳統 Linux 技術 (例如網路通訊端和共用檔案) 執行 IPC。不過,我們強烈建議您改用 Android 系統的 IPC 相關功能,例如 IntentBinderMessenger,以及 ServiceBroadcastReceiver。Android IPC 機制可讓您驗證連線至 IPC 的應用程式身分,並設定每個 IPC 機制的安全性政策。

許多安全性元素在各 IPC 機制中是共用的。如果您的 IPC 機制不適合其他應用程式使用,請在元件的資訊清單元素 (例如 <service>元素) 中,將 android:exported 屬性設為 false。這個方法可用於處理在相同 UID 下執行不同處理程序的應用程式;或者,如果您在後續開發過程中決定不要透過 IPC 機制共用資料,但是不想重寫程式碼,也可以這麼做。

如果其他應用程式能存取您的 IPC,您可以使用 <permission> 元素套用安全性政策。如果 IPC 介於您自己的應用程式之間,且皆使用相同的金鑰簽署,請在 android:protectionLevel 中使用 signature 層級權限。

意圖

對於活動和廣播接收器而言,意圖是 Android 上非同步 IPC 的偏好機制。您可以根據應用程式需求,為特定應用程式元件使用 sendBroadcast()sendOrderedBroadcast() 或明確意圖。基於安全考量,建議您使用明確意圖。

注意:如果您使用意圖繫結至 Service,請使用明確意圖來維持應用程式安全。使用隱含意圖啟動服務會危害安全性,因為您無法確定哪個服務會回應意圖,而使用者也無法查看哪個服務會啟動。自 Android 5.0 (API 級別 21) 起,如果您使用隱含意圖呼叫 bindService(),系統會擲回例外狀況。

請注意,由於單一接收器可「消耗」多個有序廣播,所以這類廣播可能不會傳送至所有應用程式。如果需要將某個意圖傳送給特定接收器,您必須使用以名稱宣告該接收器的明確意圖。

意圖傳送者可以驗證接收器是否擁有適當權限,能夠透過方法呼叫來指定非空值權限。只有具備適當權限的應用程式才會收到意圖。如果廣播意圖中的資料可能有私密性,建議您套用權限,確保惡意應用程式無法在沒有適當權限的情況下註冊,才能接收這些訊息。在這種情況下,您可以考慮直接叫用接收器,而不要發送廣播訊息。

注意:意圖篩選器並非安全防護功能。可透過明確意圖叫用的元件,不一定擁有符合意圖篩選器的資料。請在意圖接收器中執行輸入驗證,確認其符合意圖接收器、服務或活動的正確格式。

服務

Service 通常用於提供功能給其他應用程式使用。每個服務類別的資訊清單檔案中都必須有對應的 <service> 宣告。

根據預設,服務不會匯出,也無法由其他應用程式叫用。不過,只要在服務宣告中加入任何調用請求篩選器,服務就會預設對外供應。因此,建議您明確宣告 android:exported 屬性,確保其運作方式符合預期。您也可以使用 android:permission 屬性來保護服務。這樣一來,其他應用程式就需要在各自的資訊清單中宣告相對應的 <uses-permission> 元素,才能啟動、停止或繫結至該服務。

注意:如果應用程式指定的是 Android 5.0 (API 級別 21) 以上版本,請使用 JobScheduler 執行背景服務。

服務可以保護依權限接收的個別處理序間通訊 (IPC) 呼叫,只要在呼叫 checkCallingPermission() 前執行該呼叫即可。我們建議您在資訊清單中使用宣告式權限,比較不容易出錯。

注意:不要將用戶端和伺服器權限混淆;請確認呼叫的應用程式具有適當權限,並檢查您是否將相同權限授予呼叫應用程式。

繫結器和傳訊工具介面

在 Android 中,BinderMessenger 是處理 RPC 型 IPC 的偏好機制。它們可提供完善的介面,讓您視需求啟用端點相互驗證。

建議您在設計應用程式介面時,將介面設計為不需要特定權限檢查。應用程式資訊清單中不會宣告 BinderMessenger 物件,因此您無法直接為這類物件套用宣告式權限。一般而言,這類物件會繼承實作的 ServiceActivity 在應用程式資訊清單中宣告的權限。如果您建立的介面需要驗證和/或存取權控管,則必須在 BinderMessenger 介面中以程式碼形式明確加入這些控制項。

如果提供的介面不需存取控制項,請使用 checkCallingPermission() 確認呼叫者是否擁有必要權限。特別是在代表呼叫者存取服務前,請一定要確認這點,因為您應用程式的身分會傳送到其他介面。如果您叫用 Service 提供的介面,如果沒有存取指定服務的權限,對 bindService() 的叫用可能會失敗。如需允許外部程序與應用程式互動,但您不具備必要權限,可以使用 clearCallingIdentity() 方法。這個方法會對應用程式介面執行呼叫,就像由應用程式自行 (而非外部呼叫端) 發出呼叫。您之後可以使用 restoreCallingIdentity() 方法還原呼叫端權限。

如需進一步瞭解使用服務執行 IPC 的資訊,請參閱「繫結服務」。

廣播接收器

BroadcastReceiver 會處理 Intent 發起的非同步要求。

預設情況下,接收器是對外開放的,可供任何其他應用程式叫用。如果您的 BroadcastReceiver 是為了供其他應用程式使用,那麼您可以在應用程式資訊清單中使用 <receiver> 元素為接收器套用安全性權限,以防沒有適當權限的應用程式傳送意圖給 BroadcastReceiver

透過動態載入的程式碼確保安全性

我們強烈建議不要載入應用程式 APK 以外來源的程式碼,因為這些插入或改動的程式碼很可能使應用程式更容易遭到入侵,也會導致管理版本及測試應用程式的過程更複雜,而且可能會使應用程式無法驗證應用程式行為,因此在某些環境中可能會遭到禁止。

如果您的應用程式需要動態載入程式碼,請務必確認這些程式碼在執行時,具備與應用程式 APK 同樣的安全性權限。使用者會根據您的身分決定是否安裝您的應用程式,同時他們也希望您提供應用程式內執行的所有程式碼,包括動態載入的程式碼。

許多應用程式會嘗試從不安全的地方載入程式碼的應用程式,例如經由未加密的通訊協定從網路下載程式碼,或是從任何人都可以寫入的位置 (例如外部儲存空間) 下載程式碼。這些位置可讓網路上的使用者修改傳輸的內容,使用者裝置上的其他應用程式也可能修改這些內容。另一方面,其他應用程式無法修改直接包含在 APK 中的模組,無論這段程式碼是原生程式庫,還是使用 DexClassLoader 載入的類別都一樣。

虛擬機器安全性

Dalvik 是 Android 的執行階段虛擬機器 (VM)。雖然 Dalvik 是專為 Android 設計,但是也必須面臨其他虛擬機器上關於安全程式碼方面的疑慮。在一般情況下,您無須擔心虛擬機器的安全性問題。您的應用程式會在安全的沙箱環境中執行,所以系統中的其他處理程序無法存取您的程式碼或私密資料。

如要進一步瞭解虛擬機器安全性,建議您先研讀一些相關的現有文獻。以下是兩個比較熱門的資源:

本文件著重說明 Android 專屬或與其他 VM 環境不同的領域。如果開發人員曾在其他虛擬機器環境中進行程式設計,編寫 Android 應用程式時會遇到以下兩大問題:

  • 有些虛擬機器 (例如 JVM 或 .NET 執行階段) 本身就是安全性界線,會將程式碼和基本作業系統的處理功能加以分離。在 Android 中,Dalvik 虛擬機器並不是安全性界線,而是在作業系統層級執行應用程式沙箱,所以 Dalvik 可以和同一應用程式中的機器碼交互作業,沒有任何安全性限制。
  • 由於行動裝置上的儲存空間有限,開發人員通常會想要建立模組化應用程式,並使用動態類別載入。這時候需要先評估兩方面的問題,一個是擷取應用程式邏輯的來源,另一個則是應用程式在本機的儲存位置。請勿使用從未經驗證的來源 (例如不安全的網路來源或外部儲存空間) 載入的動態類別,因為這類程式碼可能會遭到修改而含有惡意行為。

原生程式碼安全性

通常情況下,我們建議開發人員使用 Android SDK 開發應用程式,不要使用原生程式碼搭配 Android NDK 的開發模式。使用原生程式碼建立的應用程式一般比較複雜、適用性較低,也比較容易發生常見的記憶體損毀問題 (例如緩衝區溢位)。

Android 是使用 Linux 核心打造,所以如果您要使用機器碼,熟悉 Linux 開發安全性最佳做法將會有所助益。Linux 安全性做法不在本文的討論範圍,但最常見的資源之一是「Secure Programming HOWTO - Creating Secure Software」。

Android 與大部分 Linux 環境最重要的不同之處,在於應用程式沙箱。在 Android 中,所有應用程式都是在應用程式沙箱中執行,就連使用原生程式碼寫成的應用程式也一樣。熟悉 Linux 的開發人員在思考何謂沙箱時,只要知道每個應用程式都會分配一個專屬使用者 ID (UID),而且該 UID 只有最基本的權限就行了。對於這個主題,我們在「Android 安全性總覽」一文中有詳細介紹。即使您使用的是原生程式碼,也應該熟悉一些應用程式權限。