Android系统中的Content Provider组件是一种用于在不同应用之间共享数据的机制。它提供了一种安全、可控的方式,允许应用访问其他应用的数据。然而,如果Provider组件的安全措施没有得到妥善实现,则可能会导致严重的安全漏洞,例如数据泄露、隐私侵犯甚至恶意代码执行。
Android安全开发之 Provider 组件安全
- 一、安全渗透案例
- 二、解决方案
- 2.1 无需 exported
- 2.2 需要 exported
- 三、正确的定义私有权限
- 四、Provider 组件的安全风险
一、安全渗透案例
测试步骤:
1.进入应用进行添加新内容,插入数据库
2.使用adb或者编写apk,对指定uri进行查询
1.adb shell content query --uri content://com.xxx.note.data.NoteProvider/note
测试结论:
编辑内容都会通过uri查询出来,泄露用户隐私信息,未鉴权
server@dev-fj-srv:~/Desktop $ adb shell content query --uri content://com.xxx.note.data.NoteProvider/note
Row: 0 _id=1, title=, content=<?xml version="1.0" encoding="UTF-8"?><note><notetxt>地址</notetxt></note>, longdate=1717745973845, iscollect=0, iscontainpic=0, isprivate=0, isdeleted=0, backgroundid=4, parentfolderid=-10, istop=0, topdate=0, iscontainvoice=0, picpath=NULL
Row: 1 _id=2, title=, content=<?xml version="1.0" encoding="UTF-8"?><note><notetxt>账户</notetxt></note>, longdate=1717745961274, iscollect=0, iscontainpic=0, isprivate=0, isdeleted=0, backgroundid=4, parentfolderid=-10, istop=0, topdate=0, iscontainvoice=0, picpath=NULL
Row: 2 _id=3, title=, content=<?xml version="1.0" encoding="UTF-8"?><note><notetxt>密码</notetxt></note>, longdate=1717745951538, iscollect=0, iscontainpic=0, isprivate=0, isdeleted=0, backgroundid=4, parentfolderid=-10, istop=0, topdate=0, iscontainvoice=0, picpath=NULL
漏洞修复建议:
增加鉴权校验
很多人可能会好奇,我的代码进行了混淆处理,如何发现我的
provider
及其authorities
,可以通过APK文件进行逆向或反编译出AndroidManifest.xml
,此类声明信息是不会被混淆的,可以直接获取的。
甚至可以直接通过adb获取:adb dumpsys package com.xxx.note
直接获取得到 ContentProvider Authorities
ContentProvider Authorities:[com.xxx.note.data.SearchProvider]:Provider{fc33540 com.xxx.note/.data.SearchProvider}applicationInfo=ApplicationInfo{5ba67a5 com.xxx.note}[com.xxx.note.provider]:Provider{19d68c3 com.xxx.note/androidx.core.content.FileProvider}applicationInfo=ApplicationInfo{a90977a com.xxx.note}[com.xxx.note.data.NoteProvider]:Provider{da7fc72 com.xxx.note/.data.NoteProvider}applicationInfo=ApplicationInfo{13db2b com.xxx.note}
二、解决方案
2.1 无需 exported
当 ContentProvider 仅应用自身使用,无需 exported 供其他应用调用,即可直接设置 exported="false"
<providerandroid:name=".data.NoteProvider"android:authorities="com.xxx.note.data.NoteProvider"android:exported="false" />
此时再通过 adb 命令查询就会出现 SecurityException
报错无法查询
server@dev-fj-srv:~/Desktop$ adb shell content query --uri content://com.xxx.note.data.NoteProvider/note
Error while accessing provider:com.xxx.note.data.NoteProvider
java.lang.SecurityException: Permission Denial: opening provider com.xxx.note.data.NoteProvider from (null) (pid=10781, uid=2000) that is not exported from UID 10196at android.os.Parcel.createExceptionOrNull(Parcel.java:3057)at android.os.Parcel.createException(Parcel.java:3041)at android.os.Parcel.readException(Parcel.java:3024)at android.os.Parcel.readException(Parcel.java:2966)at android.app.IActivityManager$Stub$Proxy.getContentProviderExternal(IActivityManager.java:7803)at com.android.commands.content.Content$Command.execute(Content.java:516)at com.android.commands.content.Content.main(Content.java:735)at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:447)
Caused by: android.os.RemoteException: Remote stack trace:at com.android.server.am.ContentProviderHelper.checkAssociationAndPermissionLocked(ContentProviderHelper.java:756)at com.android.server.am.ContentProviderHelper.getContentProviderImpl(ContentProviderHelper.java:411)at com.android.server.am.ContentProviderHelper.getContentProviderExternalUnchecked(ContentProviderHelper.java:182)at com.android.server.am.ContentProviderHelper.getContentProviderExternal(ContentProviderHelper.java:176)at com.android.server.am.ActivityManagerService.getContentProviderExternal(ActivityManagerService.java:6889)
2.2 需要 exported
当 ContentProvider 需要通过数据接口供其他应用使用时,就只能设置exported="true"
,这个时候我们需要添加鉴权校验或包名过滤。
<providerandroid:name=".data.SearchProvider"android:authorities="com.xxx.note.data.SearchProvider"android:exported="true"/>
在重写 ContentProvider query 方法时先进行校验,验证成功后再返回查询数据。
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {if (!isAuthorized()) {throw new SecurityException("Access denied. Unauthorized.");}// 返回数据查询
}private boolean isAuthorized() {return AUTHORIZE_ACCESS_PACKAGE.equals(getCallingPackage());
}
AUTHORIZE_ACCESS_PACKAGE
即为允许访问此 ContentProvider 的应用包名。
此时再通过 adb 命令访问此 ContentProvider 就会返回 java.lang.SecurityException: Access denied. Unauthorized.
报错。通过 adb 访问时候可以查询到 getCallingPackage() 为 com.android.shell
,不在我们运行访问的包名名单内。
server@dev-fj-srv:~/Desktop$ adb shell content query --uri content://com.xxx.note.data.SearchProvider
Error while accessing provider:com.xxx.note.data.SearchProvider
java.lang.SecurityException: Access denied. Unauthorized.at android.os.Parcel.createExceptionOrNull(Parcel.java:3057)at android.os.Parcel.createException(Parcel.java:3041)at android.os.Parcel.readException(Parcel.java:3024)at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190)at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)at android.content.ContentProviderProxy.query(ContentProviderNative.java:495)at com.android.commands.content.Content$QueryCommand.onExecute(Content.java:661)at com.android.commands.content.Content$Command.execute(Content.java:522)at com.android.commands.content.Content.main(Content.java:735)at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:447)
三、正确的定义私有权限
在AndroidManifest中定义私有权限的语法为:
其中android:protectionLevel
的可选值分别表示:
normal
:默认值,低风险权限,在安装的时候,系统会自动授予权限给 application。dangerous
:高风险权限,如发短信,打电话,读写通讯录。使用此protectionLevel来标识用户可能关注的一些权限。Android将会在安装程序时,警示用户关于这些权限的需求,具体的行为可能依据Android版本或者所安装的移动设备而有所变化。signature
: 签名权限,在其他 app 引用声明的权限的时候,需要保证两个 app 的签名一致。这样系统就会自动授予权限给第三方 app,而不提示给用户。signatureOrSystem
:除了具有相同签名的APP可以访问外,Android系统中的程序有权限访问。
大部分开放的Provider,是提供给本公司的其他应用使用的,一般的话一个公司打包签名APP的签名证书都应该是一致的,这种情况下,Provider的android:protectionLevel
应为设为“signature
”。
四、Provider 组件的安全风险
- 权限控制不当:如果Provider组件没有正确配置权限,则可能会允许未经授权的应用访问敏感数据。
- 数据泄露:Provider组件可能会被恶意应用利用来窃取敏感数据,例如联系人信息、通话记录、短信内容等。
- 隐私侵犯:Provider组件可能会被恶意应用利用来跟踪用户行为,例如位置信息、浏览历史等。
- 恶意代码执行:Provider组件可能会被恶意应用利用来执行恶意代码,例如植入病毒、木马等。