在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、前置思考

1.1 权限是系统安全的"守门员"

一个恶意应用能读到另一个应用的数据吗?能偷偷拍照吗?能修改系统设置吗?——全看权限校验机制:

权限校验的本质: 每次敏感操作前,问一句"你有资格吗?"

谁(主体: 应用进程)→ 做什么(操作: 读文件/拍照)
→ 对什么(客体: 目标资源)→ 依据什么(权限模型)

1.2 权限校验失效的灾难

❌ UID 可伪造 → 恶意应用冒充系统进程 → 任意读写
❌ SELinux 策略漏洞 → 应用越权访问系统数据
❌ CAP 滥用 → 普通应用获得管理员能力
❌ 校验链断裂 → 有一个环节没查 → 全盘失守

1.3 鸿蒙/OpenHarmony 权限体系

三层权限模型:
① UID/GID 权限模型: 进程身份(你是谁)
② SELinux MAC 强制访问控制: 安全策略(你能做什么)
③ CAP 能力机制: 特权能力(你有哪把钥匙)

校验调用链:
  应用请求 → 权限管理服务 → UID 校验 → SELinux 策略 → CAP 检查 → 放行/拒绝

二、核心原理

2.1 UID/GID 权限模型

UID (User ID): 进程身份标识
  普通应用: 10000 + 应用索引(如 10023)
  系统进程: 0~999(root=0, system=1000)

GID (Group ID): 进程所属组
  补充组: 提供共享权限(如 inet 组可联网)

隔离机制:
  每个应用进程用独立 UID 运行
  → 文件系统按 UID 判权限
  → 进程间不能随意互相访问(沙箱基础)

2.2 SELinux 强制访问控制(MAC)

SELinux: 内核级安全策略(比 DAC 更强)

DAC (自主访问): 文件属主自己决定谁能访问
  → 缺点是: root 用户无所不能(拦不住恶意 root)

MAC (强制访问): 系统策略统一决定
  → 即使 root 也要过策略
  → 恶意进程无法绕过

SELinux 判定:
  主体(进程) × 客体(文件/资源) × 操作(读/写/执行)
  → 查安全策略表 → allow/deny

2.3 CAP 能力机制

传统 Linux: 只有"root/非 root"二元
  → root 能做一切(太危险)

CAP 机制: 将 root 权限拆成 40+ 种能力
  CAP_NET_ADMIN: 网络管理
  CAP_SYS_TIME: 修改系统时间
  CAP_KILL: 杀死任意进程
  CAP_SYS_BOOT: 重启系统
  ...

原则: 最小特权 — 进程只需要哪几个能力就给哪几个
  普通应用: 无能力
  系统服务: 按需授予特定 CAP

三、源码/API 深度解析

3.1 权限校验调用链(应用层 → 内核)

import { abilityAccessCtrl } from '@kit.AbilityKit';
import { common } from '@kit.AbilityKit';

// 应用层权限申请 → 系统校验链
async function requestAndVerify(): Promise<void> {
  const context = getContext(this) as common.UIAbilityContext;
  const atManager = abilityAccessCtrl.createAtManager();

  // 1. 申请权限(弹出系统授权框)
  const result = await atManager.requestPermissionsFromUser(
    context, ['ohos.permission.CAMERA']
  );
  // result: { authResults: [0=授予, -1=拒绝] }

  // 2. 校验权限(每次使用前检查)
  const grantStatus = await atManager.checkAccessToken(
    atManager.getTokenID(), 'ohos.permission.CAMERA'
  );
  // grantStatus === 0 (PERMISSION_GRANTED)
  if (grantStatus !== 0) {
    // 无权限 → 降级处理
    showPermissionDenied();
    return;
  }
  // 3. 执行敏感操作
  startCamera();
}

3.2 内核权限校验(UID/GID)

// 文件访问权限校验(内核)
// 每次 open/read/write 都会调用
static int inode_permission(struct inode *inode, int mask)
{
    // 1. 获取当前进程凭据(UID/GID)
    const struct cred *cred = current_cred();
    kuid_t uid = cred->fsuid;   // 进程 UID
    kgid_t gid = cred->fsgid;   // 进程 GID

    // 2. 权限位检查 (rwx)
    //    属主权限: inode->i_mode & S_IRUSR...
    //    属组权限: inode->i_mode & S_IRGRP...
    //    其他权限: inode->i_mode & S_IROTH...
    if (uid_eq(inode->i_uid, uid)) {
        // 属主 → 查属主权限位
        mode = inode->i_mode & 0700;
    } else if (in_group_p(gid)) {
        // 属组 → 查属组权限位
        mode = inode->i_mode & 0070;
    } else {
        // 其他人 → 查其他权限位
        mode = inode->i_mode & 0007;
    }
    // 3. 权限不足 → EACCES
    if (!(mode & mask)) { return -EACCES; }
    return 0;
}

3.3 SELinux 策略(安全策略)

# SELinux 策略文件示例 (te: Type Enforcement)
# 定义类型
type sensor_app, domain;
type sensor_data, file_type;

# 允许 sensor_app 读取 sensor_data
allow sensor_app sensor_data:file { read getattr open };

# 允许访问 Binder
allow sensor_app binder_device:binder { transfer };

# 禁止项 (默认 deny): 未声明的访问全部拒绝
# 这就是 MAC 与 DAC 的区别:
#   DAC: 没拒绝 = 允许
#   MAC: 没允许 = 拒绝
// SELinux 内核校验核心
static int selinux_file_permission(struct file *file, int mask)
{
    // 1. 获取主体安全上下文 (进程的 SID)
    u32 sid = current_sid();
    // 2. 获取客体安全上下文 (文件的 SID)
    u32 tsid = file->f_path.dentry->d_inode->i_sid;
    // 3. 查策略数据库 (AVC 缓存)
    u32 av = avc_has_perm(sid, tsid,
                          SECCLASS_FILE,  // 客体类: 文件
                          file_mask_to_av(mask),  // 操作: 读/写
                          NULL);
    // 4. 未允许 → 拒绝 (EACCES)
    if (av) { return -EACCES; }
    return 0;
}

