2. Android安全架构基础:安全模型、沙箱与权限
Android的安全架构,说白了就是三个核心概念撑起来的:安全模型、沙箱机制和权限系统。这三个东西互相嵌套,构成了整个Android生态的安全基座。我做密码SDK适配,实际打交道最多的就是它们三个。
2.1 Android安全模型
Android的安全模型,你可以理解为“最小权限原则”的极致体现。什么意思呢?每个App默认只能干自己的事,想碰别人地盘,必须明确申请。
整个模型分三层:
- 内核层(Linux Kernel):负责进程隔离、文件权限、内存保护。这是最底层的物理隔离。
- 框架层(Framework):实现权限声明、IPC(进程间通信)安全检查、签名验证。我们平时调用的API多半在这一层做权限校验。
- 应用层(Application):用户能感知的部分——安装时弹出的权限列表、运行时权限弹窗。嗯,这层最烦人但也最容易出问题。
我个人习惯把这三层画成一张图,方便理解数据流和边界。
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适配时,我个人建议尽量不申请任何危险权限。如果真的需要(比如用摄像头扫描二维码或人脸识别),请遵循以下步骤:
- 优先使用系统 API 的权限豁免路径(如
BiometricPrompt的USE_BIOMETRIC自动授权)。 - 如果必须申请,在
AndroidManifest.xml中声明,并在 SDK 初始化时提醒宿主 App 申请。 - 做好权限被拒绝的回退方案。
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 三个机制如何联动
搞懂了沙箱和权限,再回头看安全模型。这三者的关系其实挺清楚的:
- 沙箱 负责物理隔离,保证App不能乱来。
- 权限系统 负责规则授权,允许App在指定范围内协作。
- 安全模型 是顶层设计,把前面的隔离和授权统一起来,形成完整的安全策略。
具体到我们密码SDK的适配,核心就是:不要打破沙箱边界,不要滥用权限,时刻遵守最小权限原则。
嗯,这些基础概念看起来很简单,但我见过太多 App 因为忽视这些机制导致的安全问题——密码明文存在外部存储、密钥文件被其他 App 读取、生物认证调用失败……几乎全是沙箱和权限适配没做对。