Appearance
安全模型
1. 安全架构总览
鸿蒙安全架构分层:
┌──────────────────────────────────────────┐
│ 应用层安全 │
│ └── 应用签名 + 权限申请 + 沙箱隔离 │
├──────────────────────────────────────────┤
│ 框架层安全 │
│ └── TokenID/ATM + 能力门限 + URI 权限 │
├──────────────────────────────────────────┤
│ 系统服务层安全 │
│ └── SELinux 强制访问控制 + 域隔离 │
├──────────────────────────────────────────┤
│ 内核层安全 │
│ └── HongMeng Kernel + MPU + 内存保护 │
├──────────────────────────────────────────┤
│ 硬件层安全 │
│ └── TEE + 安全芯片 + 安全启动链 │
└──────────────────────────────────────────┘2. TokenID 与访问控制
2.1 TokenID 机制
TokenID 是鸿蒙的核心访问控制机制:
TokenID 特性:
├── 每个进程/线程拥有唯一的 TokenID
├── TokenID 包含权限信息和角色信息
├── 访问资源时必须携带 TokenID 进行验证
├── TokenID 不可伪造,由内核分配
└── TokenID 有时间/空间/角色三维约束TokenID 的三维约束模型:
┌──────────────────────────────────┐
│ 空间维度 (空间) │
│ └── 进程/线程/Ability 级别 │
├──────────────────────────────────┤
│ 时间维度 (时效) │
│ └── Token 有效期 / 一次性使用 │
├──────────────────────────────────┤
│ 角色维度 (角色) │
│ └── 权限组 / 角色组 绑定 │
└──────────────────────────────────┘2.2 权限等级
| 权限等级 | 说明 | 示例 | 申请方式 |
|---|---|---|---|
| system_core | 核心系统权限 | system_monitor | 系统签名 |
| system_basic | 基础系统权限 | ohos.permission.INTERNET | 自动授予 |
| normal | 普通权限 | camera/storage | 用户授予 |
| dynamic | 动态权限 | location/record | 运行时申请 |
2.3 权限检查
arkts
import { permission } from '@kit.AbilityKit';
// 1. 检查权限状态
async function checkPermission() {
const token = await permission.checkAccessToken('ohos.permission.CAMERA');
console.log('Token: ' + token);
// token = 0: 无权限
// token > 0: 有权限
// token = -1: 权限不存在
}
// 2. 请求权限
async function requestCameraPermission() {
const permissions = ['ohos.permission.CAMERA'];
const token = await permission.requestPermissionsFromUser(permissions);
if (token === 0) {
console.log('权限已授予');
} else {
console.log('权限被拒绝');
}
}
// 3. 动态权限申请(用户手动弹窗)
// 与 Android onRequestPermissionsResult 类似
// 鸿蒙通过 system.router.pushUrl 弹出权限对话框2.4 权限声明
json5
// module.json5
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "需要访问网络",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.CAMERA",
"reason": "拍照需要",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
]
}
}3. ATM(Ability Token Manager)
3.1 ATM 机制
ATM (Ability Token Manager) 是系统级的能力访问管理器:
ATM 职责:
├── 管理 Ability 的生命周期 Token
├── 验证 Ability 的跨进程访问权限
├── 管理 URI 权限(file:// 和 resource://)
├── 管理 Service 连接
└── 管理跨 Ability 的参数传递3.2 URI 权限
arkts
// URI 权限:控制文件资源的跨应用访问
import { uri } from '@kit.AbilityKit';
// 1. 授予 URI 权限(临时文件访问权限)
async function grantUriPermission(want: app.Want) {
const uriPermissionFlags: number = (
uri.URIPermissionFlag.READ_URI_PERMISSION
| uri.URIPermissionFlag.WRITE_URI_PERMISSION
);
// 授予目标 Ability 读取 URI 的权限
await this.context.grantUriPermission('com.target.app', uri, uriPermissionFlags);
}
// 2. 撤销 URI 权限
async function revokeUriPermission(want: app.Want) {
await this.context.revokeUriPermission('com.target.app', uri);
}3.3 能力门限(Capability)
能力门限是鸿蒙 IPC 机制中的安全传递机制:
IPC 消息传递时,权限内嵌在消息中:
┌─────────────────────────────────────────┐
│ IPC Message │
│ ├── 消息头(类型/大小) │
│ ├── 消息体(数据内容) │
│ └── Capability(能力描述) │
│ ├── 可访问的资源列表 │
│ ├── 可执行的命令列表 │
│ └── 有效期/空间范围 │
└─────────────────────────────────────────┘
接收方验证:
1. 检查消息来源的 TokenID
2. 验证 Capability 是否包含目标资源
3. 检查有效期和空间范围
4. 通过 → 执行;不通过 → 拒绝4. 沙箱机制
4.1 应用沙箱
每个应用运行在独立的沙箱中:
沙箱特性:
├── 文件系统隔离:应用只能访问自己的沙箱目录
├── 网络隔离:独立网络命名空间
├── 进程隔离:独立 PID/UID
├── 内存隔离:独立虚拟地址空间
└── 设备能力隔离:摄像头/麦克风等需要权限4.2 沙箱目录结构
应用的沙箱目录结构:
┌── /data/user/0/<bundleName>/ (应用数据目录)
│ ├── files/ (内部文件存储)
│ ├── cache/ (内部缓存)
│ ├── preferences/ (偏好设置)
│ └── database/ (数据库)
├── /data/media/0/ (共享存储/DCIM)
├── /system/ (系统只读分区)
└── /vendor/ (厂商分区)4.3 URI 安全机制
arkts
// URI 访问安全:通过 Uri 而非直接路径访问文件
import { uri } from '@kit.AbilityKit';
// ✅ 正确:通过 URI 访问共享文件
async function shareFile(targetUri: string) {
// 通过 URI 传递,而非文件路径
const fileUri = uri.createFileUri('/data/user/0/com.example/myapp/files/doc.pdf');
// 授予临时权限给目标应用
await this.context.grantUriPermission(targetUri, fileUri, uri.URIPermissionFlag.READ_URI_PERMISSION);
}
// ❌ 错误:直接拼接文件路径(不安全)
// const filePath = '/data/data/com.example/files/doc.pdf';5. SELinux
5.1 SELinux 在鸿蒙中的应用
SELinux(Security-Enhanced Linux)提供强制访问控制:
域(Domain)模型:
├── 每个进程/线程运行在特定域中
├── 域之间通过策略规则定义访问权限
├── 默认策略:DENY ALL(拒绝所有未明确允许的访问)
└── 域过渡:通过 exec 触发域切换
示例策略规则:
┌──────────────────────────────────────┐
│ allow app_domain camera_device:chr_file { read write }; │
│ allow system_domain window_service:unix_stream_socket { connect }; │
│ allow init_domain zygote_domain:process { fork signull }; │
└──────────────────────────────────────┘5.2 SELinux 模式
| 模式 | 说明 | 适用场景 |
|---|---|---|
| enforcing | 强制执行,违规操作被阻止 | 生产环境(默认) |
| permissive | 记录违规但不阻止 | 开发调试 |
| disabled | 禁用 SELinux | 调试(不推荐) |
5.3 与 Android SELinux 对比
| 维度 | Android SELinux | 鸿蒙 SELinux |
|---|---|---|
| 策略管理 | sepolicy 编译为二进制 | init.hrc 配置策略 |
| 域划分 | app/domain/service | TokenID + ATM 联合管理 |
| 类型系统 | type + role | 能力门限 + 权限组 |
| 默认策略 | 宽松(allow by default) | 严格(deny by default) |
6. 安全启动链
安全启动 Chain of Trust:
┌─────────────────────────────────────┐
│ BootROM(固化在芯片中) │
│ ├── 验证 Bootloader 签名 │
│ └── 公钥固化在 ROM 中 │
├─────────────────────────────────────┤
│ Bootloader(U-Boot/Fastboot) │
│ ├── 验证 Kernel 签名 │
│ └── 传递 Device Tree │
├─────────────────────────────────────┤
│ Kernel(HongMeng Kernel) │
│ ├── 初始化安全子系统 │
│ ├── 验证系统分区签名 │
│ └── 挂载根文件系统 │
├─────────────────────────────────────┤
│ Userspace │
│ ├── 验证系统服务签名 │
│ └── 验证应用签名 │
└─────────────────────────────────────┘7. 国密算法支持
| 算法 | 说明 | 应用场景 |
|---|---|---|
| SM2 | 非对称加密(椭圆曲线) | 数字签名/密钥交换 |
| SM3 | 密码散列函数 | 数据完整性校验 |
| SM4 | 对称加密(分组密码) | 数据加密存储 |
| SM9 | 基于身份的密码算法 | 物联网设备认证 |
arkts
// 国密算法使用示例
import { cryptoFramework } from '@kit.SecurityFrameworkKit';
// SM2 密钥对生成
const keyPair = await cryptoFramework.generateKeyPair({
algorithm: 'SM2',
keySize: 256
});
// SM3 哈希
const hash = await cryptoFramework.hash({
algorithm: 'SM3',
data: buffer
});
// SM4 加密
const encrypted = await cryptoFramework.encrypt({
algorithm: 'SM4',
key: keyPair.publicKey,
data: buffer,
mode: 'ECB'
});8. 应用签名与安全
8.1 应用签名流程
签名流程:
┌─────────────────────────────────────┐
│ 1. 生成密钥对 │
│ → keytool 生成 .p12 密钥文件 │
├─────────────────────────────────────┤
│ 2. 生成 CSR │
│ → Certificate Signing Request │
├─────────────────────────────────────┤
│ 3. 申请证书 │
│ → 发布证书 (.cer) │
│ → 中间证书 (.p7b) │
├─────────────────────────────────────┤
│ 4. 打包签名 │
│ → 在 DevEco Studio 中配置 │
│ → hvigor 自动签名 │
└─────────────────────────────────────┘8.2 签名验证
签名验证时机:
├── 安装时:验证签名与 manifest 匹配
├── 运行时:验证跨应用调用的签名一致性
├── 更新时:验证签名与新包匹配(同签名应用才可覆盖更新)
└── 系统服务:验证系统服务的签名身份9. 🎯 面试高频考点
Q1: 鸿蒙的 TokenID 机制与 Android 的 UID 机制有什么区别?
答要点:
- TokenID 是三维约束(空间/时间/角色),UID 是一维(进程级)
- TokenID 可动态分配/撤销,UID 固定
- TokenID 通过 ATM 集中管理,UID 由内核分配
- TokenID 支持细粒度权限(能力门限),UID 是粗粒度
- TokenID 与 Ability 生命周期绑定,UID 与进程绑定
Q2: 鸿蒙的沙箱机制如何实现?
答要点:
- 文件系统:每个应用独立沙箱目录,不能访问其他应用目录
- 网络:独立网络命名空间
- 内存:独立虚拟地址空间
- URI 权限:通过 grantUriPermission 实现临时文件共享
- SELinux:强制域隔离,默认拒绝未授权的域间访问
Q3: 如何申请和检查运行时权限?
答要点:
- 在 module.json5 中声明权限
- 使用 permission.checkAccessToken() 检查权限
- 使用 permission.requestPermissionsFromUser() 申请权限
- 用户通过系统弹窗手动确认
- 权限分为 normal(自动授予)和 dynamic(需用户确认)
- 不同权限等级需要不同签名
💡 面试提示:安全是面试重点。重点掌握 TokenID 三维模型、ATM 管理、URI 权限、沙箱隔离 四个核心概念,对比 Android 的 UID/SELinux/PackageManager。