2. Android安全架构基础:安全模型、沙箱与权限

Android的安全架构,说白了就是三个核心概念撑起来的:安全模型、沙箱机制和权限系统。这三个东西互相嵌套,构成了整个Android生态的安全基座。我做密码SDK适配,实际打交道最多的就是它们三个。

2.1 Android安全模型

Android的安全模型,你可以理解为“最小权限原则”的极致体现。什么意思呢?每个App默认只能干自己的事,想碰别人地盘,必须明确申请。

整个模型分三层:

  • 内核层(Linux Kernel):负责进程隔离、文件权限、内存保护。这是最底层的物理隔离。
  • 框架层(Framework):实现权限声明、IPC(进程间通信)安全检查、签名验证。我们平时调用的API多半在这一层做权限校验。
  • 应用层(Application):用户能感知的部分——安装时弹出的权限列表、运行时权限弹窗。嗯,这层最烦人但也最容易出问题。

我个人习惯把这三层画成一张图,方便理解数据流和边界。

Android 安全模型三层架构 应用层 (Application Layer) 权限声明 · 运行时弹窗 · 签名验证 这是我们(App开发者)直接接触的层 框架层 (Framework Layer) IPC安全检查 · 权限校验 · Binder通信 密码SDK的核心适配点在这一层 内核层 (Linux Kernel) 进程隔离 · 文件权限 · SELinux · 内存保护 物理隔离基石,App无法绕过 ⚠️ 核心原则 最小权限 隔离与保护 签名验证

2.2 沙箱机制

沙箱机制是Android安全真正的基石。说白了,就是每个 App 都有一个自己的小隔间,出不去,也进不来。

每个 App 在安装时,系统会分配一个唯一的 Linux 用户 ID(UID),并在内核层创建一个专属进程。这个进程有独立的文件目录、内存空间、资源配额。App 之间默认完全隔离——你没法直接读另一个 App 的文件,也不能发信号给它的进程。

举个例子:我记得有一次排查一个奇怪的安全问题——密码SDK的持久化密钥文件找不到了。后来才发现,同一个 App 在 Android 虚拟机里跑了两个不同的 UID,修改了文件存储路径。其实不是丢失,而是沙箱隔离导致另一个 UID 进程根本读不到数据。从那以后我做密码SDK测试,一定会检查 UID 归属。

沙箱机制有几个核心特点:

  • 进程级隔离:每个 App 跑在独立进程,内核通过 PID/UID 控制资源访问。
  • 文件级隔离:每个 App 的默认数据目录(/data/data/包名)只有自己可以读写,其他进程无权限。
  • 内存级隔离:App 不能直接访问其他进程的内存空间。
  • IPC 受控:跨进程通信必须通过 Binder 等机制,且受权限检查和 SELinux 策略约束。

你想想看,沙箱机制对密码SDK有什么影响?简单说就是:密码数据默认只能被本App自己访问。如果要做跨App的密码共享(比如统一密码管理器),那就得主动设计安全的 IPC 通道,比如通过 ContentProvider 或 Service 暴露接口,并配合签名验证和权限管控。

2.3 权限系统

沙箱把世界切成了一个个孤岛,但总得有个办法让岛和岛之间能安全地交通。这个办法就是权限系统。

2.3.1 权限分类

Android的权限体系经历了多次演变。我个人把这个过程总结为三个阶段:

阶段 核心机制 代表版本
安装时授权 用户在安装时统一授权,不能单独控制 Android 5.x 及以下
运行时授权 App运行时动态弹窗,用户可单独拒绝 Android 6.0+
精细化管控 引入“仅在使用中”授权、权限分组、后台权限特殊处理 Android 11+

从权限本身的级别来看,分为:

  • 普通权限(Normal):系统自动授予,不涉及用户敏感数据,如 INTERNET。
  • 危险权限(Dangerous):涉及用户隐私(相机、位置、联系人等),需要运行时弹窗申请。
  • 签名权限(Signature):只有同签名的 App 才能获取,常用于系统内部接口或安全敏感接口。
  • 特权权限(Privileged / System):只授予系统级 App,普通开发者碰不到。

