安全机制(Permission与SELinux):Android权限模型、SELinux策略与上下文、应用沙箱与UID隔离、Keystore与加密

聊到Android安全,很多人第一反应就是「权限弹窗」。其实这只是冰山一角。我做了这么多年Framework,见过太多应用因为权限问题崩溃,也见过厂商因为SELinux策略没写好导致系统起不来。今天咱们就把这套安全体系拆开看看。

一、Android权限模型:普通、危险、签名权限

Android的权限模型,说白了就是一套「谁可以干什么」的规则。从API 23开始,Google把权限分成了几类,咱们一个个说。

1. 普通权限(Normal Permissions)

这类权限不涉及用户隐私,系统直接授予。比如INTERNETACCESS_NETWORK_STATE。你在AndroidManifest.xml里声明了,安装时就自动授权,用户甚至感知不到。

2. 危险权限(Dangerous Permissions)

这类权限涉及用户隐私数据,比如读取联系人、访问位置、使用相机。系统会在运行时弹窗让用户确认。嗯,这里要注意:危险权限是分组管理的。比如你申请了READ_CONTACTS,如果用户同意了,同组的WRITE_CONTACTS也会自动获得授权。

权限分组示例:

  • CALENDAR:READ_CALENDAR, WRITE_CALENDAR
  • CAMERA:CAMERA
  • CONTACTS:READ_CONTACTS, WRITE_CONTACTS, GET_ACCOUNTS
  • LOCATION:ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION
  • PHONE:READ_PHONE_STATE, CALL_PHONE, READ_CALL_LOG, ADD_VOICEMAIL, USE_SIP

3. 签名权限(Signature Permissions)

这个有意思。只有使用相同签名的应用才能获得该权限。我在项目中遇到过这样的场景:系统应用和厂商预装应用之间需要共享数据,但又不想暴露给第三方。这时候签名权限就派上用场了。说白了,就是「自己人才能用」。

我的经验:定义签名权限时,记得在AndroidManifest.xml中设置protectionLevel="signature"。我曾经见过有人误写成signatureOrSystem,结果系统应用也能访问,导致数据泄露风险。

<!-- 定义签名权限 -->
<permission
    android:name="com.example.MY_SIGNATURE_PERMISSION"
    android:protectionLevel="signature" />

<!-- 声明使用 -->
<uses-permission android:name="com.example.MY_SIGNATURE_PERMISSION" />

二、SELinux策略与上下文:强制访问控制的守护者

你想想看,传统的Linux权限模型(DAC)只检查UID/GID。如果应用拿到了root权限,理论上可以为所欲为。SELinux引入了强制访问控制(MAC),每个进程、每个文件都有安全上下文,规则由策略文件定义。

我记得有一次调试系统服务,发现某个进程总是无法访问一个设备节点。查了半天,原来是SELinux策略没写对。嗯,从那以后我养成了习惯:遇到权限问题,先看dmesg | grep avc

SELinux上下文格式

每个安全上下文由四部分组成:user:role:type:level。对于Android来说,最核心的是type

# 查看进程上下文
ps -Z

# 查看文件上下文
ls -Z /data/data/com.example.app

常见类型示例:

类型描述
u:r:init:s0init进程
u:r:system_server:s0系统服务进程
u:r:untrusted_app:s0第三方应用
u:object_r:app_data_file:s0应用私有数据文件

SELinux策略编写

策略文件通常放在device/<vendor>/<device>/sepolicy/目录下。写策略其实就是在定义「谁可以访问谁」。

# 允许system_app读取某个设备节点
allow system_app my_device:chr_file read;

# 允许untrusted_app创建socket
allow untrusted_app self:socket create_socket_perms;

避坑指南:我曾经在项目中遇到一个问题:添加了新的系统服务后,SELinux一直报avc denied。排查后发现是忘了给新服务定义type和domain。记住:每个新服务都需要在sepolicy中声明typedomain,并编写对应的.te文件。

三、应用沙箱与UID隔离

Android的沙箱机制,说白了就是「每个应用一个独立空间」。安装应用时,系统会分配一个唯一的UID。这个UID决定了应用能访问哪些资源。

你想想看,应用A的UID是10001,应用B的UID是10002。它们各自的数据目录是/data/data/com.example.a/data/data/com.example.b。没有特殊权限,谁也看不到谁的数据。

UID分配规则:

  • 系统应用:UID从1000开始(如system为1000,radio为1001)
  • 普通应用:UID从10000开始
  • 共享UID:如果多个应用使用相同签名并声明sharedUserId,它们共享同一个UID

我在项目中遇到过共享UID的坑。两个应用声明了相同的sharedUserId,但签名不一致,结果安装时报错INSTALL_FAILED_SHARED_USER_INCOMPATIBLE。嗯,这个错误很常见,排查起来也简单:检查签名是否一致即可。

四、Keystore与加密

Android Keystore系统,说白了就是一个「硬件级保险箱」。密钥一旦生成,就存储在TEE(可信执行环境)或SE(安全元件)中,应用只能通过API使用密钥,无法直接读取密钥内容。

Keystore的核心能力

  • 密钥生成:在安全硬件中生成密钥对
  • 密钥使用:签名、验证、加密、解密
  • 密钥保护:密钥永远不会离开安全硬件
  • 用户认证绑定:可以要求使用密钥前必须验证指纹或PIN码
// 生成RSA密钥对并存入Keystore
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
    KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
    "my_key_alias",
    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
    .setDigests(KeyProperties.DIGEST_SHA256)
    .setUserAuthenticationRequired(true) // 需要用户认证
    .build();
keyPairGenerator.initialize(spec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();

我的建议:敏感数据(如Token、密码)不要直接存SharedPreferences。用EncryptedSharedPreferences,它底层就是Keystore + AES加密。我在项目中用过,既安全又方便。

加密实践

Android支持多种加密算法,但要注意:不要自己实现加密逻辑。用现成的Android Security LibraryJetpack Security

// 使用EncryptedSharedPreferences
MasterKey masterKey = new MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build();

SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
    context,
    "secure_prefs",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

// 写入加密数据
sharedPreferences.edit().putString("token", "my_secret_token").apply();

五、知识体系总览

下面这张图是我梳理的Android安全机制核心脉络。你可以看到,从应用层到内核层,每一层都有对应的安全措施。

Android安全机制知识体系 应用层安全 权限模型(普通/危险/签名) | 应用沙箱 | UID隔离 Framework层安全 PackageManagerService | ActivityManagerService | PermissionController 系统服务层安全 SELinux策略 | 安全上下文 | 强制访问控制(MAC) 内核与硬件层安全 Keystore | TEE/SE | 硬件加密 | 文件系统加密 用户空间 内核空间

这张图从下往上看,底层是硬件加密和Keystore,往上是SELinux强制访问控制,再往上是Framework层的权限管理,最顶层是应用沙箱和UID隔离。每一层都环环相扣,缺一不可。

最后提醒一句:安全不是某个模块的事,而是一个系统工程。我见过太多开发者只关注权限弹窗,却忽略了SELinux策略和Keystore的使用。记住:攻击者往往从最薄弱的环节入手。


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