Android移动安全第一章_组件导出安全
字数 5457
更新时间 2026-03-24 18:10:02

Android 移动安全教学文档:组件导出安全

前言

Android 应用由四大组件构成:Activity、Service、BroadcastReceiver 和 ContentProvider。每个组件均可选择“导出”,允许其他应用访问。此机制是 Android 进程间通信的基础,但一旦导出,组件将暴露给设备上所有已安装应用。Android 客户端安全问题多源于组件不当导出或导出后缺少访问控制。本文系统讲解组件导出的机制、潜在攻击面和审计方法。

第一章 组件导出机制详解

1.1 显式导出 vs 隐式导出

组件是否导出由 AndroidManifest.xml 中的 android:exported 属性控制。

  • 显式导出:明确声明 android:exported="true"
<activity android:name=".PaymentActivity" android:exported="true" />
  • 隐式导出:在 Android 12 (API 31) 之前,若组件声明了 <intent-filter> 但未显式设置 exported 属性,系统默认将其视为 exported="true"
<activity android:name=".ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

此默认规则是历史上许多组件被意外导出的主要原因。

1.2 android:exported 默认值规则

条件 Android 12 之前 (targetSdk < 31) Android 12+ (targetSdk >= 31)
有 intent-filter 且未声明 exported 默认 true (自动导出) 编译报错,必须显式声明
无 intent-filter 且未声明 exported 默认 false 默认 false
显式声明 exported="true" 导出 导出
显式声明 exported="false" 不导出 不导出

Android 12 的改动强制开发者明确意图,但系统预装应用或厂商定制组件(targetSdk 可能较低)仍可能存在隐式导出。

1.3 ContentProvider 的特殊规则

ContentProvider 的默认导出规则与其他三大组件不同:

targetSdk 版本 默认 exported 值
< 17 (Android 4.2 之前) true
>= 17 false
这意味着极老应用(targetSdk < 17)的 ContentProvider 默认导出。审计遗留系统时需留意。

第二章 权限保护机制

组件导出不等于完全暴露,Android 提供多层权限保护。

2.1 Manifest 层权限控制

在组件声明中通过 android:permission 属性直接指定访问所需权限。

<permission android:name="com.example.permission.ACCESS_SENSITIVE" android:protectionLevel="signature"/>
<service android:name=".SensitiveService" android:exported="true" android:permission="com.example.permission.ACCESS_SENSITIVE"/>

调用方需在其 Manifest 中声明并获取该权限,否则调用将被拒绝(SecurityException)。
ContentProvider 可细化读写权限:android:readPermissionandroid:writePermission

2.2 权限保护级别 (protectionLevel)

自定义权限的有效性取决于其 protectionLevel

protectionLevel 授予条件 安全性评估
normal 安装时自动授予,无需用户确认 。任何应用声明即可获得。
dangerous 需要用户运行时授权 。依赖于用户判断。
signature 必须与声明权限的应用使用相同证书签名 。第三方应用无法获得。
signatureOrSystem 相同签名或为系统预装应用

关键:仅用 normal 级别权限保护导出组件,保护力度极弱。

2.3 自定义权限的“抢注”风险

Android 自定义权限的 protectionLevel先安装的应用定义。
假设目标应用定义了一个 signature 级别权限:

<permission android:name="com.victim.permission.SENSITIVE" android:protectionLevel="signature"/>

若恶意应用先安装,并抢先定义同名权限为 normal 级别:

<permission android:name="com.victim.permission.SENSITIVE" android:protectionLevel="normal"/>
<uses-permission android:name="com.victim.permission.SENSITIVE"/>

则系统采用先安装者的定义(normal),恶意应用自动获得该权限。Android 12+ 对此有所缓解(同名权限冲突导致安装失败),但低版本仍存在风险。

2.4 代码层权限校验

Manifest 权限是“门卫”,代码层校验是内部检查。利用 Android Binder 机制记录的调用方信息(UID, PID)进行校验:

// 1. 检查调用者是否持有特定权限
if (checkCallingPermission("android.permission.DUMP") != PackageManager.PERMISSION_GRANTED) {
    throw new SecurityException("Permission denied");
}
// 2. 检查调用者 UID(系统进程UID通常<10000)
if (Binder.getCallingUid() >= 10000) {
    throw new SecurityException("Only system apps allowed");
}
// 3. 检查调用者包名
String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
if (!Arrays.asList(packages).contains("com.trusted.app")) {
    throw new SecurityException("Unauthorized caller");
}

