1. 课程导入

Android 是全球装机量最大的移动操作系统,其安全设计并非单一机制,而是由应用沙箱、权限模型、应用签名、SELinux、Verified Boot、密钥库等多层防线共同构成的纵深防御体系。在这套体系中,权限模型处在“应用与系统资源之间的第一道闸门”位置:它决定了应用能访问哪些数据、能调用哪些能力、以什么身份与系统服务交互。理解权限模型,是理解 Android 安全工程其他所有主题的前提。

一、Android 安全概况:从“开放”到“可控”

Android 的安全模型建立在 Linux 内核之上,每个应用默认运行在独立的 UID 与独立进程空间中,形成应用沙箱。沙箱解决了“应用之间默认隔离”的问题,但移动应用天然需要访问相机、位置、通讯录、存储、网络等共享资源,于是系统必须提供一套受控的授权机制,这就是权限模型存在的根本原因。

从 Android 6.0(API 23)开始,权限模型从“安装时一次性授权”演进为运行时权限;从 Android 10 开始,存储访问被分区存储(Scoped Storage)重构;Android 11 引入一次性授权与自动重置;Android 12 增加大致位置与精确位置分离;Android 13 将通知、媒体读取进一步细分为独立权限。这条演进主线说明:Android 权限模型持续朝着最小权限、用户可控、按需授权、可撤销的方向收敛。

核心结论:Android 安全不是“有没有权限”的单点判断,而是“沙箱隔离 + 权限授予 + 签名约束 + 内核强制访问控制 + 系统完整性校验”的叠加结果。权限模型是其中与开发者关系最紧密、也最容易被误用的一层。

二、权限模型为什么重要

权限模型的重要性可以从三个视角理解。

  • 用户视角:权限是隐私的可视化边界。用户通过权限授予与撤销,决定应用能否读取位置、照片、麦克风等敏感数据。
  • 开发者视角:权限声明与申请方式直接影响应用能否上架、能否在后台稳定运行、能否通过 Google Play 政策审核。错误声明高危权限或过度申请权限,会导致审核拒绝或用户流失。
  • 系统安全视角:权限是攻击面收敛的关键。攻击者一旦通过漏洞绕过权限检查,就可能实现越权访问、数据窃取或提权。因此权限校验点、系统服务入口、Binder 调用边界都是安全审计的重点。

下面这张图展示了 Android 权限模型在整体安全架构中的位置:应用层发起请求,框架层进行权限校验,内核层通过 UID、SELinux 等机制执行强制约束。

Android 权限模型在安全架构中的位置 应用层(App Layer) 应用进程 · 独立 UID · 独立沙箱 · AndroidManifest 权限声明 运行时权限申请 · 用户授权弹窗 · 权限组与授权状态 Binder IPC 调用 框架层(Framework Layer) PackageManagerService · ActivityManagerService · 权限校验入口 checkPermission() · enforcePermission() · AppOps 动态管控 系统调用 / 内核接口 内核层(Kernel Layer) Linux UID / GID · DAC 自主访问控制 · SELinux 强制访问控制 文件系统隔离 · 设备节点访问控制 · 能力(Capability)约束 权限模型:贯穿三层的授权与校验主线

三、核心原理:权限模型的三层校验

Android 权限校验通常发生在三个层面,理解这三层有助于定位“权限申请了却仍然失败”的问题。

  1. 声明层:应用在 AndroidManifest.xml 中通过 <uses-permission> 声明所需权限。普通权限安装即授予,危险权限必须运行时申请。
  2. 框架层:系统服务在关键 API 入口调用 checkPermission() 或 enforcePermission(),校验调用方 UID 是否被授予对应权限。
  3. 内核层:即使框架层放行,SELinux 仍可能基于进程域与文件标签拒绝访问,形成第二道强制约束。

一个典型的权限校验失败日志如下,可以看到框架层与内核层分别给出拒绝信息:

// 框架层拒绝:调用方缺少权限
java.lang.SecurityException: Permission Denial:
  starting Intent { act=android.intent.action.CALL } from pid=12345
  requires android.permission.CALL_PHONE

// 内核层拒绝:SELinux 策略不允许
avc: denied { read } for pid=12345 comm="app_process"
  scontext=u:r:untrusted_app:s0 tcontext=u:object_r:system_data_file:s0
  tclass=file permissive=0
排障提示:遇到权限问题时,先确认 Manifest 是否声明,再确认运行时是否已授予,最后用 adb shell dumpsys package <包名> 查看 grantedPermissions,并用 adb logcat | grep avc 检查是否存在 SELinux 拒绝。

四、案例场景:一个“位置权限”引发的三类问题

假设一个外卖类应用需要获取用户位置。开发团队可能遇到以下场景:

场景现象根因处理方式
未声明权限调用定位 API 直接抛 SecurityExceptionManifest 缺少 ACCESS_FINE_LOCATION补充声明并重新安装
未运行时申请API 返回空位置或权限回调被拒危险权限未走 requestPermissions 流程在合适时机发起运行时申请
后台定位受限应用退到后台后位置更新停止Android 10+ 后台定位需单独权限与前台服务申请 ACCESS_BACKGROUND_LOCATION 并声明前台服务类型

这个案例说明:权限问题往往不是“一个开关”,而是声明、申请、使用时机、系统版本差异共同作用的结果。只盯着代码中的一行申请语句,很容易误判。

五、常见误区与复盘

  • 误区一:权限申请越多越保险。过度申请高危权限会触发应用商店审核风险,也会降低用户信任。应遵循最小权限原则。
  • 误区二:用户拒绝一次就永久拒绝。Android 允许用户多次拒绝,开发者应提供解释性 UI,而不是反复弹窗骚扰。
  • 误区三:权限授予等于数据可随意使用。即使获得权限,仍需遵守分区存储、后台限制、隐私政策与平台合规要求。
  • 误区四:只关注应用层,忽略系统层。很多“权限正常但访问失败”的问题,根因在 SELinux、文件标签或系统服务策略。
复盘要点:权限模型的学习不能停留在 API 调用层面。要同时建立“应用声明—框架校验—内核强制”的完整链路认知,才能在真实排障和安全审计中快速定位问题层级。

六、课程目标与学习路径

本课程围绕“Android 权限模型与系统安全工程”展开,目标是让学习者具备三项能力:

  1. 理解能力:掌握权限分类、权限组、运行时授权、签名权限、AppOps、SELinux 等核心概念及其关系。
  2. 实践能力:能够正确声明与申请权限,处理拒绝与撤销场景,使用 adb、dumpsys、logcat 等工具定位权限问题。
  3. 工程能力:能够从安全架构视角评估权限设计,识别越权风险,理解系统完整性校验与密钥库等配套机制。