OWASP 類別:MASVS-CODE:程式碼品質
總覽
當自訂權限定義缺少或拼寫錯誤,或是資訊清單中濫用對應的 android:protectionLevel
屬性時,就會產生與自訂權限相關的風險。
舉例來說,惡意應用程式可以利用這些風險,建立同名的自訂權限,並套用不同的保護層級。
自訂權限的設計目的,是為了讓您與其他應用程式共用資源和功能。以下是合法使用自訂權限的例子:
- 控制兩個或多個應用程式之間的處理序間通訊 (IPC)
- 存取第三方服務
- 限制應用程式共用資料的存取權
影響
這項安全漏洞的影響是,惡意應用程式可能會取得原本應受保護的資源存取權。漏洞的影響取決於受保護的資源,以及原始應用程式服務的相關權限。
風險:自訂權限錯字
您可以在資訊清單中宣告自訂權限,但由於打字錯誤,因此使用其他自訂權限來保護匯出的 Android 元件。惡意應用程式可以利用應用程式輸入錯誤的權限,方法如下:
- 先註冊該權限
- 預測後續應用程式中的拼字
這可能會讓應用程式未經授權存取資源,或控制受害應用程式。
舉例來說,有安全漏洞的應用程式想要使用權限 READ_CONTACTS
保護元件,但不小心將權限拼寫錯誤為 READ_CONACTS
。惡意應用程式可以聲明 READ_CONACTS
,因為它不屬於任何應用程式 (或系統),且可取得受保護元件的存取權。這個漏洞的另一個常見變種是 android:permission=True
。true
和 false
等值 (無論大小寫) 都是無效的權限宣告輸入內容,處理方式與其他自訂權限宣告錯字類似。如要修正這個問題,請將 android:permission
屬性的值變更為有效的權限字串。舉例來說,如果應用程式需要存取使用者的聯絡人,android:permission
屬性的值應為 android.permission.READ_CONTACTS
。
因應措施
Android Lint 檢查
宣告自訂權限時,請使用 Android Lint 檢查功能,協助您找出程式碼中的錯字和其他潛在錯誤。
命名慣例
使用一致的命名慣例,讓拼寫錯誤更容易被發現。請仔細檢查應用程式資訊清單中的自訂權限宣告是否有錯字。
風險:孤立的權限
權限可用於保護應用程式的資源。應用程式可在兩個不同位置宣告存取資源所需的權限:
- AndroidManifest.xml:在 AndroidManifest.xml 檔案中預先定義 (如果未指定,系統會使用
<application>
權限),例如 提供者權限、接收器權限、活動權限、服務權限。 - 程式碼:在執行階段程式碼中註冊,例如:
registerReceiver()
。
不過,有時裝置上 APK 資訊清單中的對應 <permission>
標記並未定義這些權限。在這種情況下,這些權限稱為孤立權限。導致這種情況的原因有很多,例如:
- 資訊清單的更新內容和具有權限檢查的程式碼之間可能會出現不同步
- 建構作業可能未納入含有權限的 APK,或是納入錯誤的版本
- 檢查或資訊清單中的權限名稱拼寫錯誤
惡意應用程式可能會定義孤立的權限,並取得該權限。如果發生這種情況,信任孤立權限來保護元件的特權應用程式就可能遭到入侵。
如果特權應用程式使用權限保護或限制任何元件,這可能會授予惡意應用程式該元件的存取權。例如啟動受權限保護的活動、存取內容供應器,或是向受孤立權限保護的廣播接收器進行廣播。
這也可能導致以下情況:特權應用程式誤以為惡意應用程式是合法應用程式,因此載入檔案或內容。
因應措施
請確認應用程式用於保護元件的所有自訂權限,也已在資訊清單中定義。
應用程式會使用自訂權限 my.app.provider.READ
和 my.app.provider.WRITE
來保護內容供應器的存取權:
Xml
<provider android:name="my.app.database.CommonContentProvider" android:readPermission="my.app.provider.READ" android:writePermission="my.app.provider.WRITE" android:exported="true" android:process=":myappservice" android:authorities="my.app.database.contentprovider"/>
應用程式也會定義及使用這些自訂權限,藉此防止其他惡意應用程式執行這類操作:
Xml
<permission android:name="my.app.provider.READ"/>
<permission android:name="my.app.provider.WRITE"/>
<uses-permission android:name="my.app.provider.READ" />
<uses-permission android:name="my.app.provider.WRITE" />
風險:誤用 android:protectionLevel
這項屬性會說明權限中的潛在風險等級,並指出系統在決定是否授予權限時,應遵循的程序。
因應措施
避免使用「一般」或「危險」防護等級
在權限中使用一般或危險 protectionLevel
,表示大多數應用程式都可以要求並取得權限:
- 「normal」只需要宣告
- 「dangerous」會獲得許多使用者的核准
因此,這些 protectionLevels
的安全性不高。
使用簽章權限 (Android 10 以上)
盡可能使用簽章保護等級。採用這項功能可確保只有與建立權限的應用程式相同憑證簽署的其他應用程式,才能存取這些受保護的功能。請務必使用專用 (不會重複使用的) 簽署憑證,並將其安全儲存在KeyStore 中。
在資訊清單中定義自訂權限,如下所示:
Xml
<permission
android:name="my.custom.permission.MY_PERMISSION"
android:protectionLevel="signature"/>
將存取權限限制為僅限已授予這項自訂權限的應用程式,例如活動,如下所示:
Xml
<activity android:name=".MyActivity" android:permission="my.custom.permission.MY_PERMISSION"/>
任何其他應用程式只要使用與宣告此自訂權限的應用程式相同的憑證簽署,就會獲得 .MyActivity
活動的存取權,並需要在其資訊清單中宣告權限,如下所示:
Xml
<uses-permission android:name="my.custom.permission.MY_PERMISSION" />
小心簽章自訂權限 (Android 10 以下)
如果您的應用程式指定 Android 10 以下版本,那麼每當應用程式的自訂權限因解除安裝或更新而移除時,惡意應用程式就可能仍能使用這些自訂權限,進而規避檢查。這是因為權限升級安全漏洞 (CVE-2019-2200
) 已在 Android 10 中修正。
這也是為何我們建議使用簽名檢查 (而非自訂權限) 的原因之一 (此外,這也能降低競爭狀態的風險)。
風險:競爭狀況
如果合法應用程式 A
定義了其他 X
應用程式使用的簽章自訂權限,但後來遭到解除安裝,則惡意應用程式 B
可以使用不同的 protectionLevel
(例如 normal) 定義相同的自訂權限。這樣一來,B
就能取得 X
應用程式中受該自訂權限保護的所有元件存取權,而無須使用與應用程式 A
相同的憑證簽署。
如果 B
在 A
之前安裝,也會發生同樣的情況。
因應措施
如果您希望元件只供使用與提供應用程式相同簽章的應用程式使用,您或許可以避免定義自訂權限來限制該元件的存取權。在這種情況下,您可以使用簽名檢查。當其中一個應用程式為另一個應用程式發出要求時,第二個應用程式可以先驗證兩個應用程式是否使用相同的憑證簽署,再發出該項要求。
資源
- 盡可能減少權限要求
- 權限總覽
- 防護等級說明
- CustomPermissionTypo Android Lint
- 如何使用 Android Lint
- 研究論文,深入說明 Android 權限和有趣的模糊測試結果