审计要点:组件导出 + Manifest 无保护 + 代码无校验 = 常见漏洞成因。

2.5 权限保护效果演示

一个有权限保护的 SecureActivity(声明了系统签名权限 android.permission.MANAGE_USERS)与无保护组件形成对比。尝试外部启动会直接被系统拒绝:

adb shell am start -n com.demo.exportedcomponents/.SecureActivity
# 输出: java.lang.SecurityException: Permission Denial: ... requires android.permission.MANAGE_USERS

第三章 四大组件攻击面分析

3.1 Activity

攻击入口startActivity(), startActivityForResult()
可控参数:Intent 中的 extras(键值对数据)
典型攻击模式

模式 原理 危害
UI 内容注入 Activity 从 extras 读取字符串直接显示 伪造弹窗,钓鱼欺骗
Intent 重定向 Activity 从 extras 取出嵌套 Intent 后直接启动 以受害应用身份启动任意(包括未导出的)组件
WebView URL 注入 Activity 从 extras 读取 URL 并传给 WebView 加载 在受害应用上下文加载恶意网页
返回值劫持 恶意应用通过 startActivityForResult 获取敏感返回数据 敏感数据泄露

演示1:UI 内容注入

// VulnDisplayActivity.java
String title = getIntent().getStringExtra("title"); // 攻击者可控
String message = getIntent().getStringExtra("message"); // 攻击者可控
new AlertDialog.Builder(this).setTitle(title).setMessage(message).show();

攻击命令

adb shell am start -n com.demo.exportedcomponents/.VulnDisplayActivity --es title "安全警告" --es message "您的账户存在异常登录..."

攻击者可完全控制弹窗内容,进行钓鱼。

演示2:Intent 重定向

// VulnRedirectActivity.java
Intent nested = getIntent().getParcelableExtra("next_intent"); // 攻击者可控
if (nested != null) {
    startActivity(nested); // 漏洞:直接启动攻击者提供的 Intent
}

攻击命令(启动一个未导出的内部Activity):

adb shell am start -n com.demo.exportedcomponents/.VulnRedirectActivity --es target_package "com.demo.exportedcomponents" --es target_class "com.demo.exportedcomponents.InternalSecretActivity"

成功绕过 exported=false 的限制。

3.2 Service

攻击入口

  • Started Service: startService()
  • Bound Service: bindService()
    可控参数
  • Started Service: Intent extras
  • Bound Service: AIDL 接口的所有方法参数
    典型攻击模式
    模式 原理 危害
    未授权功能触发 通过 startService() 触发敏感后台操作 权限提升、数据篡改
    AIDL 方法滥用 绑定服务后,调用未授权校验的 AIDL 接口方法 数据泄露、功能滥用
    系统服务未授权访问 通过 ServiceManager 获取系统 Binder 服务代理 绕过系统权限模型

Bound Service 攻击面更大:建立连接后可持续调用方法。
演示:Started Service 未授权写文件

// VulnService.java
String filename = intent.getStringExtra("filename"); // 攻击者可控
String content = intent.getStringExtra("content"); // 攻击者可控
File file = new File(getFilesDir(), filename);
FileWriter writer = new FileWriter(file);
writer.write(content); // 漏洞:无校验,直接写入
writer.close();

攻击命令

adb shell am startservice -n com.demo.exportedcomponents/.VulnService --es filename "config.txt" --es content "恶意数据"
adb shell run-as com.demo.exportedcomponents cat /data/data/com.demo.exportedcomponents/files/config.txt
# 输出: 恶意数据

3.3 BroadcastReceiver

攻击入口sendBroadcast(), sendOrderedBroadcast()
可控参数:Intent 中的 action, extras
典型攻击模式

模式 方向 原理 危害
广播注入 攻击者 → 受害者 向导出 Receiver 发送伪造广播 触发敏感逻辑、命令执行
广播劫持 受害者 → 攻击者 注册高优先级 Receiver 拦截有序广播 窃取广播中的敏感数据
隐式广播窃听 受害者 → 攻击者 监听未设置包名的隐式广播 信息泄露

演示:广播注入

// VulnReceiver.java
public void onReceive(Context context, Intent intent) {
    String data = intent.getStringExtra("log_data"); // 攻击者可控
    Log.i(TAG, "Received data: " + data); // 漏洞:直接信任并处理
}

攻击命令

