一、AOSP安全架构概览:Android安全模型演进、安全子系统组成、SELinux在Android中的角色
各位同学,咱们今天聊聊Android安全架构的底子。说实话,我刚入行那会儿,觉得Android安全就是加个锁、设个密码。后来被现实狠狠教育了一顿——有一次客户设备被root了,数据全丢,我才真正开始啃AOSP安全源码。嗯,今天就把这些经验掰开揉碎了讲给你听。
1.1 Android安全模型的演进之路
Android安全模型不是一天建成的。它经历了几个关键阶段,我把它总结成一张表,方便你对照理解:
| 版本 | 核心安全特性 | 我的个人感受 |
|---|---|---|
| Android 1.0 - 2.3 | 基于UID的沙箱、权限声明 | 说白了就是「各扫门前雪」,每个App一个UID,互不干扰 |
| Android 4.0 - 4.4 | SELinux初步集成( enforcing模式可选) | 我记得当时很多厂商直接关掉,因为策略太严,兼容性问题一堆 |
| Android 5.0 | SELinux强制 enforcing 模式 | 这是转折点。我有个项目因为没适配SELinux策略,系统服务直接起不来 |
| Android 6.0+ | 运行时权限、密钥认证、硬件-backed安全 | 用户终于可以拒绝权限了,开发者开始头疼了 |
| Android 10+ | 分区加密、TEE、BiometricPrompt | 现在连指纹数据都走TEE,想偷?门都没有 |
为什么会这样演进?你想想看,早期Android就是个「开放」系统,谁都能装App。但开放带来的就是恶意软件泛滥。Google不得不一步步收紧权限,从「信任所有App」变成「默认不信任任何App」。说白了,安全就是一场攻防博弈的升级战。
1.2 安全子系统组成:不只是SELinux
很多人以为Android安全就是SELinux,其实不然。我习惯把Android安全体系拆成四个层次,就像盖房子一样:
- 内核层安全:Linux内核的自主访问控制(DAC)、命名空间、seccomp。这是地基,但地基不够硬——DAC太容易被绕过。
- 框架层安全:权限管理、包管理器、Intent校验。嗯,这里有个坑:Intent隐式发送容易被劫持,我踩过。
- 应用层安全:应用沙箱、签名验证、运行时权限。每个App默认只能用自己的UID,想访问别人?得申请权限。
- 硬件层安全:TEE(可信执行环境)、KeyStore、指纹/人脸认证。这是最后一道防线,硬件隔离,软件攻不破。
重点来了:SELinux在Android中扮演的角色,就是「强制访问控制」(MAC)。它弥补了DAC的不足——即使你是root用户,没有SELinux策略允许,你也干不了坏事。我在项目中见过太多人以为「root了就能为所欲为」,结果被SELinux教做人。
1.3 SELinux在Android中的角色:从「可选」到「必选」
SELinux最初是NSA开发的,后来被Google移植到Android。它的核心思想很简单:默认拒绝一切,只允许明确授权的操作。
在Android中,SELinux主要干三件事:
- 进程隔离:每个进程(包括系统服务)都有安全上下文。比如
init进程是u:r:init:s0,system_server是u:r:system_server:s0。它们之间不能随便通信。 - 文件访问控制:每个文件也有安全上下文。比如
/data/system目录只有system_server能写,其他进程碰都别想碰。 - 系统调用过滤:通过seccomp限制进程能调用的系统调用。比如Chrome渲染进程只能调用白名单里的syscall,想搞缓冲区溢出?没门。
我举个例子你就明白了。假设你写了个App,想读取 /data/system/packages.xml:
// 普通App尝试读取系统文件
File file = new File("/data/system/packages.xml");
FileInputStream fis = new FileInputStream(file);
// 结果:Permission denied
// 即使你的App有root权限,SELinux也会阻止
// 因为App的安全上下文是 u:r:untrusted_app:s0
// 而文件的安全上下文是 u:object_r:system_data_file:s0
// 策略里没有允许 untrusted_app 读 system_data_file
避坑指南:我曾经在移植AOSP到新设备时,因为SELinux策略没写对,导致WiFi服务启动失败。查了两天才发现是 wpa_supplicant 进程缺少对 wlan0 网络接口的访问权限。所以,移植时一定要先跑一遍 avc_denied 日志,看看哪些权限被拒绝了。
1.4 安全策略文件结构:你该从哪里下手?
如果你要移植或修改SELinux策略,先搞清楚文件结构。AOSP中SELinux策略文件主要分布在:
system/sepolicy/:通用策略,所有设备共享device/<vendor>/<device>/sepolicy/:设备特定策略,你主要改这里external/selinux/:SELinux用户空间工具和库
常见的文件类型:
| 文件后缀 | 作用 | 示例 |
|---|---|---|
.te |
类型强制规则,定义安全上下文和访问规则 | untrusted_app.te |
.fc |
文件上下文,定义文件路径的安全上下文 | file_contexts |
.genfs_contexts |
伪文件系统(如proc、sysfs)的安全上下文 | genfs_contexts |
mac_permissions.xml |
应用签名到secontext的映射 | 决定App属于哪个domain |
警告:千万不要直接修改 system/sepolicy/ 下的通用策略!我见过有人改了 untrusted_app.te 给所有App开了写外部存储的权限,结果Google CTS测试直接挂掉。正确的做法是在设备策略里添加 neverallow 规则覆盖,或者用 allow 规则补充。
1.5 实战视角:如何快速定位SELinux问题?
移植过程中,你大概率会遇到SELinux拒绝日志。别慌,我教你一招:
# 抓取SELinux拒绝日志
adb logcat -b events | grep "avc: denied"
# 或者从kernel log看
adb shell dmesg | grep "avc: denied"
# 典型输出:
# type=1400 audit(0.0:123): avc: denied { read } for pid=1234 comm="my_app" name="secret.txt" dev="mmcblk0p25" ino=5678 scontext=u:r:untrusted_app:s0 tcontext=u:object_r:system_data_file:s0 tclass=file permissive=0
看到这个日志,你就能定位到:
- 谁(
untrusted_app) - 想干什么(
read) - 对谁(
system_data_file) - 被拒绝了
然后你只需要在设备策略里加一条 allow 规则:
# 在 device/<vendor>/<device>/sepolicy/untrusted_app.te 中添加
allow untrusted_app system_data_file:file read;
嗯,这里要注意:别随便加 allow 规则,想清楚这个App真的需要读这个文件吗?我见过有人为了省事,直接 allow * *:* *,那跟关掉SELinux有什么区别?
小结
今天咱们把Android安全架构的骨架搭起来了。从DAC到MAC,从可选到强制,SELinux在Android中扮演着「最后一道防线」的角色。下一章我会带你手把手写一个完整的SELinux策略文件,从零开始适配一个新设备。到时候你会明白,为什么我说「SELinux策略写得好,加班少一半」。
记住:安全不是功能,是习惯。每次加一条 allow 规则前,多问自己一句——「真的需要吗?」