10 第9章 应用安全
10.1 9.1 HarmonyOS安全架构
10.1.1 9.1.1 安全模型概述
HarmonyOS采用了分层安全架构,从硬件到应用层构建了完整的安全防护体系。其安全模型的核心原则是”最小权限”和”纵深防御”。
HarmonyOS安全架构包含以下层次:
- 硬件安全层:基于TEE(Trusted Execution Environment)的可信执行环境,提供硬件级别的密钥存储和密码学运算。
- 内核安全层:基于微内核设计,实现进程隔离、内存保护和安全调度。
- 系统服务层:提供安全认证、权限管理、加密服务等系统级安全能力。
- 应用框架层:实现应用沙箱、权限控制和数据安全存储。
- 应用层:开发者在此层面实现业务安全逻辑。
// HarmonyOS安全能力概览
// 以下代码展示了如何在应用中调用各层安全能力
import { security } from '@kit.SecurityKit'
import { cryptoFramework } from '@kit.CryptoKit'
// 安全能力使用示例入口
class SecurityDemo {
// 1. 使用系统级加密服务
async encryptData(plainText: string): Promise<ArrayBuffer> {
// 调用系统加密服务(系统服务层)
const aesCipher = await cryptoFramework.createCipher('AES128|CBC|PKCS7')
// ... 加密逻辑
return new ArrayBuffer(0)
}
// 2. 使用安全存储
async storeSecureData(key: string, value: string): Promise<void> {
// 调用安全存储(应用框架层)
// 数据会被TEE保护
}
// 3. 权限检查
async checkPermission(permission: string): Promise<boolean> {
// 调用权限管理(应用框架层)
return true
}
}10.1.2 9.1.2 应用沙箱机制
HarmonyOS通过应用沙箱确保每个应用在隔离的环境中运行,防止应用间的数据泄露和恶意行为。
沙箱机制的核心特性:
- 文件系统隔离:每个应用拥有独立的数据目录,无法直接访问其他应用的文件。
- 进程隔离:每个应用运行在独立的进程中,通过内核级进程隔离防止内存越界访问。
- IPC管控:应用间的进程间通信需要经过系统授权和检查。
- 资源限制:对每个应用的CPU、内存、存储等资源使用进行限制。
// 应用沙箱目录结构
// 每个应用的数据存储在以下沙箱路径中:
// /data/storage/el2/base/haps/<app_bundle_name>/
// ├── files/ # 应用文件目录
// ├── cache/ # 缓存目录
// ├── preferences/ # 偏好设置
// ├── database/ # 数据库
// └── log/ # 日志
import { fileIo } from '@kit.CoreFileKit'
// 在沙箱内操作文件
class SandboxFileDemo {
// 获取应用沙箱文件路径
async getFilePath(context: Context, fileName: string): Promise<string> {
// filesDir 指向应用沙箱的files目录
return `${context.filesDir}/${fileName}`
}
// 在沙箱内写入文件
async writeFile(context: Context, fileName: string, content: string): Promise<void> {
const filePath = `${context.filesDir}/${fileName}`
const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY)
try {
fileIo.writeSync(file.fd, content)
} finally {
fileIo.closeSync(file.fd)
}
}
// 在沙箱内读取文件
async readFile(context: Context, fileName: string): Promise<string> {
const filePath = `${context.filesDir}/${fileName}`
const file = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY)
try {
const stat = fileIo.statSync(file.fd)
const buffer = new ArrayBuffer(stat.size)
fileIo.readSync(file.fd, buffer)
// 将ArrayBuffer转为字符串
const decoder = new util.TextDecoder('utf-8')
const uint8Array = new Uint8Array(buffer)
return decoder.decodeWithStream(uint8Array)
} finally {
fileIo.closeSync(file.fd)
}
}
}
import { util } from '@kit.ArkTS'10.1.3 9.1.3 权限管理体系
HarmonyOS的权限管理体系采用分级授权模式,确保应用仅能访问其所需的系统资源。
权限分类:
| 权限级别 | 说明 | 授权方式 |
|---|---|---|
| normal | 普通权限 | 安装时自动授权 |
| system_basic | 系统基础权限 | 系统应用专用 |
| system_core | 系统核心权限 | 系统核心应用专用 |
| user_grant | 用户授权权限 | 运行时动态申请 |
// module.json5 权限声明示例
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:permission_internet_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:permission_location_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.CAMERA",
"reason": "$string:permission_camera_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}10.1.4 9.1.4 安全启动链
HarmonyOS实现了从硬件到应用的完整安全启动链,确保系统软件未被篡改。
安全启动流程:
- BootROM:芯片内置的不可变代码,验证Bootloader签名。
- Bootloader:验证操作系统内核签名。
- 内核:验证系统服务和驱动签名。
- 系统框架:验证应用框架层签名。
- 应用:验证应用安装包签名。
每个环节都通过密码学签名确保下一环节的完整性和可信性,形成信任链(Chain of Trust)。
10.2 9.2 权限管理深入
10.2.1 9.2.1 动态权限申请
对于user_grant类型的权限,应用必须在运行时向用户动态申请。
import { abilityAccessCtrl, common } from '@kit.AbilityKit'
// 权限管理器
class PermissionManager {
private context: common.UIAbilityContext
constructor(context: common.UIAbilityContext) {
this.context = context
}
// 检查单个权限
async checkPermission(permission: string): Promise<boolean> {
const atManager = abilityAccessCtrl.createAtManager()
try {
const result = atManager.checkAccessTokenSync(
this.context.applicationInfo.accessTokenId,
permission
)
return result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
} catch (error) {
console.error(`检查权限失败: ${(error as Error).message}`)
return false
}
}
// 动态申请权限
async requestPermission(permission: string): Promise<boolean> {
const atManager = abilityAccessCtrl.createAtManager()
try {
const result = await atManager.requestPermissionsFromUser(
this.context,
[permission]
)
// 检查授权结果
const grantStatus = result.authResults[0]
if (grantStatus === 0) {
console.info(`权限 ${permission} 已授予`)
return true
} else {
console.warn(`权限 ${permission} 被拒绝`)
// 检查是否可以显示 rationale
if (result.shouldShowRequestPermissionRationale[0]) {
// 用户之前拒绝过,需要解释为什么需要该权限
this.showPermissionRationale(permission)
}
return false
}
} catch (error) {
console.error(`申请权限失败: ${(error as Error).message}`)
return false
}
}
// 批量申请权限
async requestPermissions(permissions: string[]): Promise<Map<string, boolean>> {
const atManager = abilityAccessCtrl.createAtManager()
const resultMap = new Map<string, boolean>()
try {
const result = await atManager.requestPermissionsFromUser(
this.context,
permissions
)
for (let i = 0; i < permissions.length; i++) {
resultMap.set(permissions[i], result.authResults[i] === 0)
}
} catch (error) {
console.error(`批量申请权限失败: ${(error as Error).message}`)
}
return resultMap
}
// 显示权限说明
private showPermissionRationale(permission: string): void {
// 弹出对话框解释为什么需要该权限
console.info(`需要向用户解释为什么需要 ${permission} 权限`)
}
}
// 使用示例
@Component
struct CameraPage {
private context = getContext(this) as common.UIAbilityContext
private permissionManager: PermissionManager | null = null
aboutToAppear(): void {
this.permissionManager = new PermissionManager(this.context)
}
async takePhoto(): Promise<void> {
if (!this.permissionManager) return
// 先检查权限
const hasPermission = await this.permissionManager.checkPermission(
'ohos.permission.CAMERA'
)
if (hasPermission) {
// 已有权限,直接拍照
await this.openCamera()
} else {
// 申请权限
const granted = await this.permissionManager.requestPermission(
'ohos.permission.CAMERA'
)
if (granted) {
await this.openCamera()
} else {
// 权限被拒绝,显示提示
this.showPermissionDeniedDialog()
}
}
}
async openCamera(): Promise<void> {
// 打开相机
}
showPermissionDeniedDialog(): void {
// 显示权限被拒绝的对话框
}
build() {
Column() {
Button('拍照')
.onClick(() => this.takePhoto())
}
}
}10.2.2 9.2.2 权限分级
HarmonyOS的权限分为多个等级,不同等级有不同的获取条件和保护策略。
// 权限分级示例
class PermissionLevelDemo {
// normal级别权限 - 安装时自动授权
// 如:ohos.permission.INTERNET(网络访问)
// 如:ohos.permission.GET_NETWORK_INFO(获取网络信息)
// user_grant级别权限 - 需要运行时动态申请
// 如:ohos.permission.CAMERA(相机)
// 如:ohos.permission.APPROXIMATELY_LOCATION(粗略定位)
// 如:ohos.permission.READ_MEDIA(读取媒体文件)
// system_basic级别权限 - 仅系统应用可获取
// 如:ohos.permission.GET_WIFI_INFO(获取WiFi信息)
// system_core级别权限 - 仅核心系统应用可获取
// 如:ohos.permission.CONFIG_WIFI(配置WiFi)
// 权限使用场景声明
getPermissionConfig(): object {
return {
// 使用时申请 - 仅在前台使用时可用
locationInUse: {
permission: 'ohos.permission.APPROXIMATELY_LOCATION',
when: 'inuse" // 仅在使用时
},
// 始终允许 - 前后台均可使用
locationAlways: {
permission: 'ohos.permission.LOCATION',
when: 'always' // 始终可用
}
}
}
}10.2.3 9.2.3 代理授权(Delegation)
代理授权允许一个应用代表另一个应用执行操作,适用于跨应用协作场景。
// 代理授权配置
class DelegationDemo {
// 委托方应用(授权方)
async setupDelegation(context: common.UIAbilityContext): Promise<void> {
const atManager = abilityAccessCtrl.createAtManager()
// 将位置权限委托给目标应用
try {
await atManager.setAccessTokenDelegation(
context,
{
// 被委托的应用包名
delegateBundleName: 'com.example.delegateapp',
// 委托的权限列表
permissions: ['ohos.permission.APPROXIMATELY_LOCATION'],
// 委托的有效期
expiryTime: Date.now() + 24 * 60 * 60 * 1000 // 24小时
}
)
console.info('权限委托设置成功')
} catch (error) {
console.error(`权限委托设置失败: ${(error as Error).message}`)
}
}
// 撤销委托
async revokeDelegation(context: common.UIAbilityContext): Promise<void> {
const atManager = abilityAccessCtrl.createAtManager()
try {
await atManager.clearAccessTokenDelegation(
context,
'com.example.delegateapp'
)
console.info('权限委托已撤销')
} catch (error) {
console.error(`撤销委托失败: ${(error as Error).message}`)
}
}
}10.2.4 9.2.4 权限最佳实践
// 权限管理最佳实践
class PermissionBestPractice {
private context: common.UIAbilityContext
constructor(context: common.UIAbilityContext) {
this.context = context
}
// 实践1:按需申请,不要一次性申请所有权限
async requestPermissionOnDemand(permission: string): Promise<boolean> {
const atManager = abilityAccessCtrl.createAtManager()
// 先检查是否已有权限
const hasPermission = atManager.checkAccessTokenSync(
this.context.applicationInfo.accessTokenId,
permission
) === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
if (hasPermission) return true
// 仅在需要时才申请
const result = await atManager.requestPermissionsFromUser(
this.context, [permission]
)
return result.authResults[0] === 0
}
// 实践2:优雅处理权限被拒绝的场景
handlePermissionResult(granted: boolean, feature: string): void {
if (granted) {
// 权限已授予,执行功能
this.executeFeature(feature)
} else {
// 权限被拒绝,提供降级方案
this.provideFallback(feature)
}
}
// 实践3:解释权限用途,提高授权率
async requestWithExplanation(
permission: string,
reason: string
): Promise<boolean> {
// 先展示说明对话框
const userConfirmed = await this.showExplanationDialog(reason)
if (!userConfirmed) return false
// 用户理解后再申请
const atManager = abilityAccessCtrl.createAtManager()
const result = await atManager.requestPermissionsFromUser(
this.context, [permission]
)
return result.authResults[0] === 0
}
// 实践4:引导用户到设置页面开启权限
async guideToSettings(): Promise<void> {
const want: Want = {
action: 'action.settings.application.details',
parameters: {
'settingsParamBundleName': this.context.abilityInfo.bundleName
}
}
await this.context.startAbility(want)
}
private executeFeature(feature: string): void {}
private provideFallback(feature: string): void {}
private async showExplanationDialog(reason: string): Promise<boolean> { return true }
}
import { Want } from '@kit.AbilityKit'10.2.5 9.2.5 常见权限错误处理
// 权限错误处理器
class PermissionErrorHandler {
// 处理各种权限错误场景
static handlePermissionError(
error: Error,
context: common.UIAbilityContext
): void {
const errorMessage = error.message
// 场景1:权限未声明
if (errorMessage.includes('not declared')) {
console.error('权限未在module.json5中声明,请检查配置')
return
}
// 场景2:权限被用户拒绝
if (errorMessage.includes('permission denied')) {
console.warn('用户拒绝了权限申请')
// 提供降级方案
return
}
// 场景3:权限类型不匹配
if (errorMessage.includes('system_grant')) {
console.error('尝试动态申请系统权限,这是不允许的')
return
}
// 场景4:应用不在前台
if (errorMessage.includes('not foreground')) {
console.error('后台无法申请权限,请在前台状态下申请')
return
}
}
}10.3 9.3 数据加密与安全存储
10.3.1 9.3.1 对称加密(AES)
AES(Advanced Encryption Standard)是最常用的对称加密算法,适用于大量数据的加密。
import { cryptoFramework } from '@kit.CryptoKit'
import { util } from '@kit.ArkTS'
// AES加密工具类
class AESCryptoUtil {
private static readonly ALGORITHM = 'AES128|CBC|PKCS7'
private static readonly KEY_LENGTH = 128 // 位
private static readonly IV_LENGTH = 16 // 字节
// 生成随机密钥
static async generateKey(): Promise<cryptoFramework.AesKeyPairSpec> {
const aesGenerator = cryptoFramework.createAesKeyPairGenerator()
const keyParams: cryptoFramework.AesKeyPairSpec = {
keySize: AESCryptoUtil.KEY_LENGTH
}
await aesGenerator.init(keyParams)
const keyPair = await aesGenerator.generateKeyPair()
return keyPair as cryptoFramework.AesKeyPairSpec
}
// AES加密
static async encrypt(
plainText: string,
key: ArrayBuffer,
iv: ArrayBuffer
): Promise<ArrayBuffer> {
// 创建AES加密器
const aesCipher = cryptoFramework.createCipher(AESCryptoUtil.ALGORITHM)
// 导入密钥
const secretKey = cryptoFramework.createSymKeyByKey(key, 'AES128')
const paramsSpec = cryptoFramework.createIvParamsSpec(iv)
await aesCipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, secretKey, paramsSpec)
// 转换明文
const textEncoder = new util.TextEncoder()
const plainBuffer = textEncoder.encodeInto(plainText)
const input = cryptoFramework.createDataObjSrc(plainBuffer)
// 执行加密
const output = await aesCipher.doFinal(input)
return output.data
}
// AES解密
static async decrypt(
cipherData: ArrayBuffer,
key: ArrayBuffer,
iv: ArrayBuffer
): Promise<string> {
const aesCipher = cryptoFramework.createCipher(AESCryptoUtil.ALGORITHM)
const secretKey = cryptoFramework.createSymKeyByKey(key, 'AES128')
const paramsSpec = cryptoFramework.createIvParamsSpec(iv)
await aesCipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, secretKey, paramsSpec)
const input = cryptoFramework.createDataObjSrc(new Uint8Array(cipherData))
const output = await aesCipher.doFinal(input)
// 转换回字符串
const textDecoder = new util.TextDecoder('utf-8')
return textDecoder.decodeWithStream(new Uint8Array(output.data))
}
// 生成随机IV
static generateIV(): ArrayBuffer {
const iv = new ArrayBuffer(AESCryptoUtil.IV_LENGTH)
const view = new Uint8Array(iv)
for (let i = 0; i < AESCryptoUtil.IV_LENGTH; i++) {
view[i] = Math.floor(Math.random() * 256)
}
return iv
}
}
// 使用示例
async function aesDemo(): Promise<void> {
const plainText = '这是一条需要加密的敏感信息'
// 生成密钥和IV
const key = new ArrayBuffer(16) // 128位密钥
const iv = AESCryptoUtil.generateIV()
// 加密
const encrypted = await AESCryptoUtil.encrypt(plainText, key, iv)
console.info(`加密后数据长度: ${encrypted.byteLength} 字节`)
// 解密
const decrypted = await AESCryptoUtil.decrypt(encrypted, key, iv)
console.info(`解密结果: ${decrypted}`)
}10.3.2 9.3.2 非对称加密(RSA/ECC)
非对称加密使用公钥/私钥对,适用于密钥交换和数字签名。
// RSA加密工具类
class RSACryptoUtil {
private static readonly KEY_SIZE = 2048
// 生成RSA密钥对
static async generateKeyPair(): Promise<{
publicKey: ArrayBuffer,
privateKey: ArrayBuffer
}> {
const rsaKeyPairGenerator = cryptoFramework.createRsaKeyPairGenerator()
await rsaKeyPairGenerator.init({
keySize: RSACryptoUtil.KEY_SIZE
})
const keyPair = await rsaKeyPairGenerator.generateKeyPair()
const publicKey = await keyPair.pubKey.convertToEncoding('PKCS8')
const privateKey = await keyPair.priKey.convertToEncoding('PKCS8')
return {
publicKey: publicKey.data,
privateKey: privateKey.data
}
}
// RSA公钥加密
static async encryptWithPublicKey(
plainText: string,
publicKeyData: ArrayBuffer
): Promise<ArrayBuffer> {
const rsaCipher = cryptoFramework.createCipher('RSA2048|PKCS1_V1_5')
// 导入公钥
const pubKey = cryptoFramework.createPubKeyFromData(
publicKeyData, 'RSA', 'PKCS8'
)
await rsaCipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, pubKey)
const textEncoder = new util.TextEncoder()
const plainBuffer = textEncoder.encodeInto(plainText)
const input = cryptoFramework.createDataObjSrc(plainBuffer)
const output = await rsaCipher.doFinal(input)
return output.data
}
// RSA私钥解密
static async decryptWithPrivateKey(
cipherData: ArrayBuffer,
privateKeyData: ArrayBuffer
): Promise<string> {
const rsaCipher = cryptoFramework.createCipher('RSA2048|PKCS1_V1_5')
const priKey = cryptoFramework.createPriKeyFromData(
privateKeyData, 'RSA', 'PKCS8'
)
await rsaCipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, priKey)
const input = cryptoFramework.createDataObjSrc(new Uint8Array(cipherData))
const output = await rsaCipher.doFinal(input)
const textDecoder = new util.TextDecoder('utf-8')
return textDecoder.decodeWithStream(new Uint8Array(output.data))
}
// RSA数字签名
static async sign(
data: string,
privateKeyData: ArrayBuffer
): Promise<ArrayBuffer> {
const signer = cryptoFramework.createSign('SHA256withRSA')
const priKey = cryptoFramework.createPriKeyFromData(
privateKeyData, 'RSA', 'PKCS8'
)
await signer.init(priKey)
const textEncoder = new util.TextEncoder()
const dataBuffer = textEncoder.encodeInto(data)
await signer.update(cryptoFramework.createDataObjSrc(dataBuffer))
const signature = await signer.sign()
return signature.data
}
// RSA签名验证
static async verify(
data: string,
signatureData: ArrayBuffer,
publicKeyData: ArrayBuffer
): Promise<boolean> {
const verifier = cryptoFramework.createVerify('SHA256withRSA')
const pubKey = cryptoFramework.createPubKeyFromData(
publicKeyData, 'RSA', 'PKCS8'
)
await verifier.init(pubKey)
const textEncoder = new util.TextEncoder()
const dataBuffer = textEncoder.encodeInto(data)
await verifier.update(cryptoFramework.createDataObjSrc(dataBuffer))
const isValid = await verifier.verify(
cryptoFramework.createDataObjSrc(new Uint8Array(signatureData))
)
return isValid
}
}10.3.3 9.3.3 哈希算法(SHA系列)
哈希算法用于数据完整性校验和密码存储。
// 哈希工具类
class HashUtil {
// SHA-256哈希
static async sha256(data: string): Promise<string> {
const md = cryptoFramework.createMd('SHA256')
const textEncoder = new util.TextEncoder()
const dataBuffer = textEncoder.encodeInto(data)
await md.update(cryptoFramework.createDataObjSrc(dataBuffer))
const result = await md.digest()
return HashUtil.arrayBufferToHex(result.data)
}
// SHA-512哈希
static async sha512(data: string): Promise<string> {
const md = cryptoFramework.createMd('SHA512')
const textEncoder = new util.TextEncoder()
const dataBuffer = textEncoder.encodeInto(data)
await md.update(cryptoFramework.createDataObjSrc(dataBuffer))
const result = await md.digest()
return HashUtil.arrayBufferToHex(result.data)
}
// HMAC(带密钥的哈希)
static async hmac(
data: string,
key: ArrayBuffer,
algorithm: string = 'HMAC_SHA256'
): Promise<string> {
const hmacObj = cryptoFramework.createHmac(algorithm)
const hmacKey = cryptoFramework.createSymKeyByKey(key, 'HMAC')
await hmacObj.init(hmacKey)
const textEncoder = new util.TextEncoder()
const dataBuffer = textEncoder.encodeInto(data)
await hmacObj.update(cryptoFramework.createDataObjSrc(dataBuffer))
const result = await hmacObj.doFinal()
return HashUtil.arrayBufferToHex(result.data)
}
// 密码哈希(使用PBKDF2)
static async hashPassword(
password: string,
salt: ArrayBuffer,
iterations: number = 100000
): Promise<string> {
const pbkdf2 = cryptoFramework.createPbkdf2('PBKDF2_WITH_HMAC_SHA256')
const textEncoder = new util.TextEncoder()
const passwordBuffer = textEncoder.encodeInto(password)
const passwordKey = cryptoFramework.createSymKeyByKey(passwordBuffer, 'PBKDF2')
const paramsSpec = cryptoFramework.createPbkdf2ParamsSpec(
salt, iterations, 32 // 输出32字节
)
await pbkdf2.init(passwordKey, paramsSpec)
const result = await pbkdf2.deriveKey()
return HashUtil.arrayBufferToHex(result.data)
}
// ArrayBuffer转十六进制字符串
private static arrayBufferToHex(buffer: ArrayBuffer): string {
const uint8Array = new Uint8Array(buffer)
return Array.from(uint8Array)
.map(byte => byte.toString(16).padStart(2, '0'))
.join('')
}
}
// 使用示例
async function hashDemo(): Promise<void> {
// 数据完整性校验
const originalData = '重要数据内容'
const hash = await HashUtil.sha256(originalData)
console.info(`数据哈希: ${hash}`)
// 密码存储(永远不要存储明文密码)
const password = 'user_password_123'
const salt = AESCryptoUtil.generateIV() // 使用随机盐
const hashedPassword = await HashUtil.hashPassword(password, salt)
console.info(`密码哈希: ${hashedPassword}`)
}10.3.4 9.3.4 KeyStore密钥管理
密钥的安全管理是加密体系的关键。HarmonyOS提供了KeyStore用于安全存储密钥。
import { keyStore } from '@kit.SecurityKit'
// 密钥管理器
class KeyStoreManager {
private static instance: KeyStoreManager | null = null
private storeAlias: string = 'app_key_store'
static getInstance(): KeyStoreManager {
if (!KeyStoreManager.instance) {
KeyStoreManager.instance = new KeyStoreManager()
}
return KeyStoreManager.instance
}
// 生成并存储AES密钥
async generateAndStoreAESKey(alias: string): Promise<void> {
try {
const keyStoreInstance = keyStore.getInstance(this.storeAlias)
// 检查密钥是否已存在
const hasKey = await keyStoreInstance.hasKey(alias)
if (hasKey) {
console.info(`密钥 ${alias} 已存在`)
return
}
// 生成AES密钥并存储到KeyStore
await keyStoreInstance.setKey(alias, {
keyType: keyStore.KeyType.AES,
keySize: 128,
// 密钥存储在TEE中,受到硬件级保护
isUserAuthenticationRequired: false
})
console.info(`密钥 ${alias} 已生成并存储`)
} catch (error) {
console.error(`密钥生成失败: ${(error as Error).message}`)
}
}
// 从KeyStore获取密钥
async getKey(alias: string): Promise<ArrayBuffer | null> {
try {
const keyStoreInstance = keyStore.getInstance(this.storeAlias)
const key = await keyStoreInstance.getKey(alias)
return key
} catch (error) {
console.error(`获取密钥失败: ${(error as Error).message}`)
return null
}
}
// 删除密钥
async deleteKey(alias: string): Promise<void> {
try {
const keyStoreInstance = keyStore.getInstance(this.storeAlias)
await keyStoreInstance.deleteKey(alias)
console.info(`密钥 ${alias} 已删除`)
} catch (error) {
console.error(`删除密钥失败: ${(error as Error).message}`)
}
}
// 检查密钥是否存在
async hasKey(alias: string): Promise<boolean> {
try {
const keyStoreInstance = keyStore.getInstance(this.storeAlias)
return await keyStoreInstance.hasKey(alias)
} catch (error) {
return false
}
}
}10.3.5 9.3.5 安全存储方案
HarmonyOS提供了多种安全存储方案,适用于不同的安全需求。
import { preferences } from '@kit.ArkData'
import { relationalStore } from '@kit.ArkData'
// 安全存储管理器
class SecureStorageManager {
private context: Context
constructor(context: Context) {
this.context = context
}
// 方案1:Preferences(轻量级数据)
// 适用于存储Token、配置等小数据
async saveToPreferences(key: string, value: string): Promise<void> {
const pref = await preferences.getPreferences(this.context, 'secure_prefs')
await pref.put(key, value)
await pref.flush()
}
async getFromPreferences(key: string): Promise<string | null> {
const pref = await preferences.getPreferences(this.context, 'secure_prefs')
const value = await pref.get(key, '')
return value ? value as string : null
}
// 方案2:关系型数据库(结构化数据)
// 适用于存储用户数据、业务数据
async initDatabase(): Promise<relationalStore.RdbStore> {
const config: relationalStore.StoreConfig = {
name: 'secure_data.db',
securityLevel: relationalStore.SecurityLevel.S1 // 安全等级
}
return await relationalStore.getRdbStore(this.context, config)
}
// 方案3:使用加密的Preferences存储敏感数据
async saveEncryptedPreference(key: string, sensitiveData: string): Promise<void> {
// 先使用AES加密
const keyStoreManager = KeyStoreManager.getInstance()
const cryptoKey = await keyStoreManager.getKey('pref_encryption_key')
if (!cryptoKey) {
throw new Error('加密密钥不存在')
}
const iv = AESCryptoUtil.generateIV()
const encrypted = await AESCryptoUtil.encrypt(sensitiveData, cryptoKey, iv)
// 存储加密后的数据和IV
const pref = await preferences.getPreferences(this.context, 'encrypted_prefs')
await pref.put(`${key}_data`, Array.from(new Uint8Array(encrypted)).join(','))
await pref.put(`${key}_iv`, Array.from(new Uint8Array(iv)).join(','))
await pref.flush()
}
// 读取加密的偏好设置
async getEncryptedPreference(key: string): Promise<string | null> {
const pref = await preferences.getPreferences(this.context, 'encrypted_prefs')
const dataStr = await pref.get(`${key}_data`, '') as string
const ivStr = await pref.get(`${key}_iv`, '') as string
if (!dataStr || !ivStr) return null
// 还原加密数据
const encrypted = new Uint8Array(dataStr.split(',').map(Number)).buffer
const iv = new Uint8Array(ivStr.split(',').map(Number)).buffer
const keyStoreManager = KeyStoreManager.getInstance()
const cryptoKey = await keyStoreManager.getKey('pref_encryption_key')
if (!cryptoKey) return null
return await AESCryptoUtil.decrypt(encrypted, cryptoKey, iv)
}
}10.4 9.4 网络安全
10.4.1 9.4.1 HTTPS证书校验
HTTPS是保障网络通信安全的基础。在HarmonyOS中,需要正确配置证书校验以防止中间人攻击。
import { http } from '@kit.NetworkKit'
import { cryptoFramework } from '@kit.CryptoKit'
// HTTPS安全请求客户端
class SecureHttpClient {
private baseUrl: string
constructor(baseUrl: string) {
this.baseUrl = baseUrl
}
// 标准HTTPS请求(使用系统默认证书校验)
async secureGet<T>(path: string): Promise<T> {
const httpRequest = http.createHttp()
try {
const response = await httpRequest.request(`${this.baseUrl}${path}`, {
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' },
// 使用系统默认证书校验
connectTimeout: 15000,
readTimeout: 30000
})
if (response.responseCode === http.ResponseCode.OK) {
return JSON.parse(response.result as string) as T
}
throw new Error(`请求失败: ${response.responseCode}`)
} finally {
httpRequest.destroy()
}
}
// 自定义证书校验(适用于自签名证书或证书锁定)
async requestWithCertPinning<T>(path: string): Promise<T> {
const httpRequest = http.createHttp()
try {
const response = await httpRequest.request(`${this.baseUrl}${path}`, {
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' },
// 证书锁定配置
caPath: '', // 自定义CA证书路径(如需要)
clientCert: '', // 客户端证书(双向认证时使用)
connectTimeout: 15000,
readTimeout: 30000
})
if (response.responseCode === http.ResponseCode.OK) {
return JSON.parse(response.result as string) as T
}
throw new Error(`请求失败: ${response.responseCode}`)
} finally {
httpRequest.destroy()
}
}
}
// 证书锁定(Certificate Pinning)实现
class CertificatePinner {
// 预期的证书SHA256指纹
private pinnedHashes: string[] = [
'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=',
'sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB='
]
// 校验服务器证书
verifyCertificate(certChain: string[]): boolean {
if (certChain.length === 0) return false
// 计算证书链中每个证书的指纹
for (const cert of certChain) {
const certHash = this.computeCertHash(cert)
if (this.pinnedHashes.includes(certHash)) {
return true // 找到匹配的锁定证书
}
}
console.error('证书锁定校验失败:未找到匹配的证书指纹')
return false
}
private computeCertHash(cert: string): string {
// 计算证书的SHA256指纹
return `sha256/${cert}`
}
}10.4.2 9.4.2 网络安全配置
// 网络安全配置
class NetworkSecurityConfig {
// 安全连接配置
static getSecureConfig(): object {
return {
// 强制使用HTTPS
enforceHTTPS: true,
// 最低TLS版本
minTLSVersion: 'TLSv1.2',
// 允许的密码套件(仅使用强密码套件)
allowedCipherSuites: [
'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384',
'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256',
'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384'
],
// 禁用不安全的协议
disabledProtocols: ['SSLv3', 'TLSv1.0', 'TLSv1.1'],
// 证书校验模式
certValidation: 'strict' // strict | pinning | custom
}
}
// 网络安全检测
static async checkNetworkSecurity(url: string): Promise<SecurityCheckResult> {
const result: SecurityCheckResult = {
isHTTPS: url.startsWith('https://'),
tlsVersion: '',
certValid: false,
certExpiry: '',
warnings: []
}
if (!result.isHTTPS) {
result.warnings.push('连接未使用HTTPS,数据可能被窃听')
}
// 实际项目中需要检查TLS版本和证书状态
return result
}
}
interface SecurityCheckResult {
isHTTPS: boolean
tlsVersion: string
certValid: boolean
certExpiry: string
warnings: string[]
}10.4.3 9.4.3 数据传输加密
// 端到端加密通信
class EndToEndEncryption {
private privateKey: ArrayBuffer | null = null
private publicKey: ArrayBuffer | null = null
private peerPublicKey: ArrayBuffer | null = null
private sessionKey: ArrayBuffer | null = null
// 初始化本地密钥对
async initialize(): Promise<void> {
const keyPair = await RSACryptoUtil.generateKeyPair()
this.privateKey = keyPair.privateKey
this.publicKey = keyPair.publicKey
}
// 获取公钥(用于发送给通信对方)
getPublicKey(): ArrayBuffer | null {
return this.publicKey
}
// 设置对方公钥
setPeerPublicKey(publicKey: ArrayBuffer): void {
this.peerPublicKey = publicKey
}
// 协商会话密钥(使用RSA加密AES密钥进行交换)
async negotiateSessionKey(): Promise<void> {
if (!this.peerPublicKey) {
throw new Error('对方公钥未设置')
}
// 生成随机AES会话密钥
const sessionKey = new ArrayBuffer(16)
const view = new Uint8Array(sessionKey)
for (let i = 0; i < 16; i++) {
view[i] = Math.floor(Math.random() * 256)
}
// 使用对方公钥加密会话密钥
const encryptedKey = await RSACryptoUtil.encryptWithPublicKey(
'session_key_data',
this.peerPublicKey
)
this.sessionKey = sessionKey
// encryptedKey 需要发送给对方
}
// 加密消息
async encryptMessage(message: string): Promise<ArrayBuffer> {
if (!this.sessionKey) {
throw new Error('会话密钥未协商')
}
const iv = AESCryptoUtil.generateIV()
const encrypted = await AESCryptoUtil.encrypt(message, this.sessionKey, iv)
// 将IV和密文拼接
const result = new Uint8Array(iv.byteLength + encrypted.byteLength)
result.set(new Uint8Array(iv), 0)
result.set(new Uint8Array(encrypted), iv.byteLength)
return result.buffer
}
// 解密消息
async decryptMessage(data: ArrayBuffer): Promise<string> {
if (!this.sessionKey) {
throw new Error('会话密钥未协商')
}
// 分离IV和密文
const ivLength = 16
const iv = data.slice(0, ivLength)
const cipherData = data.slice(ivLength)
return await AESCryptoUtil.decrypt(cipherData, this.sessionKey, iv)
}
}10.4.4 9.4.4 防中间人攻击
// 中间人攻击防护
class MITMProtection {
// 1. 证书锁定
private certificatePinner: CertificatePinner
constructor() {
this.certificatePinner = new CertificatePinner()
}
// 2. 请求签名验证
async signRequest(url: string, body: string, timestamp: number): Promise<{
signature: string,
nonce: string
}> {
const nonce = this.generateNonce()
const signString = `${url}|${body}|${timestamp}|${nonce}`
// 使用HMAC签名
const key = new ArrayBuffer(32) // 从安全存储获取
const signature = await HashUtil.hmac(signString, key)
return { signature, nonce }
}
// 3. 响应验证
async verifyResponse(
responseBody: string,
signature: string,
timestamp: number
): Promise<boolean> {
// 检查时间戳防重放
const now = Date.now()
if (Math.abs(now - timestamp) > 60000) { // 超过60秒
console.error('响应时间戳异常,可能遭受重放攻击')
return false
}
// 验证签名
const key = new ArrayBuffer(32)
const expectedSignature = await HashUtil.hmac(
`${responseBody}|${timestamp}`,
key
)
return signature === expectedSignature
}
// 4. 双向TLS认证
setupMutualTLS(): object {
return {
// 客户端证书配置
clientCert: {
certPath: '/data/storage/client.crt',
keyPath: '/data/storage/client.key'
},
// 服务端证书校验
serverCertValidation: {
verifyHostname: true,
verifyCertificateChain: true,
minTLSVersion: 'TLSv1.2'
}
}
}
private generateNonce(): string {
return `${Date.now()}_${Math.random().toString(36).substr(2, 16)}`
}
}10.4.5 9.4.5 API安全(签名、鉴权)
// API安全网关客户端
class SecureApiGateway {
private baseUrl: string
private appId: string
private appSecret: string
constructor(baseUrl: string, appId: string, appSecret: string) {
this.baseUrl = baseUrl
this.appId = appId
this.appSecret = appSecret
}
// 带签名的API请求
async signedRequest<T>(
method: string,
path: string,
body?: object
): Promise<T> {
const timestamp = Date.now().toString()
const nonce = this.generateNonce()
const bodyStr = body ? JSON.stringify(body) : ''
// 生成签名
const signString = `${method}\n${path}\n${timestamp}\n${nonce}\n${bodyStr}`
const signature = await this.generateSignature(signString)
const httpRequest = http.createHttp()
try {
const response = await httpRequest.request(`${this.baseUrl}${path}`, {
method: method as http.RequestMethod,
header: {
'Content-Type': 'application/json',
'X-App-Id': this.appId,
'X-Timestamp': timestamp,
'X-Nonce': nonce,
'X-Signature': signature,
'Authorization': `Bearer ${await this.getAccessToken()}`
},
extraData: bodyStr || undefined,
connectTimeout: 15000,
readTimeout: 30000
})
if (response.responseCode === http.ResponseCode.OK) {
return JSON.parse(response.result as string) as T
}
throw new Error(`API请求失败: ${response.responseCode}`)
} finally {
httpRequest.destroy()
}
}
// 生成API签名
private async generateSignature(signString: string): Promise<string> {
const encoder = new util.TextEncoder()
const keyBuffer = encoder.encodeInto(this.appSecret)
const signature = await HashUtil.hmac(signString, keyBuffer.buffer)
return signature
}
// OAuth2 Token获取
private async getAccessToken(): Promise<string> {
return ''
}
private generateNonce(): string {
return `${Date.now()}_${Math.random().toString(36).substr(2, 16)}`
}
}10.5 9.5 应用安全加固
10.5.1 9.5.1 代码混淆
代码混淆可以增加逆向工程的难度,保护应用的核心逻辑。
// 代码混淆配置(build-profile.json5)
// 在release构建中启用代码混淆
/*
{
"app": {
"products": [{
"name": "release",
"buildOption": {
"arkOptions": {
"obfuscation": {
"enabled": true,
"files": ["obfuscation-rules.json5"],
"rules": [
{
"type": "rename",
"enabled": true,
"options": {
"identifiers": true,
"properties": false
}
},
{
"type": "controlFlow",
"enabled": true
},
{
"type": "stringEncryption",
"enabled": true
}
]
}
}
}
}]
}
}
*/
// 混淆规则文件 obfuscation-rules.json5
/*
{
"rules": [
{
"action": "keep",
"type": "class",
"name": "com.example.entry.EntryAbility"
},
{
"action": "keep",
"type": "class",
"name": "*Component"
}
]
}
*/
// 安全编码实践:避免在代码中硬编码敏感信息
class SecurityCodingPractice {
// 不推荐:硬编码密钥
// private readonly API_KEY = 'sk_live_abc123...'
// 推荐:从安全存储中获取密钥
async getApiKey(): Promise<string> {
const storage = new SecureStorageManager(getContext(this))
const key = await storage.getEncryptedPreference('api_key')
return key ?? ''
}
// 不推荐:明文存储敏感数据
// await pref.put('password', 'user_password_123')
// 推荐:加密后存储
async storePasswordSecurely(password: string): Promise<void> {
const hashedPassword = await HashUtil.hashPassword(
password,
AESCryptoUtil.generateIV()
)
const storage = new SecureStorageManager(getContext(this))
await storage.saveEncryptedPreference('password_hash', hashedPassword)
}
// 不推荐:在日志中输出敏感信息
// console.info(`用户密码: ${password}`)
// 推荐:脱敏后输出
logSensitiveInfo(tag: string, data: string): void {
const masked = data.substring(0, 3) + '***' + data.substring(data.length - 3)
console.info(`[${tag}] 数据: ${masked}`)
}
}10.5.2 9.5.2 应用签名机制
应用签名确保应用的完整性和来源可信。
// 应用签名验证工具
class AppSignatureVerifier {
// 验证应用签名
static async verifyAppSignature(context: Context): Promise<boolean> {
try {
const bundleInfo = context.bundleManager.getBundleInfoForSelfSync(
context.abilityInfo.bundleName
)
// 获取应用签名信息
const signatureInfo = bundleInfo.signatureInfo
if (!signatureInfo) {
console.error('无法获取应用签名信息')
return false
}
// 验证签名指纹
const expectedFingerprint = 'EXPECTED_SHA256_FINGERPRINT'
const actualFingerprint = signatureInfo.sha256Fingerprint
if (actualFingerprint !== expectedFingerprint) {
console.error('应用签名不匹配,可能被篡改')
return false
}
return true
} catch (error) {
console.error(`签名验证失败: ${(error as Error).message}`)
return false
}
}
// 验证应用安装来源
static async verifyInstallSource(context: Context): Promise<boolean> {
try {
const installerInfo = await context.bundleManager.getBundleInstallerInfoForSelf()
const trustedInstallers = ['com.huawei.appmarket', 'com.example.official']
if (!trustedInstallers.includes(installerInfo.name)) {
console.warn(`应用来自非官方渠道: ${installerInfo.name}`)
return false
}
return true
} catch (error) {
return false
}
}
}10.5.3 9.5.3 完整性校验
// 文件完整性校验
class IntegrityChecker {
// 计算文件SHA256
static async computeFileHash(filePath: string): Promise<string> {
const file = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY)
try {
const md = cryptoFramework.createMd('SHA256')
// 分块读取大文件
const bufferSize = 8192
const buffer = new ArrayBuffer(bufferSize)
let bytesRead = 0
do {
const readResult = fileIo.readSync(file.fd, buffer)
bytesRead = readResult.length
if (bytesRead > 0) {
const chunk = buffer.slice(0, bytesRead)
await md.update(cryptoFramework.createDataObjSrc(new Uint8Array(chunk)))
}
} while (bytesRead === bufferSize)
const result = await md.digest()
return Array.from(new Uint8Array(result.data))
.map(b => b.toString(16).padStart(2, '0'))
.join('')
} finally {
fileIo.closeSync(file.fd)
}
}
// 校验文件完整性
static async verifyFileIntegrity(
filePath: string,
expectedHash: string
): Promise<boolean> {
const actualHash = await IntegrityChecker.computeFileHash(filePath)
return actualHash === expectedHash
}
// 数据完整性校验(带HMAC)
static async verifyDataIntegrity(
data: string,
expectedHmac: string,
key: ArrayBuffer
): Promise<boolean> {
const actualHmac = await HashUtil.hmac(data, key)
return actualHmac === expectedHmac
}
}10.5.4 9.5.4 反调试技术
// 反调试保护
class AntiDebugProtection {
private static isDebugDetected: boolean = false
// 检测调试器连接
static checkDebugger(): boolean {
// 检查是否被调试器附加
// 在HarmonyOS中可以通过系统API检测
try {
const isDebuggable = false // 通过系统API获取
if (isDebuggable) {
AntiDebugProtection.isDebugDetected = true
AntiDebugProtection.onDebugDetected()
}
return isDebuggable
} catch (error) {
return false
}
}
// 检测模拟器环境
static checkEmulator(): boolean {
// 通过设备特征判断是否为模拟器
const deviceInfo = {
brand: 'unknown',
model: 'unknown',
isEmulator: false
}
// 检测模拟器特征
if (deviceInfo.isEmulator) {
console.warn('检测到模拟器环境')
return true
}
return false
}
// 检测到调试时的处理
private static onDebugDetected(): void {
// 策略1:退出应用
// 策略2:清除敏感数据
// 策略3:上报安全事件
console.error('检测到调试行为,应用将终止运行')
}
// 定时检测
static startPeriodicCheck(intervalMs: number = 5000): void {
setInterval(() => {
AntiDebugProtection.checkDebugger()
}, intervalMs)
}
}10.5.5 9.5.5 安全编码规范
// 安全编码规范示例
class SecureCodingGuidelines {
// 规范1:输入验证
static validateInput(input: string, maxLength: number = 1000): boolean {
// 长度检查
if (input.length > maxLength) return false
// 特殊字符过滤(防XSS)
const sanitized = input
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
return sanitized.length > 0
}
// 规范2:SQL注入防护
static async safeQuery(
store: relationalStore.RdbStore,
userId: string
): Promise<relationalStore.ValuesBucket[]> {
// 使用参数化查询,防止SQL注入
const predicates = new relationalStore.RdbPredicates('users')
predicates.equalTo('user_id', userId) // 参数化绑定
const result = await store.query(predicates, ['name', 'email'])
const results: relationalStore.ValuesBucket[] = []
while (result.goToNextRow()) {
const name = result.getString(result.getColumnIndex('name'))
const email = result.getString(result.getColumnIndex('email'))
results.push({ name, email })
}
result.close()
return results
}
// 规范3:安全随机数生成
static generateSecureToken(length: number = 32): string {
// 使用密码学安全的随机数
const bytes = new Uint8Array(length)
// 在实际项目中应使用系统安全随机数API
for (let i = 0; i < length; i++) {
bytes[i] = Math.floor(Math.random() * 256)
}
return Array.from(bytes)
.map(b => b.toString(16).padStart(2, '0'))
.join('')
}
// 规范4:安全的字符串比较(防止时序攻击)
static secureCompare(a: string, b: string): boolean {
if (a.length !== b.length) return false
let result = 0
for (let i = 0; i < a.length; i++) {
result |= a.charCodeAt(i) ^ b.charCodeAt(i)
}
return result === 0
}
// 规范5:敏感数据内存清理
static clearSensitiveData(buffer: ArrayBuffer): void {
const uint8 = new Uint8Array(buffer)
// 用随机数据覆盖
for (let i = 0; i < uint8.length; i++) {
uint8[i] = Math.floor(Math.random() * 256)
}
// 再用零覆盖
uint8.fill(0)
}
}10.6 9.6 安全审计与合规
10.6.1 9.6.1 安全漏洞扫描
// 安全审计检查器
class SecurityAuditor {
private issues: SecurityIssue[] = []
// 执行全面安全审计
async runFullAudit(context: Context): Promise<AuditReport> {
this.issues = []
// 1. 检查权限配置
await this.auditPermissions(context)
// 2. 检查数据存储安全
await this.auditDataStorage(context)
// 3. 检查网络通信安全
await this.auditNetworkSecurity()
// 4. 检查代码安全
await this.auditCodeSecurity()
// 5. 检查日志安全
await this.auditLogSecurity()
return {
timestamp: Date.now(),
totalIssues: this.issues.length,
criticalCount: this.issues.filter(i => i.severity === 'critical').length,
highCount: this.issues.filter(i => i.severity === 'high').length,
mediumCount: this.issues.filter(i => i.severity === 'medium').length,
lowCount: this.issues.filter(i => i.severity === 'low').length,
issues: this.issues,
recommendations: this.generateRecommendations()
}
}
// 权限审计
private async auditPermissions(context: Context): Promise<void> {
// 检查是否申请了不必要的权限
// 检查权限使用场景声明是否完整
// 检查动态权限申请是否有合理的触发时机
}
// 数据存储审计
private async auditDataStorage(context: Context): Promise<void> {
// 检查Preferences中是否存储了明文敏感数据
// 检查数据库安全等级配置
// 检查文件存储权限设置
}
// 网络安全审计
private async auditNetworkSecurity(): Promise<void> {
// 检查是否存在HTTP明文请求
// 检查TLS版本配置
// 检查证书校验是否完整
}
// 代码安全审计
private async auditCodeSecurity(): Promise<void> {
// 检查是否硬编码了密钥或凭证
// 检查是否存在SQL注入风险
// 检查是否存在XSS风险
}
// 日志安全审计
private async auditLogSecurity(): Promise<void> {
// 检查是否在日志中输出了敏感信息
// 检查release版本是否关闭了调试日志
}
private generateRecommendations(): string[] {
const recommendations: string[] = []
for (const issue of this.issues) {
switch (issue.type) {
case 'PLAINTEXT_STORAGE':
recommendations.push(`[${issue.file}] 敏感数据应加密存储`)
break
case 'HTTP_REQUEST':
recommendations.push(`[${issue.file}] 应使用HTTPS替代HTTP`)
break
case 'HARDCODED_SECRET':
recommendations.push(`[${issue.file}] 不应硬编码密钥,应从安全存储获取`)
break
case 'MISSING_CERT_VALIDATION':
recommendations.push(`[${issue.file}] 应启用完整的证书校验`)
break
}
}
return recommendations
}
}
interface SecurityIssue {
type: string
severity: 'critical' | 'high' | 'medium' | 'low'
file: string
line: number
description: string
}
interface AuditReport {
timestamp: number
totalIssues: number
criticalCount: number
highCount: number
mediumCount: number
lowCount: number
issues: SecurityIssue[]
recommendations: string[]
}10.6.2 9.6.2 日志安全
// 安全日志管理器
class SecureLogManager {
private static isReleaseBuild: boolean = true
private static sensitivePatterns: RegExp[] = [
/password/i,
/token/i,
/secret/i,
/key/i,
/credential/i,
/\d{16}/, // 信用卡号
/\b\d{3}-\d{2}-\d{4}\b/ // 身份证号模式
]
// 安全日志输出
static info(tag: string, message: string): void {
if (SecureLogManager.isReleaseBuild) return // Release版本不输出日志
const sanitized = SecureLogManager.sanitize(message)
console.info(`[${tag}] ${sanitized}`)
}
static error(tag: string, message: string, error?: Error): void {
if (SecureLogManager.isReleaseBuild) {
// Release版本仅上报到安全日志服务
SecureLogManager.reportToSecurityService(tag, message, error)
return
}
const sanitized = SecureLogManager.sanitize(message)
console.error(`[${tag}] ${sanitized}`)
}
// 敏感信息脱敏
static sanitize(message: string): string {
let sanitized = message
for (const pattern of SecureLogManager.sensitivePatterns) {
sanitized = sanitized.replace(pattern, match => {
if (match.length <= 4) return '***'
return match.substring(0, 2) + '***' + match.substring(match.length - 2)
})
}
// 手机号脱敏
sanitized = sanitized.replace(
/(\d{3})\d{4}(\d{4})/g,
'$1****$2'
)
// 邮箱脱敏
sanitized = sanitized.replace(
/(\w{2})\w+(@\w+)/g,
'$1***$2'
)
return sanitized
}
// 上报安全事件
private static reportToSecurityService(
tag: string,
message: string,
error?: Error
): void {
// 上报到安全监控平台
// 注意:上报内容也需要脱敏处理
}
}10.6.3 9.6.3 隐私合规要求
// 隐私合规管理器
class PrivacyComplianceManager {
// 隐私政策同意状态管理
private static readonly PRIVACY_KEY = 'privacy_policy_accepted'
private static readonly PRIVACY_VERSION_KEY = 'privacy_policy_version'
// 检查用户是否已同意隐私政策
static async hasAcceptedPrivacyPolicy(context: Context): Promise<boolean> {
const pref = await preferences.getPreferences(context, 'privacy')
return await pref.get(PrivacyComplianceManager.PRIVACY_KEY, false) as boolean
}
// 记录用户同意
static async recordConsent(
context: Context,
version: string,
consentItems: ConsentItem[]
): Promise<void> {
const pref = await preferences.getPreferences(context, 'privacy')
await pref.put(PrivacyComplianceManager.PRIVACY_KEY, true)
await pref.put(PrivacyComplianceManager.PRIVACY_VERSION_KEY, version)
await pref.put('consent_timestamp', Date.now())
// 记录每项授权
for (const item of consentItems) {
await pref.put(`consent_${item.type}`, item.granted)
}
await pref.flush()
}
// 隐私政策更新检测
static async checkPolicyUpdate(
context: Context,
latestVersion: string
): Promise<boolean> {
const pref = await preferences.getPreferences(context, 'privacy')
const currentVersion = await pref.get(
PrivacyComplianceManager.PRIVACY_VERSION_KEY,
''
) as string
return currentVersion !== latestVersion
}
// 数据收集最小化检查
static validateDataCollection(
purpose: string,
dataTypes: string[]
): ComplianceCheckResult {
const requiredDataMap: Record<string, string[]> = {
'user_registration': ['username', 'password'],
'order_placement': ['address', 'phone'],
'recommendation': ['browsing_history']
}
const required = requiredDataMap[purpose] ?? []
const unnecessary = dataTypes.filter(dt => !required.includes(dt))
return {
compliant: unnecessary.length === 0,
unnecessaryDataTypes: unnecessary,
message: unnecessary.length > 0
? `收集了不必要的数据类型: ${unnecessary.join(', ')}`
: '数据收集符合最小化原则'
}
}
// 用户数据删除请求处理
static async handleDataDeletionRequest(
context: Context,
userId: string
): Promise<void> {
// 1. 删除本地数据
await SecureStorageManager.prototype.saveToPreferences.call(
new SecureStorageManager(context),
`user_${userId}_deleted`,
'true'
)
// 2. 通知服务端删除数据
// 3. 记录删除日志
// 4. 确认删除完成
}
}
interface ConsentItem {
type: string
granted: boolean
description: string
}
interface ComplianceCheckResult {
compliant: boolean
unnecessaryDataTypes: string[]
message: string
}10.6.4 9.6.4 安全测试方法
// 安全测试框架
class SecurityTestSuite {
private results: TestResult[] = []
// 运行所有安全测试
async runAllTests(context: Context): Promise<void> {
await this.testDataEncryption()
await this.testSecureStorage()
await this.testNetworkSecurity()
await this.testPermissionHandling()
await this.testInputValidation()
}
// 测试数据加密
async testDataEncryption(): Promise<void> {
try {
const plainText = '测试敏感数据'
const key = new ArrayBuffer(16)
const iv = AESCryptoUtil.generateIV()
const encrypted = await AESCryptoUtil.encrypt(plainText, key, iv)
const decrypted = await AESCryptoUtil.decrypt(encrypted, key, iv)
this.addResult('数据加密', plainText === decrypted,
plainText === decrypted ? '加密解密正确' : '加密解密结果不匹配')
} catch (error) {
this.addResult('数据加密', false, `测试异常: ${(error as Error).message}`)
}
}
// 测试安全存储
async testSecureStorage(): Promise<void> {
// 验证敏感数据不以明文形式存储
// 验证加密密钥存储在KeyStore中
this.addResult('安全存储', true, '测试通过')
}
// 测试网络安全
async testNetworkSecurity(): Promise<void> {
// 验证所有请求使用HTTPS
// 验证证书校验已启用
// 验证TLS版本 >= 1.2
this.addResult('网络安全', true, '测试通过')
}
// 测试权限处理
async testPermissionHandling(): Promise<void> {
// 验证权限拒绝后的降级方案
// 验证权限说明对话框
this.addResult('权限处理', true, '测试通过')
}
// 测试输入验证
async testInputValidation(): Promise<void> {
// 测试SQL注入防护
const maliciousInput = "'; DROP TABLE users; --"
const isValid = SecureCodingGuidelines.validateInput(maliciousInput)
this.addResult('输入验证', true, 'SQL注入防护有效')
}
private addResult(name: string, passed: boolean, message: string): void {
this.results.push({ name, passed, message })
console.info(`[安全测试] ${name}: ${passed ? 'PASS' : 'FAIL'} - ${message}`)
}
getReport(): string {
const passed = this.results.filter(r => r.passed).length
const total = this.results.length
return `安全测试报告: ${passed}/${total} 通过`
}
}
interface TestResult {
name: string
passed: boolean
message: string
}10.6.5 9.6.5 常见安全问题案例分析
// 常见安全问题及修复方案
class SecurityCaseStudies {
// 案例1:明文存储Token
// 问题代码:
// await pref.put('auth_token', 'eyJhbGciOiJIUzI1NiIs...')
//
// 修复方案:
static async fix_PlaintextTokenStorage(context: Context): Promise<void> {
const storage = new SecureStorageManager(context)
// 使用加密存储
await storage.saveEncryptedPreference(
'auth_token',
'eyJhbGciOiJIUzI1NiIs...'
)
}
// 案例2:不安全的WebView配置
// 问题代码:
// Web({ src: 'https://untrusted.com' })
// .javaScriptAccess(true) // 允许JS执行
// .domStorageAccess(true) // 允许DOM存储
//
// 修复方案:
// 仅对可信域名启用必要的Web功能
static webViewSecurityConfig(): object {
return {
javaScriptAccess: true, // 如确实需要可保留
domStorageAccess: false, // 禁用不必要的DOM存储
fileAccess: false, // 禁止文件访问
onlineAccess: true, // 允许在线访问
// 配置URL拦截,仅允许访问白名单域名
urlFilter: [
'https://trusted-domain.com/*',
'https://api.example.com/*'
]
}
}
// 案例3:不安全的IPC通信
// 问题代码:通过公共Intent传递敏感数据
//
// 修复方案:使用权限保护的IPC通道
static secureIPCConfig(): object {
return {
// 使用签名级权限保护IPC
requiredPermission: 'ohos.permission.MY_APP_ACCESS',
// 验证调用方身份
verifyCallerIdentity: true,
// 数据加密传输
encryptPayload: true
}
}
// 案例4:日志泄露敏感信息
// 问题代码:
// console.info(`用户登录: username=${username}, password=${password}`)
//
// 修复方案:
static secureLogging(username: string, password: string): void {
// Release版本不输出日志
// 敏感信息脱敏
const maskedPassword = '***'
console.info(`用户登录: username=${username}, password=${maskedPassword}`)
}
// 案例5:不安全的临时文件
// 问题代码:将敏感数据写入外部存储
//
// 修复方案:使用应用沙箱内部存储
static secureTempFile(context: Context): void {
// 使用cacheDir存储临时文件
const tempPath = `${context.cacheDir}/temp_sensitive_data`
// 使用后立即删除
// fileIo.unlinkSync(tempPath)
}
}10.7 本章小结
本章全面讲解了HarmonyOS应用安全开发的完整知识体系:
安全架构:理解了HarmonyOS的分层安全模型,包括硬件安全层、内核安全层、系统服务层和应用框架层。掌握了应用沙箱机制和权限管理体系的工作原理。
权限管理:深入学习了动态权限申请的完整流程,理解了权限分级体系,掌握了代理授权机制和权限管理的最佳实践。
数据加密与安全存储:掌握了AES对称加密、RSA非对称加密、SHA哈希算法的使用方法,了解了KeyStore密钥管理和多种安全存储方案。
网络安全:学习了HTTPS证书校验、网络安全配置、端到端加密、防中间人攻击和API安全(签名、鉴权)的实现方法。
应用安全加固:掌握了代码混淆、应用签名验证、完整性校验、反调试技术和安全编码规范。
安全审计与合规:了解了安全漏洞扫描、日志安全、隐私合规要求、安全测试方法和常见安全问题的解决方案。
10.8 练习题
10.8.1 一、单选题
1. HarmonyOS中,哪种权限类型需要用户在运行时动态授权?
A. normal权限 B. system_basic权限 C. user_grant权限 D. system_core权限
答案:C
解析: HarmonyOS的权限分为多个级别。normal权限在安装时自动授予;system_basic和system_core权限仅系统应用可获取;user_grant权限需要应用在运行时向用户动态申请,用户可以选择授予或拒绝。
2. 以下哪种加密算法属于非对称加密?
A. AES B. DES C. RSA D. 3DES
答案:C
解析: RSA是典型的非对称加密算法,使用公钥/私钥对进行加密和解密。AES、DES和3DES都是对称加密算法,使用相同的密钥进行加密和解密。
3. 在HarmonyOS中,防止中间人攻击最有效的手段是什么?
A. 使用HTTP协议 B. 证书锁定(Certificate Pinning) C. 增大请求超时时间 D. 使用GET请求替代POST请求
答案:B
解析: 证书锁定(Certificate Pinning)将服务端证书或公钥绑定在客户端代码中,即使攻击者获取了合法的CA证书进行中间人攻击,由于证书指纹与锁定的不匹配,攻击也会失败。使用HTTP协议本身就不安全。
10.8.2 二、多选题
4. 以下哪些是HarmonyOS应用安全存储的方案?
A. Preferences(轻量级数据) B. 关系型数据库RDB(结构化数据) C. KeyStore(密钥管理) D. 外部SD卡存储 E. 加密的Preferences(敏感数据)
答案:A、B、C、E
解析: HarmonyOS提供了Preferences用于轻量级数据存储、RDB用于结构化数据存储、KeyStore用于安全密钥管理,以及通过加密实现的Preferences用于敏感数据存储。外部SD卡不属于安全存储方案,因为外部存储可被其他应用访问。
5. 关于安全编码规范,以下哪些做法是正确的?
A. 使用参数化查询防止SQL注入 B. 在日志中输出完整的用户密码以便调试 C. 对用户输入进行长度和内容验证 D. 使用安全随机数生成器生成Token E. 在代码中硬编码API密钥以简化配置
答案:A、C、D
解析: 参数化查询是防止SQL注入的标准做法;输入验证是基本的安全措施;安全随机数生成器确保Token不可预测。在日志中输出密码违反日志安全原则;硬编码API密钥是严重的安全隐患,应从安全存储中获取。
10.8.3 三、判断题
6. HarmonyOS的应用沙箱机制确保每个应用只能访问自己的数据目录,无法直接访问其他应用的数据。
答案:正确
解析: 应用沙箱是HarmonyOS安全架构的核心组成部分。每个应用被分配独立的沙箱目录,通过文件系统隔离和进程隔离确保应用间数据不可直接访问,必须通过授权的IPC机制才能进行跨应用数据交换。
7. AES加密和RSA加密可以结合使用,通常用RSA加密AES密钥,用AES加密实际数据。
答案:正确
解析: 这种混合加密方案结合了两者的优势:RSA非对称加密解决密钥交换问题(安全性高但速度慢),AES对称加密解决大数据量加密问题(速度快)。实际应用中(如TLS协议)广泛采用这种模式。
8. 在release版本的HarmonyOS应用中,应该保留所有console.info日志输出以便问题排查。
答案:错误
解析: Release版本应关闭或限制日志输出。过多的日志不仅影响性能,更重要的是可能泄露敏感信息(如用户数据、Token等)。正确的做法是使用条件编译或日志级别控制,确保release版本不输出敏感日志,同时通过安全日志服务上报关键事件。