adb shell am broadcast -n com.demo.exportedcomponents/.VulnReceiver -a com.demo.exportedcomponents.ACTION_LOG --es log_data "injected_by_attacker"

应用会处理攻击者注入的数据。

3.4 ContentProvider

攻击入口ContentResolver.query(), insert(), update(), delete(), openFile(), call()
可控参数:URI, selection, selectionArgs, projection, 文件路径等。
典型攻击模式

模式 原理 危害
数据泄露 query() 无权限保护,直接返回数据 读取用户敏感数据
SQL 注入 selection/projection 参数未过滤直接拼接 SQL 读取任意表数据
路径遍历 openFile() 未过滤 ../ 等路径字符 读取应用沙箱外任意文件
call() 方法滥用 call() 方法执行敏感操作且无校验 功能滥用

演示:数据泄露
Provider 无任何权限保护:

<provider android:name=".VulnProvider" android:authorities="com.demo.exportedcomponents.provider" android:exported="true"/>

攻击命令

adb shell content query --uri content://com.demo.exportedcomponents.provider/users

直接返回所有用户数据(用户名、邮箱、手机号)。
路径遍历示例

public ParcelFileDescriptor openFile(Uri uri, String mode) {
    String path = uri.getLastPathSegment(); // 攻击者可控: 例如 "../../shared_prefs/secret.xml"
    File file = new File(getContext().getFilesDir(), path); // 漏洞:可能跳出沙箱
    return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}

第四章 Android 版本演进的影响

4.1 Android 12 (API 31): 强制声明 exported

核心改动targetSdkVersion >= 31 的应用,所有包含 <intent-filter> 的组件必须显式声明 android:exported 属性,否则安装失败。
意义:从根本上消除“隐式导出”。
注意

  • 系统预装应用可能不受此限制。
  • 已安装的旧版本应用不受影响。
  • 开发者可能为图省事全部设为 exported="true"

4.2 Android 13+:更严格的 Intent 过滤

  • 动态注册的 BroadcastReceiver 默认不接收外部应用广播,除非注册时显式指定 Context.RECEIVER_EXPORTED 标志。
    // 接收外部广播
    registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
    // 不接收外部广播
    registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED);
    
  • PendingIntent 的 mutability 要求更严格。

4.3 厂商定制 ROM 的额外攻击面

厂商定制 ROM(如 MIUI, EMUI, ColorOS)常添加大量自定义系统服务。这些服务通过 ServiceManager.addService() 注册,不受 Manifest 的 exported 属性约束

  • 攻击入口:任何应用可通过 ServiceManager.getService("service_name") 获取 Binder 代理并调用其方法。
  • 安全性:完全依赖于服务内部代码层的权限校验(如 checkCallingPermission, checkCallingUid)。
  • 危害:此类服务通常运行在 system_server 进程(UID 1000),一旦存在未授权访问,危害极大。

第五章 总结与审计要点

组件导出是 Android 安全的“门户”。审计核心在于检查“门是否该开”以及“开门后是否有守卫”。

审计 checklist

  1. 识别导出组件:检查 AndroidManifest.xml 中所有组件的 android:exported 属性。特别注意 Android 12 前的“隐式导出”和低版本 ContentProvider 的默认导出。
  2. 评估权限保护
    • 组件是否受 android:permission 保护?
    • 保护权限的 protectionLevel 是什么?normal 级别保护几乎无效。
    • 是否存在自定义权限“抢注”风险(针对低版本)?
  3. 检查代码层校验:对于导出的组件,检查其代码中是否对调用者身份(权限、UID、包名)进行了校验。
  4. 分析攻击面
    • Activity: 检查 Intent 参数是否被安全使用,是否存在 UI 注入、Intent 重定向。
    • Service: 检查 onStartCommand 逻辑及 AIDL 接口方法。
    • BroadcastReceiver: 检查广播数据是否被无条件信任。
    • ContentProvider: 检查查询、文件操作、call() 方法是否存在数据泄露、SQL 注入、路径遍历。
  5. 关注系统服务:在厂商定制 ROM 中,注意审计通过 ServiceManager 注册的自定义系统服务。
  6. 考虑 Android 版本:注意应用 targetSdkVersion,评估 Android 12/13 新规带来的安全影响。

核心安全原则最小权限原则。非必要不导出;若必须导出,则必须实施有效的权限保护(signature 级别或以上)和严格的代码层调用者校验。

相似文章
相似文章
 全屏