这里有个关键判断:密码SDK里涉及 FLAG_SECURE、KeyStore、BiometricPrompt 等接口时,属于“普通权限”范畴,不需要申请危险权限。但注意:如果密码SDK要访问设备状态(如读取手机号、IMEI),那可能涉及 READ_PHONE_STATE,这个权限在 Android 10+ 被严格管控,且 Android 14 后基本被废弃。

2.3.2 运行时权限适配

Android 6.0 引入运行时权限后,所有危险权限都需要在代码中动态申请。这直接影响了密码SDK的接入方式。

我曾经在开发一款密码SDK时,遇到过一个问题:App 在 Android 10 上运行时,死活拿不到 指纹认证 的授权。排查了半天才发现,App 没有动态申请 USE_BIOMETRIC 权限——虽然它是普通权限,但有部分厂商的定制ROM把它标记为危险权限,需要动态申请。

避坑指南:密码SDK适配时,我个人建议尽量不申请任何危险权限。如果真的需要(比如用摄像头扫描二维码或人脸识别),请遵循以下步骤:

  1. 优先使用系统 API 的权限豁免路径(如 BiometricPrompt 的 USE_BIOMETRIC 自动授权)。
  2. 如果必须申请,在 AndroidManifest.xml 中声明,并在 SDK 初始化时提醒宿主 App 申请。
  3. 做好权限被拒绝的回退方案。

2.3.3 权限与密码SDK的适配关系

我把密码SDK开发中常见的权限场景整理成了下表:

功能场景 涉及权限 权限类型 适配要求
屏幕锁定保护 SECURE_SETTINGS 系统/特权 通常 App 无法获取,改用 FLAG_SECURE
生物识别认证 USE_BIOMETRIC 或 USE_FINGERPRINT 普通/部分厂商标记为危险 动态检测权限+回退方案
读取Android KeyStore 无(系统密钥存储不需额外权限) — KeyStore 直接使用
读取写入密钥文件(外部存储) READ/WRITE_EXTERNAL_STORAGE 危险 尽量禁止;必须则动态申请 + Android 10+ 适配范围存储
密码自动填充服务 BIND_AUTOFILL_SERVICE 签名/系统 仅系统 App 可用,普通 App 不走此路径

每次我在项目中看到密码SDK硬编码写死了某个路径去读取外部文件,或者在 AndroidManifest 里直接声明了危险权限却没有做运行时申请,就知道后面肯定要踩坑。

⚠️ 特别警告:从 Android 11 开始,系统对 MANAGE_EXTERNAL_STORAGE 和 QUERY_ALL_PACKAGES 等权限做了严格限制。如果密码SDK想要扫描设备上的其他 App 来识别密码输入框,或者想要访问所有文件,不仅需要权限声明,还得向 Google 申请特殊审批——普通 App 几乎拿不到。我建议密码SDK开发者放弃这种思路,改用 AutofillFramework 或 AccessibilityService 的合规路径。

2.4 三个机制如何联动

搞懂了沙箱和权限,再回头看安全模型。这三者的关系其实挺清楚的:

  1. 沙箱 负责物理隔离,保证App不能乱来。
  2. 权限系统 负责规则授权,允许App在指定范围内协作。
  3. 安全模型 是顶层设计,把前面的隔离和授权统一起来,形成完整的安全策略。

具体到我们密码SDK的适配,核心就是:不要打破沙箱边界,不要滥用权限,时刻遵守最小权限原则。

嗯,这些基础概念看起来很简单,但我见过太多 App 因为忽视这些机制导致的安全问题——密码明文存在外部存储、密钥文件被其他 App 读取、生物认证调用失败……几乎全是沙箱和权限适配没做对。