在应用被逆向、数据被窃取的案例层出不穷的今天,安全不再是上线前"补一刀"的可选项,而是架构设计阶段就要考虑的基础能力。HarmonyOS 在系统层提供了相当完整的安全设施:HUKS 密钥管理、Asset 关键资产存储、加密算法框架、代码混淆与完整性校验等。本文从实战出发,把这些能力串成一条完整的数据保护链路:敏感数据怎么存、密钥怎么管、传输怎么防、代码怎么护。
动手写代码之前,先做数据分类。一个典型应用中的数据大致分四档:
不同档位对应不同的存储方案,这是后面所有技术选型的依据。把所有数据一股脑 AES 加密,或者把 token 明文丢进 Preferences,都是典型的分级缺失。
很多开发者习惯把 token 存进 Preferences,这在 HarmonyOS 上不是最优解。系统提供了专门的关键资产存储服务(Asset Store Kit),底层由系统级加密保护,支持"仅解锁后可访问""绑定生物认证"等访问控制策略。
import{asset}from'@kit.AssetStoreKit';
import{util}from'@kit.ArkTS';
functionstringToArray(str:string):Uint8Array{
returnnewutil.TextEncoder().encodeInto(str);
}
asyncfunctionsaveToken(token:string):Promise<void>{
constattr:asset.AssetMap=newMap();
attr.set(asset.Tag.SECRET,stringToArray(token));
attr.set(asset.Tag.ALIAS,stringToArray('user_token'));
// 仅在设备首次解锁后可访问
attr.set(asset.Tag.ACCESSIBILITY,asset.Accessibility.DEVICE_FIRST_UNLOCKED);
// 应用卸载后自动清除
attr.set(asset.Tag.IS_PERSISTENT,false);
try{
awaitasset.add(attr);
}catch(err){
// 已存在则走更新逻辑
if(err.code===24000003){
awaitupdateToken(token);
}
}
}
asyncfunctionreadToken():Promise<string|null>{
constquery:asset.AssetMap=newMap();
query.set(asset.Tag.ALIAS,stringToArray('user_token'));
query.set(asset.Tag.RETURN_TYPE,asset.ReturnType.ALL);
try{
constres=awaitasset.query(query);
constsecret=res[0].get(asset.Tag.SECRET)asUint8Array;
returnnewutil.TextDecoder().decodeToString(secret);
}catch{
returnnull;
}
}几个实战要点:
ACCESSIBILITY 选 DEVICE_FIRST_UNLOCKED 是多数场景的平衡点:开机未解锁时不可读,但后台任务可用REQUIRE_PASSWORD_SET 或绑定生物认证(AUTH_TYPE),支付类凭据建议上认证绑定对需要加密的业务数据,正确姿势是密钥永远不落地:密钥生成、存储、使用全部在 HUKS(HarmonyOS Universal KeyStore)内完成,应用只拿句柄,拿不到密钥原文。
import{huks}from'@kit.UniversalKeystoreKit';
constKEY_ALIAS='biz_data_key';
functiongetAesGenProperties():huks.HuksParam[]{
return[
{tag:huks.HuksTag.HUKS_TAG_ALGORITHM,value:huks.HuksKeyAlg.HUKS_ALG_AES},
{tag:huks.HuksTag.HUKS_TAG_KEY_SIZE,value:huks.HuksKeySize.HUKS_AES_KEY_SIZE_256},
{tag:huks.HuksTag.HUKS_TAG_PURPOSE,
value:huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT|huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT},
{tag:huks.HuksTag.HUKS_TAG_BLOCK_MODE,value:huks.HuksCipherMode.HUKS_MODE_GCM},
{tag:huks.HuksTag.HUKS_TAG_PADDING,value:huks.HuksKeyPadding.HUKS_PADDING_NONE},
];
}
asyncfunctionensureKey():Promise<void>{
constexist=awaithuks.isKeyItemExist(KEY_ALIAS,{properties:[]})
.catch(()=>false);
if(!exist){
awaithuks.generateKeyItem(KEY_ALIAS,{properties:getAesGenProperties()});
}
}asyncfunctionencrypt(plain:Uint8Array,nonce:Uint8Array):Promise<Uint8Array>{
constprops:huks.HuksParam[]=[
{tag:huks.HuksTag.HUKS_TAG_ALGORITHM,value:huks.HuksKeyAlg.HUKS_ALG_AES},
{tag:huks.HuksTag.HUKS_TAG_PURPOSE,value:huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT},
{tag:huks.HuksTag.HUKS_TAG_KEY_SIZE,value:huks.HuksKeySize.HUKS_AES_KEY_SIZE_256},
{tag:huks.HuksTag.HUKS_TAG_BLOCK_MODE,value:huks.HuksCipherMode.HUKS_MODE_GCM},
{tag:huks.HuksTag.HUKS_TAG_PADDING,value:huks.HuksKeyPadding.HUKS_PADDING_NONE},
{tag:huks.HuksTag.HUKS_TAG_NONCE,value:nonce},
];
constsession=awaithuks.initSession(KEY_ALIAS,{properties:props});
constresult=awaithuks.finishSession(session.handle,{properties:props,inData:plain});
returnresult.outDataasUint8Array;
}要点:
cryptoFramework.createRandom(),不要用 Math.random()updateSession 分段处理,避免一次性把整个文件读进内存relationalStore 支持开箱即用的加密,一行配置的事情不要自己造轮子:
constconfig:relationalStore.StoreConfig={
name:'app.db',
securityLevel:relationalStore.SecurityLevel.S3,
encrypt:true,// 数据库文件加密
};securityLevel(S1~S4)决定数据的安全分级,影响分布式场景下能否跨设备同步——高安全级数据不会流向低安全级设备。这是 HarmonyOS 特有的数据流转管控,分布式应用一定要正确标注。
HTTPS 是底线,但公共 Wi-Fi 下的中间人攻击、用户手动安装的代理证书,都可能让"合法"的 TLS 被旁路。对金融、账号类接口,建议加证书锁定(Certificate Pinning):
// src/main/resources/rawfile/untrusted_certs.json (网络配置)
{
"network-security-config":{
"domain-config":[
{
"domains":[{"include-subdomains":true,"name":"api.example.com"}],
"pin-set":{
"expiration":"2027-08-01",
"pin":[
{"digest-algorithm":"sha256","digest":"base64编码的公钥哈希=="}
]
}
}
]
}
}实战建议:
expiration,过期后降级为普通校验,给自己留退路rejectUnauthorized: false 之类的"临时方案"进主干DevEco Studio 的 release 构建默认支持 ArkTS 字节码混淆,在模块的 build-profile.json5 中确认:
"arkOptions":{
"obfuscation":{
"ruleOptions":{
"enable":true,
"files":["./obfuscation-rules.txt"]
}
}
}obfuscation-rules.txt 中按需开启更强的选项:
-enable-property-obfuscation
-enable-toplevel-obfuscation
-enable-filename-obfuscation
# 反射访问、SO 桥接的符号要 keep
-keep-global-name
MyNativeBridge混淆后务必留存 nameCache 映射文件,否则线上崩溃堆栈没法还原——这一步和崩溃分析体系是配套的。
对安全敏感的应用,可以在启动时做环境自检:
import{bundleManager}from'@kit.AbilityKit';
asyncfunctionselfCheck():Promise<boolean>{
constinfo=awaitbundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SIGNATURE_INFO);
constfingerprint=info.signatureInfo.fingerprint;
// 与预置指纹比对,发现重签名直接拒绝运行核心功能
returnfingerprint===EXPECTED_FINGERPRINT;
}配合服务端做二次校验(把签名指纹随登录请求上报,服务端比对)比纯本地判断更可靠——本地判断逻辑本身也可能被篡改。
安全加固的最后一环是合规:
APPROXIMATELY_LOCATION)就别上精准定位window.setWindowPrivacyMode(true) 防截屏录屏IS_PERSISTENT 设为 false 保证卸载即清环节 | 方案 | 关键点 |
|---|---|---|
凭据存储 | Asset Store Kit | 访问控制策略 + 卸载清除 |
业务数据加密 | HUKS + AES-GCM | 密钥不出 TEE,nonce 随机 |
数据库 | relationalStore encrypt | securityLevel 正确标注 |
传输 | HTTPS + 证书锁定 | 锁公钥、配备用 pin |
代码 | 混淆 + 签名自检 | keep 规则 + nameCache 留存 |
隐私 | 最小化 + 脱敏 + 防截屏 | privacyMode、日志治理 |
安全是体系不是功能:存储、传输、代码、合规四条线都补齐,才算真正把用户数据放进了保险箱。建议从"数据分类"开始落地,先把关键凭据迁到 Asset、把数据库加密打开,这两步成本最低、收益最大,然后再逐步推进证书锁定与代码加固。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。