22、Android安全机制:沙箱机制、权限模型、签名验证、SELinux与加密存储

聊到Android安全,很多人第一反应就是「权限弹窗」。其实这只是冰山一角。我做了这么多年系统层开发,可以负责任地说:Android的安全体系,是一套从内核到应用层的立体防御。今天咱们就把这五个核心机制掰开揉碎了讲清楚。

一、沙箱机制:每个App都是「坐牢」的

沙箱,说白了就是给每个App画个圈。你在圈里怎么折腾都行,想出圈?门儿都没有。

Android的沙箱基于Linux的用户隔离机制。每个App安装时,系统会分配一个独立的UID(用户ID)。这个UID就是App的「身份证」,也是它的「监狱编号」。

核心原理:每个App运行在独立的进程中,拥有独立的UID,默认无法访问其他App的资源。

我记得有一次排查一个诡异问题:两个App明明都是同一个开发者,却无法共享数据文件。后来一看,它们用了不同的sharedUserId。嗯,这就是沙箱在起作用。

沙箱的隔离层级是这样的:

  • 进程隔离:每个App跑在独立的Linux进程中
  • 文件系统隔离:/data/data/包名 目录只有该App能访问
  • 资源隔离:CPU、内存等资源按进程分配
  • 网络隔离:默认情况下,App不能监听端口(除非声明权限)

避坑指南:我曾经见过一个团队,为了「方便」让多个App共享数据,把sharedUserId设成一样的。结果其中一个App被攻破,所有App的数据都泄露了。沙箱的隔离性一旦被打破,安全防线就形同虚设。

二、权限模型:从「全有」到「按需」

Android的权限模型经历过一次大变革。Android 6.0之前,安装时一次性授权,之后App想干嘛就干嘛。这就像你请人进家门,他顺手把你家钥匙配了一把。

6.0之后改成了运行时权限。App要用摄像头?弹窗问用户。要用位置?再弹一次。用户随时可以撤销。

权限分四个等级:

等级 说明 示例
Normal 自动授予,不弹窗 INTERNET、VIBRATE
Dangerous 运行时弹窗,用户决定 CAMERA、ACCESS_FINE_LOCATION
Signature 只有相同签名的App才能获取 BIND_ACCESSIBILITY_SERVICE
Privileged 系统级权限,普通App拿不到 INSTALL_PACKAGES

你想想看,为什么有些App明明没申请权限,却能读取你的设备信息?那是因为它们用了「联合权限」——比如获取了READ_PHONE_STATE,就能间接拿到IMEI。嗯,这里要注意,权限的「组合拳」有时候比单个权限更危险。

注意:权限不是越多越好。我见过一个计算器App申请了读取联系人权限,这明显不合理。用户看到这种App,第一反应就是卸载。

三、签名验证:App的「身份证」

签名机制,说白了就是给App盖个章。这个章是开发者私钥盖的,系统用公钥验证。如果章被篡改,系统直接拒绝安装。

签名的作用有三点:

  • 身份认证:证明这个App是你开发的
  • 完整性校验:确保APK没有被篡改
  • 信任传递:相同签名的App可以共享UID和权限

Android支持两种签名方案:

  • v1签名(JAR签名):对APK内的每个文件签名,但META-INF目录本身不签名。这就留下了「漏洞」——攻击者可以删除META-INF目录,然后重新打包。
  • v2签名(APK签名方案v2):对整个APK文件进行签名,包括META-INF。篡改任何字节都会导致签名失效。
  • v3签名:在v2基础上增加了「密钥轮换」支持,可以更换签名密钥而不丢失信任链。

个人经验:我曾经接手过一个项目,之前的开发者把签名密钥文件(.jks)直接提交到了Git仓库。结果整个团队都能拿到密钥,签名验证形同虚设。密钥文件一定要放在安全的地方,最好用硬件加密模块存储。

四、SELinux:给系统加把「锁中锁」

SELinux,全称是Security-Enhanced Linux。它给Android加了一层「强制访问控制」(MAC)。

传统的Linux权限模型是「自主访问控制」(DAC)——文件所有者可以决定谁能访问。但问题是,如果root用户被攻破,整个系统就完蛋了。

SELinux的核心理念是:即使你是root,也不能为所欲为。每个进程都有一个「安全上下文」,系统根据预定义的策略决定进程能做什么。

举个例子:

# 查看进程的安全上下文
ps -Z

# 输出示例:
u:r:untrusted_app:s0:c15,c256,c512,c768  # 普通App
u:r:system_server:s0                      # 系统服务
u:r:init:s0                               # init进程

每个安全上下文由四部分组成:用户(user)、角色(role)、类型(type)、安全级别(level)。其中「类型」是最关键的,它决定了进程能访问哪些资源。

我记得有一次,一个系统服务总是报「权限拒绝」的错误。查了半天,发现是SELinux策略没写对。那个服务需要访问一个设备节点,但策略里没放行。加上一条allow规则,问题就解决了。

调试技巧:当遇到SELinux拒绝时,可以用dmesg | grep avc查看拒绝日志。日志里会告诉你哪个进程想访问什么资源,以及需要什么样的策略规则。

五、加密存储:数据在「保险箱」里

加密存储,就是把数据变成一堆乱码。即使攻击者拿到了存储芯片,也读不出原始数据。

Android的加密存储分两层:

  • 文件级加密(FBE):每个文件用不同的密钥加密。Android 7.0引入,支持「直接启动」模式——设备刚开机时,只有少数核心应用能访问加密数据。
  • 全盘加密(FDE):整个数据分区用一个密钥加密。Android 5.0引入,但有个缺点:设备重启后,必须输入密码才能访问所有数据。

我个人更推荐FBE。为什么?因为FBE允许在设备刚开机时,闹钟App就能响铃,电话App就能接听。而FDE必须等用户输入密码后才能工作。

加密存储的密钥管理是这样的:

  1. 设备启动时,硬件安全模块(如TEE或StrongBox)生成一个「主密钥」
  2. 主密钥用用户的PIN码/指纹/人脸解锁
  3. 每个文件或目录使用独立的「文件密钥」加密
  4. 文件密钥用主密钥加密后存储

注意:加密不是万能的。如果App在运行时把解密后的数据写到了临时文件,或者通过日志输出,那加密就白费了。我曾经见过一个App,把用户密码解密后直接打印到Logcat里。嗯,这操作真是让人哭笑不得。

知识体系总览

下面这张图,把Android安全机制的五个核心模块串起来了。你可以看到它们是如何分层协作的:

Android安全机制知识体系 应用层 App运行在沙箱中,通过权限模型申请资源 框架层 权限管理、签名验证、加密存储API 系统服务层 PackageManagerService、ActivityManagerService、KeyStore 内核层 SELinux策略、文件加密(FBE/FDE)、进程隔离 硬件安全模块(TEE / StrongBox) 沙箱机制 权限模型 签名验证 SELinux 加密存储

从这张图可以看得很清楚:安全不是某一个模块的事,而是从硬件到应用层层设防。每一层都有它的职责,每一层都不能出问题。

好了,关于Android安全机制的五个核心模块,今天就聊到这儿。这些东西我在实际项目中反复踩过坑,也反复受益。希望你能把这些机制理解透,而不是停留在「知道有这回事」的层面。毕竟,安全这东西,不出事则已,一出事就是大事。


公众号:蓝海资料掘金营,微信deep3321