概述
当Android应用程序需要访问设备上的敏感资源时,应用程序开发人员会使用权限模型。虽然该模型使用起来非常简单,但开发人员在使用权限时容易出错,从而导致安全漏洞。本文中,首先将探讨几种不同类型的权限,然后解释开发人员在实现权限模型时常犯的错误,以及如何避免这些错误。
权限声明及其类型
权限有助于限制对某些Android组件,如活动、服务和内容提供商的访问。开发人员可以在应用程序的AndroidManifest.xml文件中声明各种权限类型。
权限还用于在运行时验证应用程序是否有权访问敏感信息或执行危险操作。例如,对于要访问联系人的应用程序,开发人员必须在其AndroidManifest.xml文件中包含uses-permission声明:
<uses-permission android:name="android.permission.READ_CONTACTS" />
这是一个将protectionLevel设置为危险的内置权限。在实现此权限级别时,Android+系统会在安装过程中询问用户是否愿意向此应用授予此类权限。如果用户拒绝,则不会安装该应用程序。
如果开发人员希望创建自己的权限,则必须在清单中声明它,如下所示:
<permission android:name="com.mycoolcam.USE_COOL_CAMERA" android:protectionLevel="dangerous" />
<activity android:name=".CoolCamactivity" android:exported="true" android:permission="com.mycoolcam.USE_COOL_CAMERA"> <intent-filter> <action android:name="com.mycoolcam.LAUNCH_COOL_CAM" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
然后,如果第三方应用程序想要使用此示例的功能,则需要进行如下声明:
<uses-permission android:name="com.mycoolcam.USE_COOL_CAMERA" />
并在安装和使用时要求用户授予权限。
除此之外,以下还列举了一些其他权限级别,例如:
normal:此权限对用户“不可见”。用户不知道应用程序正在请求访问此资源,因为在安装过程中会自动授予权限。
dangerous:如前所述,此级别要求用户确认应用程序是否可以访问特定资源。
signature:使用此权限级别的应用程序必须使用与声明它的应用程序相同的证书进行签名。这可以防止攻击者为此级别创建uses-permission声明,因为 Android将不允许安装该应用程序。
系统应用程序还使用其他几个级别,如system、 installer、privileged等。从攻击者的角度来看,这些应该被视为signature级别,即使用此保护级别的权限不能在 uses-permission中被声明。
接下来,让我们看看开发人员在使用权限时会犯的一些常见错误。
忘记protectionLevel保护等级
如果在权限声明中没有提到protectionLevel保护等级,则默认情况下它将被标记为normal,这将允许任何应用程序能够使用它。三星内置应用程序的开发人员也犯了类似的错误,导致手机上的其他应用程序有可能读取受害者的通话记录、短信等。
<permission android:name="com.mycoolcam.USE_COOL_CAMERA" />
生态系统错误
假设开发人员有两个应用程序的生态系统:My Cool Cam和My Cool Reader。阅读器应用程序使用相机应用程序的功能。
相机应用程序的清单:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mycoolcam"> <permission android:name="com.mycoolcam.USE_COOL_CAMERA" android:protectionLevel="signature" /> <uses-permission android:name="com.mycoolcam.USE_COOL_CAMERA" /> <Application android:label="My Cool Cam"> <activity android:name=".CoolCamActivity" android:exported="true" android:permission="com.mycoolcam.USE_COOL_CAMERA"> <intent-filter> <action android:name="com.mycoolcam.LAUNCH_COOL_CAM" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <!-- ... --> </application>
阅读器应用程序的清单:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mycoolreader"> <uses-permission android:name="com.mycoolcam.USE_COOL_CAMERA" /> <application android:label="My Cool Reader"> <provider android:name=".AllUserNotesContentProvider" android:authorities="com.mycoolreader.notes_provider" android:exported="true" android:permission="com.mycoolcam.USE_COOL_CAMERA" /> <!-- ... --> </application> </manifest>
乍一看,似乎一切都很好且安全,因为只有生态系统中的应用程序才能访问存储在 AllUserNotesContentProvider中的敏感信息。
但是,如果受害者的设备只安装了阅读器应用程序会发生什么?在这种情况下,Android系统将不知道com.mycoolcam.USE_COOL_CAMERA权限声明的任何内容,因此默认情况下会将权限级别标记为normal。
为防止出现这种情况,请确保在每个应用程序中也分别声明来自多个应用程序生态系统的每个权限。
权限名称中的错别字
开发人员在权限名称中打错字是非常常见的:
<permission android:name="com.mycoolcam.USE_COOL_CAMERA" android:protectionLevel="signature" />
<activity android:name=".CoolCamActivity" android:exported="true" android:permission="com.mycoolcam.USE_COOL_CAM"> <intent-filter> <action android:name="com.mycoolcam.LAUNCH_COOL_CAM" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
在以上示例中,com.mycoolcam.USE_COOL_CAMERA的权限的保护等级为signature,而com.mycoolcam.USE_COOL_CAM的将被设置为normal。这允许任意应用程序使用com.mycoolcam.USE_COOL_CAM进行使用权限声明,从而授予对CoolCamActivity活动的访问权。
组件声明中的错别字
<permission android:name="com.mycoolcam.USE_COOL_CAMERA" android:protectionLevel="signature" />
<activity android:name=".CoolCamActivity" android:exported="true" android:uses-permission="com.mycoolcam.USE_COOL_CAMERA"> <intent-filter> <action android:name="com.mycoolcam.LAUNCH_COOL_CAM" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
在以上示例中,它没有使用android:permission属性(设置组件的访问级别),而是使用android:uses-permission。这将允许任何第三方应用程序访问它,因为这意味着该组件没有保护级别。
权限保护不足
某些应用程序并没有完全保护他们使用的权限,这为第三方应用程序利用易受攻击的应用程序并获得权限留下了空间。
让我们通过一个简单的例子更好地理解这一点。
AndroidManifest.xml文件:
<uses-permission android:name="android.permission.READ_CONTACTS" />
<provider android:name=".ContactsProvider" android:authorities="com.exampleapp.contacts" android:exported="true" />
ContactsProvider.JAVA文件:
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return getContext().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, selection, selectionArgs, sortOrder); }
在这种情况下,您可以清楚地看到访问 URIContactsContract.CommonDataKinds.Phone(别名为//com.android.contacts/data/phones)需要android.permission.READ_CONTACTS权限。但是,访问 content://com.exampleapp.contacts不需要任何权限。
为了防止这种情况,应用程序应该确保对其收到的任何权限进行保护。