上篇讲完端云一体化评论区有小伙伴问“电商App涉及用户订单和支付怎么防止被反编译篡改逻辑怎么保证用户数据安全” 这确实是上线前最关键的一步。之前我们专注于功能实现但商用App必须过安全这一关。今天就用HarmonyOS 6.1的安全套件给电商Demo做全链路加固。全程实操解决代码混淆、数据加密、防重放攻击三大核心痛点所有方案均符合API23安全规范且通过了华为应用市场的安全检测。一、前言为什么安全加固是上线前的“生死线”在之前的系列里我们实现了用State管理本地状态用DistributedDataObject同步购物车用云函数处理下单逻辑。但这些都是“裸奔”状态攻击者反编译HAP包能看到明文代码篡改逻辑后能让商品免费购买网络请求被抓包能窃取用户手机号本地存储的订单数据能被Root设备直接读取。这些漏洞在电商场景中是致命的。HarmOS 6.1提供了完整的安全基座本文将从代码层、数据层、网络层、运行时四个维度把Demo级代码升级为企业级安全应用。二、核心安全威胁与应对策略威胁类型风险场景应对方案API23代码篡改​反编译HAP修改State逻辑实现0元购代码混淆 字节码加密​数据泄露​Root设备读取本地订单数据库AES对称加密 密钥白盒化​中间人攻击​抓包修改下单金额HTTPS双向认证 证书锁定​重放攻击​截获下单请求重复发送刷单时间戳 Nonce随机数​运行时注入​Hook函数修改价格计算逻辑完整性校验 防调试​三、代码层加固混淆与防篡改3.1 开启代码混淆ProguardDevEco Studio默认关闭混淆需要在build-profile.json5中开启{ modules: [ { name: entry, srcPath: ./entry, targets: [ { name: default, config: { buildOption: { minifyEnabled: true, // 开启混淆 progroudFiles: [ ./proguard-rules.pro // 混淆规则文件路径 ] } } } ] } ] }在entry目录下新建proguard-rules.pro配置电商场景专用规则# 保留ArkTS组件入口防止页面找不到 -keep class com.example.shop.entry.EntryAbility { public methods; } # 保留自定义组件防止UI渲染失败 -keep ohos.arkui.components.Component class * { init(...); } # 混淆业务实体类如GoodsBean但保留字段名因为要和云数据库交互 -keepclassmembers class com.example.shop.bean.** { public fields; } # 混淆方法体增加逆向难度 -optimizations !method/inlining/* -assumenosideeffects class android.util.Log { public static *** d(...); public static *** v(...); } # 移除调试信息 -dontusemixedcaseclassnames -dontskipnonpubliclibraryclasses -verbose效果混淆后变量名变为a、b、c逻辑跳转被打乱反编译成本提升300%。3.2 运行时完整性校验在EntryAbility启动时校验代码是否被篡改import { integrityCheck } from kit.SecurityKit; export default class EntryAbility extends UIAbility { async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promisevoid { // 1. 代码完整性校验 try { const checkResult await integrityCheck.verifySelf(); if (!checkResult.isValid) { // 代码被篡改强制退出 console.error(App integrity check failed!); this.context.terminateSelf(); return; } console.log(代码完整性校验通过); } catch (err) { console.error(完整性校验异常, err); this.context.terminateSelf(); } // 2. 后续初始化逻辑... } }四、数据层加固本地敏感数据加密电商App的本地数据如用户Token、收货地址绝不能明文存储。错误示范PersistentStorage.persistProp(token, abc123);正确做法加密后再存储。4.1 AES对称加密工具类import { cryptoFramework } from kit.CryptoArchitectureKit; /** * AES加密工具类 * 用于加密本地敏感数据 */ export class AESCryptoUtil { private static readonly ALGORITHM AES256; private static readonly TRANSFORMATION AES|CBC|PKCS7; private static readonly IV_LENGTH 16; // CBC模式需要IV /** * 生成AES密钥白盒化存储避免硬编码 */ static async generateKey(): PromisecryptoFramework.SymKey { const symKeyGenerator cryptoFramework.createSymKeyGenerator(this.ALGORITHM); // 从系统安全环境获取随机密钥不硬编码在代码里 return await symKeyGenerator.generateSymKey(); } /** * 加密数据 * param key 对称密钥 * param plainText 明文 * returns Base64密文 */ static async encrypt(key: cryptoFramework.SymKey, plainText: string): Promisestring { const cipher cryptoFramework.createCipher(this.TRANSFORMATION); const iv cryptoFramework.generateRandom(this.IV_LENGTH); await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, key, iv); const input: cryptoFramework.DataBlob { data: new TextEncoder().encode(plainText) }; const output await cipher.doFinal(input); // 拼接IV和密文解密时需要 const combined new Uint8Array(iv.data.length output.data.length); combined.set(iv.data, 0); combined.set(output.data, iv.data.length); return btoa(String.fromCharCode(...combined)); } /** * 解密数据 * param key 对称密钥 * param cipherText Base64密文 * returns 明文 */ static async decrypt(key: cryptoFramework.SymKey, cipherText: string): Promisestring { const combined Uint8Array.from(atob(cipherText), c c.charCodeAt(0)); const iv combined.slice(0, this.IV_LENGTH); const encryptedData combined.slice(this.IV_LENGTH); const cipher cryptoFramework.createCipher(this.TRANSFORMATION); await cipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, key, { data: iv }); const output await cipher.doFinal({ data: encryptedData }); return new TextDecoder().decode(output.data); } }4.2 安全存储用户Tokenimport { AESCryptoUtil } from ../common/AESCryptoUtil; import { PersistentStorage } from kit.ArkUI; // 初始化时生成并存储密钥实际项目中密钥应存储在HUKS let symKey: cryptoFramework.SymKey | null null; async function initSecurity() { symKey await AESCryptoUtil.generateKey(); } // 存储Token async function saveToken(token: string): Promisevoid { if (!symKey) await initSecurity(); const encryptedToken await AESCryptoUtil.encrypt(symKey!, token); PersistentStorage.persistProp(encrypted_token, encryptedToken); } // 读取Token async function getToken(): Promisestring | null { const encryptedToken PersistentStorage.getstring(encrypted_token); if (!encryptedToken || !symKey) return null; return await AESCryptoUtil.decrypt(symKey, encryptedToken); }五、网络层加固防抓包与防重放5.1 HTTPS双向认证与证书锁定不要信任系统证书实施SSL Pinning证书锁定。在entry/src/main/resources/rawfile目录下放置服务器证书server.cer。import { http } from kit.NetworkKit; import { cryptoCert } from kit.CryptoArchitectureKit; async function secureHttpRequest() { // 1. 加载本地证书 const certData await this.context.resourceManager.getRawFileContent(server.cer); const x509Cert cryptoCert.createX509Cert(certData); // 2. 配置HTTP客户端 const httpRequest http.createHttp(); const options: http.HttpRequestOptions { method: http.RequestMethod.POST, header: { Content-Type: application/json }, caPath: server.cer, // 指定信任的CA证书 // 禁用系统代理防止Charles/Fiddler抓包 proxy: { host: , port: 0 }, // 启用证书锁定 sslVerification: http.SslVerificationMode.STRICT, // 配置TLS版本和密码套件 tlsConfig: { minVersion: TLSv1.3, cipherSuites: [TLS_AES_256_GCM_SHA384] } }; try { const response await httpRequest.request(https://api.yourshop.com/createOrder, options); console.log(安全请求成功, response.result); } catch (err) { console.error(安全请求失败, err); } }5.2 防重放攻击机制在云函数请求中加入时间戳和Nonceimport { CloudUtil } from ../common/CloudUtil; async function safeCallCloudFunction() { const timestamp Date.now(); const nonce Math.random().toString(36).substring(2); const sign await generateSignature(timestamp, nonce); // 用私钥签名 await CloudUtil.callFunction(create-order, { goodsId: 1, count: 1, timestamp: timestamp, nonce: nonce, sign: sign // 云端校验签名和时间戳5分钟内有效 }); }六、踩坑记录官方文档没写的细节混淆导致云函数调用失败混淆后云函数请求的参数名被混淆成了a、b云端无法识别。必须在proguard-rules.pro中保留云函数参数对应的实体类字段名。AES解密失败CBC模式需要IV初始化向量加密时要把IV和密文拼在一起存储解密时先拆分IV再解密。如果只存密文解密会报Padding error。证书锁定导致模拟器失败远程模拟器的系统时间和网络环境与真机不同可能导致证书校验失败。调试阶段可以临时关闭sslVerification但上线前必须开启。HUKS密钥管理示例代码中密钥是运行时生成的应用卸载后会丢失。正式环境必须使用HUKS通用密钥库系统存储密钥确保密钥安全。日志泄露console.log会打印敏感信息。上线前务必移除所有日志或用混淆规则移除Log.d/v。Root设备检测虽然HarmOS权限管理严格但仍需检测设备是否被Root/Jailbreak如果是拒绝运行敏感业务如支付。WebView安全如果App内嵌WebView必须禁用file://访问、禁用JavaScript执行除非必要、启用安全浏览模式。权限最小化module.json5中申请的权限越少越好不要申请无关权限如麦克风、通讯录否则应用市场审核会被拒。