3.4 CAP 能力校验

// 能力检查(内核核心函数)
// 每次特权操作调用
int capable(int cap)
{
    // 1. 获取当前进程的有效能力集
    const struct cred *cred = current_cred();
    kernel_cap_t cap_effective = cred->cap_effective;

    // 2. 检查是否拥有该能力
    if (!cap_raised(cap_effective, cap)) {
        return 0;   // 无能力 → 拒绝
    }
    return 1;       // 有能力 → 允许
}

// 实例: 修改系统时间需要 CAP_SYS_TIME
if (!capable(CAP_SYS_TIME)) {
    return -EPERM;   // Operation not permitted
}

四、企业级实战落地

4.1 权限安全清单

检查点 说明
应用 权限最小化 用到的才申请
应用 使用前校验 checkAccessToken
系统 UID 隔离 独立 UID 沙箱
系统 SELinux 策略 未声明即拒绝
系统 CAP 最小化 按需授予能力

4.2 完整示例:权限校验链演示

@Entry
@ComponentV2
struct KernelPermissionDemo {
  @Local logs: string[] = [];
  @Local state: string = '未校验';

  private runPermissionChain(): void {
    this.logs = [];
    this.state = '校验中';
    this.log('🔐 权限校验调用链');
    this.log('请求: 应用访问相机');
    this.log('① UID 校验: 进程 UID=10023 (应用沙箱)');
    this.log('   → 身份有效 ✅');
    this.log('② SELinux 策略: 查 AVC 缓存');
    this.log('   → allow app camera_app:camera { open }');
    this.log('   → 策略允许 ✅');
    this.log('③ CAP 检查: 应用无需特权能力');
    this.log('   → 普通能力集即可 ✅');
    this.log('④ 授权框: 用户确认 → Token 记录');
    this.log('   → 校验通过,相机打开 ✅');
    this.state = '校验通过 (相机已授权)';
  }

  private runDeniedChain(): void {
    this.logs = [];
    this.state = '拒绝演示';
    this.log('🔒 权限不足场景');
    this.log('恶意应用尝试读取其他应用数据:');
    this.log('① UID 校验: UID=10099 ≠ 目标 UID=10023');
    this.log('   → 文件属主不匹配 ❌');
    this.log('② SELinux 策略: 未声明该访问');
    this.log('   → MAC: 未允许 = 拒绝 ❌');
    this.log('③ 结论: 双重拒绝,无法越权');
    this.log('→ 返回 EACCES / EPERM');
  }

  build() {
    Column({ space: 12 }) {
      Text('🔐 内核权限校验机制').fontSize(20).fontWeight(FontWeight.Bold)
      Text('状态: ' + this.state).fontSize(13).fontColor('#4FC3F7')

      Row({ space: 8 }) {
        Button('放行链路').layoutWeight(1).height(40).fontSize(12)
          .onClick(() => this.runPermissionChain())
        Button('拒绝链路').layoutWeight(1).height(40).fontSize(12)
          .onClick(() => this.runDeniedChain())
      }
      .width('100%')

      Scroll() {
        Column() {
          ForEach(this.logs, (l: string) => {
            Text(l).fontSize(11).lineHeight(18).fontColor('rgba(255,255,255,0.8)').width('100%')
          }, (l: string, i: number) => l + i)
        }.width('100%')
      }
      .layoutWeight(1).width('100%').scrollBar(BarState.Off)
    }
    .width('100%').height('100%').padding(16)
    .backgroundColor('#0D1B2A')
  }
}

4.3 权限模型对比

模型 控制方式 强度 特点
UID/GID 身份判定 基础隔离
SELinux MAC 策略判定 未允许即拒绝
CAP 能力判定 最小特权
组合使用 多层校验 极高 层层设防

五、问题排查与性能优化

问题 原因 解决
应用无法访问 SELinux 策略缺失 补充 allow 策略
越权成功 CAP 授予过多 能力最小化
校验变慢 策略表大 AVC 缓存命中
权限被恶意利用 校验链缺失 全链路校验
root 逃逸 单层 DAC SELinux 兜底
服务权限过大 共用 root 拆分 UID + CAP

5.1 权限安全优化

1. 最小特权: 应用只申请用到的权限,服务只给需要的 CAP
2. SELinux 补全: 新功能上线前审查策略(未声明即拒绝)
3. 校验前置: 敏感操作前主动 checkAccessToken(别依赖系统拦截)
4. 分层设防: UID 隔离 + MAC 策略 + CAP 能力,缺一不可
5. 审计留痕: 权限授予/拒绝都记录(安全审计)

六、高阶总结与最佳实践

  1. 身份先行:UID/GID 是权限的基础,独立 UID = 沙箱隔离的基石。
  2. MAC 兜底:SELinux"未允许即拒绝",即使 root 也无法绕过——这是最后防线。
  3. 能力最小化:CAP 拆分了 root 权限,按需授予是安全最佳实践。
  4. 全链路校验:应用层申请 + 系统层策略 + 内核层校验,每层都要查。
  5. 审计闭环:权限行为留痕,违规可追溯。

一句话记住:权限校验三层防线——UID 管身份(你是谁)、SELinux 管行为(你能做什么,未允许即拒绝)、CAP 管特权(你有哪把钥匙)——应用按需申请、系统策略兜底、内核层层校验。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