长亭百川云 - 文章详情

应用身份校验安全方案

vivo千镜

56

2024-07-13

引言

不同应用间有时存在功能协作场景,例如交互应用A接收用户输入的数据需要同步给后台应用B,让用户B执行具体的管控策略,此时后台应用B就可能开放数据库增删该接口给应用A,而应用B不想该功能接口被A以外的应用访问。这类为端侧特定应用提供非公开功能的场景,应用往往需要对调用方进行身份校验,以防止接口被滥用,危害自身应用安全甚至对系统安全造成影响。本文针对该类场景,从典型错误校验方式、不同组件获取包名的正确方式、校验包名/签名的正确方式等方面进行阐述,为进行端侧应用间的身份校验提供技术参考。

01 威胁分析

从威胁建模角度分析,“其他APP”与“己方APP”交互时,跨越了信任边界,存在安全威胁。

如上图所示,“其他APP”作为与“己方APP”交互的对象,属于外部实体,存在“仿冒威胁”和“抵赖威胁”。

当存在“仿冒威胁”且业务未对“其他APP”的身份进行安全校验时,恶意应用就可以伪造身份,针对性发起攻击,最终可能导致存在以下风险:

  • 非法访问特权接口;

  • 窃取用户敏感数据;

  • ............

02  典型错误

在端侧校验调用方身份时,可以选择的校验方式包括:包名校验、签名校验、权限检查等。校验方式选择不恰当或校验逻辑错误,均会导致校验失效,使得恶意应用可以调用受保护的特权接口。

2.1 校验方式错误

① 仅校验包名

若身份校验只采用验证白名单包名方式,当白名单内的应用在用户设备上未安装时,恶意应用可伪造白名单内未被安装的应用,从而绕过包名校验。

② 自定义权限使用不当

  • 自定义权限保护级别设置过低

自定义权限可通过protectionLevel来设置权限保护级别,保护级别分为normal、dangerous、signature等,若将保护级别设置为normal,则该权限只需声明即可使用,无需用户确认;

  • 使用未定义权限风险

自定义权限有一个定义方和若干个使用方,由于权限定义应用被卸载或开发人员未定义权限等原因,导致被声明使用的权限没有定义方。此时恶意应用可主动定义该权限并设定为normal等级,导致权限失去保护作用。

<!-- 定义com.xxx.permission.xxx权限 -->

权限定义和声明使用案例

2.2 校验函数使用不当

① 获取包名方法错误

若未选择正确的获取包名方法,恶意应用可利用方法缺陷,绕过包名校验和依赖包名进行的签名校验。下面列举部分获取包名的错误方法:

  • 通过getNameForUid获取包名

该方法在调用方存在sharedUserId时获取的包名为“包名:uid”,无法获取正确包名;且任意应用可通过sharedUserId伪造包名;

例如,当恶意应用声明“android:sharedUserId=”com.xxx.example”时,使用getNameForUid获取的恶意应用包名为”com.xxx.example:10xxx”(其中10xxx为恶意应用实际uid),而应用真实包名为“com.test.xxx”。

恶意应用com.test.xxx在manifest中的声明如下:

<?xml version="1.0" encoding="utf-8"?>
  • 获取processName作为包名

processName可由应用通过”android:process”自行定义,且”android:process”声明无任何限制。若使用processName作为包名,恶意应用可直接声明”android:process”为白名单内应用,绕过白名单限制。

如下为示例错误代码:

//该方法为错误的获取包名方法,只能获取进程名,进程名可伪造

例如:若白名单中存在”com.xxx.example”,恶意应用包名为“com.test.xxx”,恶意应用就可以在其manifest中声明” android:process=”com.xxx.example” ”,此时通过该方法获取的恶意应用的processName为”com.xxx.example”,从而绕过白名单校验。

② 校验包名方法错误

采用startswith、endswith或String类的contains等可绕过的包名匹配方法,或匹配包名时忽略大小写,均可被绕过。

例如:校验com.xxx.function包名,绕过示例如下:

使用startswith(“com.xxx.function”):com.xxx.functiontest

使用endswith(“com.xxx.function”):com.testcom.xxx.function

使用contains(“com.xxx.function”):com.xxx.functiontest、com.testcom.xxx.function

使用equalsIgnoreCase(“com.xxx.function”):com.xxx.Function

03  应用身份校验安全方案

若采用白名单方式校验应用,由于白名单内应用可能被卸载,推荐同时校验白名单内应用的包名和签名。

3.1 获取调用方包名安全方案

① Activity获取调用方包名(Android 11及以上版本可用)

Activity可通过反射Activity的mReferrer方法获取调用方包名,代码示例如下:

Field referrerField = Activity.class.getDeclaredField("mReferrer");

② Provider获取调用方包名

Provider可通过getCallingPackage方法获取调用方包名,代码示例如下:

String packageName = getCallingPackage();

③ Service获取调用方包名

  • 通过uid获取

若通过aidl方式进行通信,可通过getpackagesforuid获取包名,此方式获取的是同uid的包名列表,虽然这种范围扩大了获取包名的范围,但该方式获取的包名列表内一定都是同签名应用,因此用通过这种方式获取的包名同样可以用作签名验证。代码示例如下:

/* 通过uid获取调用方包名,任意应用可用*/

若通过messenger进行通信,可通过msg.sendinguid获取调用方uid,进而通过getpackagesforuid获取同uid的包名列表,代码示例如下:

/* 通过uid获取调用方包名,任意应用可用*/
  • 通过pid获取_(需REAL_GET_TASKS权限)_

获取方法代码示例如下,该方法首先获取全部进程信息,通过进程pid确定进程info,进而通过info.pkgList获取对应的同pid包名列表。

/* 通过pid获取调用方包名 

3.2 校验包名安全方案

包名校验均应避免使用startswith、endswith或String类的contains方法,推荐使用equals方法或ArrayList的contains方法(本质仍为equals方法)

代码示例如下:

//包名白名单

3.3  校验签名安全方案

通过应用包名获取签名代码示例如下:(获取签名需确保调用方对于被调用方可见)

private static Signature[] getSignatures(Context context, String packageName) {

3.4 权限校验方案

权限校验:所有调用方声明使用共同的signature级别自定义权限,此方式必须保证权限定义方已被安装。

往期推荐:

基于复杂性理论生成高质量的LLM注入攻击对抗样本

您有一份2023vivo安全年报待查收!

vivo荣获AIIA“安全治理委员会副组长单位”

关注我们,了解更多安全内容!

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2