HarmonyOS 6.1 安全攻防实战:从“裸奔”到“防弹”的代码级防护
系列安全加固篇·第48篇。AI融合篇后有安全专家问“Demo功能这么全但代码有没有被反编译的风险用户数据会不会被中间人劫持界面会不会被恶意APP覆盖钓鱼” 安全是应用的底线。今天我们将电商Demo进行一次彻底的安全“体检”和“加固”覆盖代码混淆、防逆向、数据加密、防界面劫持、证书锁定、Root检测等关键领域。我们将使用DevEco Studio安全插件和华为安全检测服务模拟黑客攻击并修复漏洞。全程基于API23含官方文档未涉及的“Native层防护”和“运行时自检”技巧。一、前言为什么“功能完整”不等于“可以上线”很多开发者认为“只要功能跑通了加个HTTPS就是安全”。现实是残酷的静态分析黑客用jadx反编译你的HAP包秒读你的API Key和加密逻辑。动态调试用fridahook你的登录函数绕过密码验证。中间人攻击用Charles抓包看光你的所有网络请求。界面劫持恶意APP覆盖你的支付界面诱导用户输入密码。安全原则永远不要相信客户端环境。今天的实战我们将假设客户端运行在“敌占区”通过层层防御即使代码被反编译数据被拦截也能将损失降到最低。二、核心概念辨析常见安全误区误区真相正确做法HTTPS安全HTTPS只加密传输不防客户端破解代码混淆数据二次加密前端验证安全前端验证可被绕过关键逻辑必须后端验证硬编码密钥方便硬编码密钥等于没加密使用KeyStore/Enclave存储日志调试高效日志可能泄露敏感信息发布版移除所有调试日志权限越多功能越强过多权限增加攻击面最小权限原则三、代码实现从“裸奔”到“防弹”3.1 代码混淆与字符串加密防止反编译后直接看到明文逻辑。步骤1开启混淆在build-profile.json5中配置{ buildOption: { arkOptions: { obfuscation: { enable: true, // 开启混淆 rules: ./obfuscation-rules.txt // 混淆规则文件 } } } }步骤2编写混淆规则 (obfuscation-rules.txt)# 保留ArkUI组件否则界面会白屏 -keep class * extends ohos.arkui.UIComponent { *; } -keep class * extends ohos.arkui.Component { *; } # 保留Entry注解的类 -keep ohos.arkui.annotation.Entry class * { *; } # 保留Native方法 -keepclasseswithmembernames class * { native methods; } # 混淆所有本地变量名和方法名 -obfuscate_names -obfuscate_methods -obfuscate_fields # 移除日志关键 -assumenosideeffects class ohos.hiviewdfx.HiLog { public static *** d(...); public static *** i(...); public static *** w(...); }步骤3字符串加密自定义脚本创建scripts/encrypt_strings.js在构建前加密敏感字符串// 简单的异或加密示例 const crypto require(crypto); function xorEncrypt(text, key) { let result ; for (let i 0; i text.length; i) { result String.fromCharCode(text.charCodeAt(i) ^ key.charCodeAt(i % key.length)); } return Buffer.from(result).toString(base64); } // 加密API Key const apiKey your_real_api_key_here; const encryptedKey xorEncrypt(apiKey, secret_key); console.log(Encrypted API Key: ${encryptedKey}); // 在ETS代码中使用解密函数 // const decryptedKey xorDecrypt(encryptedKey, secret_key);3.2 数据安全多层加密与KeyStore错误示范// 明文存储密码 preferences.putSync(password, 123456);正确做法import { cryptoFramework } from kit.CryptoArchitectureKit import { huks } from kit.UniversalKeyStoreKit export class SecureStorage { private static readonly KEY_ALIAS user_password_key private static readonly TRANSFORMATION AES/GCM/NoPadding /** * 生成并存储AES密钥到HUKS硬件安全环境 */ async generateAndStoreKey(): Promisevoid { const keyProperties { alg: huks.HuksKeyAlg.HUKS_ALG_AES, size: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256, purpose: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT, digest: huks.HuksKeyDigest.HUKS_DIGEST_SHA256, padding: huks.HuksKeyPadding.HUKS_PADDING_NONE, blockMode: huks.HuksKeyMode.HUKS_MODE_GCM } const options { properties: [keyProperties] } try { await huks.generateKey(SecureStorage.KEY_ALIAS, options) console.log(密钥已安全存储在HUKS中) } catch (err) { console.error(密钥生成失败:, err) } } /** * 加密数据 */ async encrypt(data: string): Promisestring { const cipher cryptoFramework.createCipher(SecureStorage.TRANSFORMATION) const key await this.getKey() const iv cryptoFramework.createRandomIv(12) // GCM模式需要12字节IV await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, key, iv) const inputData { data: new Uint8Array(new TextEncoder().encode(data)) } const encryptedData await cipher.doFinal(inputData) // 将IV和密文一起存储 const result new Uint8Array(iv.data.length encryptedData.data.length) result.set(iv.data, 0) result.set(encryptedData.data, iv.data.length) return Buffer.from(result).toString(base64) } /** * 解密数据 */ async decrypt(base64Data: string): Promisestring { const data Buffer.from(base64Data, base64) const iv data.slice(0, 12) const encrypted data.slice(12) const cipher cryptoFramework.createCipher(SecureStorage.TRANSFORMATION) const key await this.getKey() const ivParam { data: new Uint8Array(iv) } await cipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, key, ivParam) const encryptedParam { data: new Uint8Array(encrypted) } const decryptedData await cipher.doFinal(encryptedParam) return new TextDecoder().decode(decryptedData.data) } /** * 从HUKS获取密钥 */ private async getKey(): PromisecryptoFramework.SymKey { const keyBlob await huks.getKey(SecureStorage.KEY_ALIAS, { properties: [] }) return cryptoFramework.convertKey(keyBlob) } }3.3 网络安全证书锁定与双向认证防止中间人攻击MitM。步骤1证书锁定Certificate Pinning在module.json5中配置{ module: { metadata: [ { name: network-security-config, resource: $profile:network_security_config } ] } }创建resources/base/profile/network_security_config.xml?xml version1.0 encodingutf-8? network-security-config domain-config cleartextTrafficPermittedfalse domain includeSubdomainstrueapi.yourdomain.com/domain pin-set expiration2024-12-31 !-- 你的域名证书公钥哈希Base64编码 -- pin digestSHA-256AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/pin !-- 备份证书公钥哈希 -- pin digestSHA-256BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB/pin /pin-set !-- 强制使用证书锁定 -- trust-anchors certificates srcraw/your_certificate/ /trust-anchors /domain-config /network-security-config步骤2双向SSL认证import { http } from kit.NetworkKit import { cryptoCert } from kit.CryptoArchitectureKit async function secureRequest(): Promisevoid { const httpRequest http.createHttp() // 加载客户端证书P12文件 const clientCert await fileIo.readFile(getContext().filesDir /client.p12) const certBlob: cryptoCert.EncodingBlob { data: new Uint8Array(clientCert), encoding: cryptoCert.EncodingFormat.FORMAT_PKCS12 } // 配置双向认证 const options: http.HttpRequestOptions { method: http.RequestMethod.POST, header: { Content-Type: application/json }, caCert: -----BEGIN CERTIFICATE-----\n...服务器证书...\n-----END CERTIFICATE-----, clientCert: certBlob, clientCertPassword: cert_password, // 证书密码 sslVerifyMode: http.SslVerifyMode.VERIFY_PEER | http.SslVerifyMode.VERIFY_HOSTNAME } try { const response await httpRequest.request(https://api.yourdomain.com/secure-endpoint, options) console.log(安全请求成功:, response.result) } catch (err) { console.error(安全请求失败:, err) // 证书验证失败可能是中间人攻击 promptAction.showToast({ message: 网络连接不安全请检查网络环境 }) } }3.4 运行时防护防调试、防Root、防注入检测调试器import { hidebug } from kit.PerformanceAnalysisKit export class RuntimeProtection { /** * 检测是否被调试 */ static isBeingDebugged(): boolean { try { // hidebug.isDebuggerConnected() 返回是否连接调试器 return hidebug.isDebuggerConnected() } catch (err) { console.error(检测调试器失败:, err) return false } } /** * 检测设备是否已Root/Jailbreak */ static isDeviceRooted(): Promiseboolean { return new Promise((resolve) { // 检查常见Root路径 const rootPaths [ /system/bin/su, /system/xbin/su, /sbin/su, /vendor/bin/su, /system/app/Superuser.apk ] let foundRoot false rootPaths.forEach(path { try { fileIo.accessSync(path) foundRoot true } catch (e) { // 文件不存在 } }) // 检查是否可以写入系统目录 try { const testFile /system/test_rooted fileIo.writeSync(fileIo.openSync(testFile, fileIo.OpenMode.WRITE_ONLY), new Uint8Array([1])) fileIo.unlinkSync(testFile) foundRoot true } catch (e) { // 无法写入 } resolve(foundRoot) }) } /** * 检测是否被Frida等工具注入 */ static isInjected(): boolean { // 检查/proc/self/maps中是否存在frida相关模块 try { const maps fileIo.readTextSync(/proc/self/maps) if (maps.includes(frida) || maps.includes(gadget)) { return true } } catch (err) { // 无法读取maps文件 } return false } /** * 运行时自检 */ static async selfCheck(): Promiseboolean { console.log(开始运行时自检...) if (this.isBeingDebugged()) { console.error(检测到调试器连接) return false } if (await this.isDeviceRooted()) { console.warn(设备已Root安全性降低) // 可以选择降低功能级别而非直接退出 } if (this.isInjected()) { console.error(检测到代码注入) return false } console.log(运行时自检通过) return true } }3.5 防界面劫持Overlay Attackimport { window } from kit.ArkUI import { abilityAccessCtrl } from kit.AbilityKit Entry Component struct SecurePaymentPage { private windowClass: window.Window | null null aboutToAppear(): void { this.protectAgainstOverlay() } /** * 防止界面被覆盖 */ async protectAgainstOverlay(): Promisevoid { try { // 1. 获取窗口实例 this.windowClass await window.getLastWindow(getContext(this)) // 2. 设置窗口属性防止被其他应用覆盖 // 注意HarmonyOS没有直接等同于Android SYSTEM_ALERT_WINDOW的权限 // 但可以通过以下方式增强安全性 // 设置窗口为全屏减少被覆盖的可能性 this.windowClass.setWindowLayoutFullScreen(true) // 设置窗口焦点变化监听 this.windowClass.on(windowEvent, (data) { if (data window.WindowEventType.WINDOW_EVENT_FOCUS_OUT) { // 窗口失去焦点可能被其他应用覆盖 this.onWindowFocusLost() } }) // 3. 检查悬浮窗权限如果应用使用了悬浮窗 const atManager abilityAccessCtrl.createAtManager() const result await atManager.checkAccessToken( abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED, ohos.permission.SYSTEM_FLOAT_WINDOW ) if (result ! abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { console.log(未授予悬浮窗权限降低被覆盖风险) } } catch (err) { console.error(防覆盖设置失败:, err) } } onWindowFocusLost(): void { // 窗口失去焦点时的处理逻辑 console.warn(支付页面失去焦点可能存在安全风险) promptAction.showToast({ message: 警告页面失去焦点请确认环境安全 }) // 可以选择暂时模糊页面内容 this.blurContent() } blurContent(): void { // 实现页面模糊效果 console.log(模糊页面内容以保护隐私) } build() { Column() { Text(安全支付页面) .fontSize(24) .margin(20) Text(请确认当前页面为官方应用警惕仿冒界面) .fontSize(14) .fontColor(#FF0000) .margin(10) // ... 支付表单 } .width(100%) .height(100%) .backgroundColor(#FFFFFF) } }四、踩坑记录官方文档没写的安全细节混淆导致的“灵异bug”混淆后ArkUI的State变量名可能被改变导致UI不更新。解决方案在混淆规则中保留所有State、Prop、Link等装饰器修饰的变量-keepclassmembers class * { ohos.arkui.State *; }。HUKS的性能陷阱每次加密解密都从HUKS获取密钥会有性能开销。解决方案在应用启动时获取一次密钥缓存在内存中注意内存清理或使用密钥派生函数KDF从主密钥派生会话密钥。证书锁定的维护噩梦证书过期或更换会导致应用无法联网。解决方案在pin-set中配置备份证书实现证书轮换机制在旧证书过期前通过云端下发新证书指纹。Root检测的误报某些安全软件或系统更新可能导致Root检测误报。解决方案不要仅依赖单一检测方法结合多种检测手段检测到Root时采取降级处理如禁用敏感功能而非直接崩溃。Native层防护的必要性JS代码容易被反编译核心安全逻辑如签名验证、密钥派生应放在Native层C/C实现并通过NAPI调用。虽然Native代码也能被逆向但难度呈指数级上升。

相关新闻

ASE复刻《原神》风格草地:从Shader原理到性能优化全解析

ASE复刻《原神》风格草地:从Shader原理到性能优化全解析

1. 项目概述:为什么选择ASE复刻《原神》风格草地? 如果你在Unity里做过场景,尤其是开放世界或者风格化渲染,肯定对《原神》里那片随风摇曳、色彩层次丰富的草地印象深刻。那种草地的灵动感,远不是贴一张草皮纹理就能搞…

2026/10/28 16:38:21 阅读更多 →
HarmonyOS 6.1 AI深度融合:从“功能”到“智能”的大模型落地

HarmonyOS 6.1 AI深度融合:从“功能”到“智能”的大模型落地

系列智能化篇第47篇。全场景篇后,有产品经理问:“现在的电商Demo功能齐全了,但感觉还是‘死’的。能不能接入AI,让它像真人客服一样聊天,或者自动生成商品文案?” 这正是生成式AI(GenAI&#xf…

2026/10/28 16:38:20 阅读更多 →
18650电池片定制实力企业

18650电池片定制实力企业

找18650电池片定制,为何实力企业是品质的“压舱石”?在新能源浪潮席卷全球的今天,18650电池片作为动力与储能的基石,其定制化需求正变得前所未有的旺盛。从智能小家电到电动工具,从便携式储能到精密仪器,一…

2026/10/28 17:18:11 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/28 23:48:14 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/29 8:45:01 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/29 9:52:56 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/28 22:10:30 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/29 9:51:34 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/29 8:44:28 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/29 17:01:10 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/29 9:42:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/28 19:25:09 阅读更多 →