• [开发技术领域专区] 开发者技术支持-防截图录屏功能适配
    1 问题说明(一)原生隐私保护能力分散​ 鸿蒙系统提供的隐私保护功能(如禁止截屏、录屏)主要通过 WindowManager 和 Window 相关API实现,但这些能力分散在不同模块中,开发者需要:手动获取当前窗口实例(getLastWindow)调用 setWindowPrivacyMode 设置隐私模式处理异步操作的成功/失败回调管理隐私模式的开启/关闭时机(二)适配成本高每个需要隐私保护的页面都需要重复编写相同的窗口操作代码,包括:窗口实例获取与异常处理隐私模式状态管理页面生命周期与隐私模式的联动用户提示与错误日志记录(三)用户体验反馈不明确原生API缺乏用户友好的状态提示机制,开发者需要额外实现:隐私模式开启/关闭的用户提示操作失败时的错误提示隐私状态的可视化反馈2 原因分析(一)原生能力通用化程度不足鸿蒙系统的隐私保护API设计更偏向底层能力提供,缺乏面向业务场景的高级封装,导致:重复代码问题:每个页面都需要编写相似的窗口操作逻辑一致性难保证:不同开发者的实现方式可能存在差异维护成本高:API变更时需要修改多处代码(二)缺乏统一的工具链支持在 harmony-utils 三方库出现前,开发者面临:Toast提示不统一:需要自行实现提示逻辑,样式和交互可能不一致日志记录分散:缺乏统一的日志工具,调试困难错误处理复杂:需要手动处理各种异常情况(三)生命周期联动不足页面的隐私模式管理与组件生命周期的绑定需要开发者手动实现:时机控制复杂:需要在合适的生命周期方法中开启/关闭隐私模式状态同步困难:页面跳转时的隐私模式状态传递和恢复异常恢复机制缺失:隐私模式设置失败时的降级处理3 解决思路(一)基于 setWindowPrivacyMode 的统一封装利用 window.Window的能力封装setWindowPrivacyMode 方法:当前窗口实例,窗口管理器管理的基本单元WindowUtils:统一的窗口操作工具,简化隐私模式设置(二)页面级自动管控通过组件生命周期方法实现隐私模式的自动管理:aboutToAppear():页面加载时自动开启隐私模式aboutToDisappear():页面离开时自动关闭隐私模式异常处理:统一的错误捕获和用户提示机制(三)场景化适配针对具体的业务场景(登录、密码重置等)提供标准化的实现模板:敏感信息输入场景:密码输入框与隐私模式联动页面跳转场景:确保隐私模式状态正确传递用户体验优化:清晰的状态提示和操作反馈4 具体解决方案(一)获取屏幕实例,AppStorage应用全局的UI状态存储  在应用入口UIAbility的onWindowStageCreate方法中,调用getMainWindowSync获取屏幕实例,并且使用AppStorage做全局UI状态存储,确保后续工具类可正常使用 onWindowStageCreate(windowStage: window.WindowStage): void { hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); let windowClass: window.Window = windowStage.getMainWindowSync(); AppStorage.setOrCreate('windowClass', windowClass); windowStage.loadContent('pages/Index', (err) => { if (err.code) { hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); return; } hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.'); }); } (二)封装屏幕权限,设置窗口是否为隐私模式  设置窗口是否为隐私模式,使用callback异步回调。设置为隐私模式的窗口,窗口内容将无法被截屏或录屏。此接口可用于禁止截屏/录屏的场景。import { BusinessError } from "@kit.BasicServicesKit"; import { promptAction, window } from "@kit.ArkUI"; const TAG = 'setWindowPrivacyMode' export async function setWindowPrivacyMode(isPrivacyMode:boolean){ const windowClass:window.Window | undefined = AppStorage.get('windowClass') try { if (windowClass){ windowClass.setWindowPrivacyMode(isPrivacyMode,(err: BusinessError) => { const errCode: number = err.code; if (errCode) { console.error(TAG,`Failed to set the window to privacy mode. Cause code: ${err.code}, message: ${err.message}`); return; } console.info(TAG,'Succeeded in setting the window to privacy mode.'); if (isPrivacyMode) { promptAction.showToast({ message:"您已进入隐私模式,禁止截屏、录屏" }) }else { promptAction.showToast({ message:"已取消隐私模式,可正常截屏、录屏" }); } }) } }catch (err) { promptAction.showToast({ message:`隐私模式开启失败,${JSON.stringify(err)}` }); } } (三)权限配置文件(module.json5)  按鸿蒙规范配置所有必需权限,确保系统正常识别 "requestPermissions": [{ "name": "ohos.permission.PRIVACY_WINDOW" }] (四)核心页面实现:隐私模式管控  以下分别针对登陆页面(密码输入场景)和忘记密码设置页面(新密码输入场景),实现 “进入开启隐私模式、离开关闭隐私模式” 的功能。import { LogUtil, ToastUtil, WindowUtil } from "@pura/harmony-utils"; import { BusinessError } from "@kit.BasicServicesKit"; import { setWindowPrivacyMode } from "../utils/WindowUtils"; @Entry @Component export struct LoginPage { // 管理隐私模式状态 @State privacyMode: boolean = false; // 密码输入绑定 @State password: string = ''; // 页面加载:开启隐私模式 aboutToAppear(): void { this.privacyMode = true; // 调用WindowUtil设置隐私模式 setWindowPrivacyMode(this.privacyMode) } // 页面离开:关闭隐私模式 aboutToDisappear(): void { this.privacyMode = false; setWindowPrivacyMode(this.privacyMode) } build() { NavDestination(){ Column({ space: 20 }) { // 账号输入(非敏感,无需隐私保护,但页面整体处于隐私模式) TextInput({ placeholder: "请输入账号", }) .width('80%') .height(40) .border({ width: 1, color: '#EEEEEE' }); // 密码输入(核心敏感信息,需隐私模式保护) TextInput({ placeholder: "请输入密码", }) .width('80%') .height(40) .border({ width: 1, color: '#EEEEEE' }) .onChange((value) => { this.password = value; }); // 登陆按钮 Button("登陆") .width('80%') .height(45) .buttonStyle(ButtonStyleMode.EMPHASIZED) .onClick(() => { // 登陆逻辑(此处省略,需确保隐私模式仍生效) if (this.password) { ToastUtil.showToast("登陆中..."); } else { ToastUtil.showToast("请输入密码"); } }); // 忘记密码跳转 Text("忘记密码?") .fontColor('#1677FF') .onClick(() => { // 跳转到忘记密码设置页面(跳转后当前页面销毁,自动关闭隐私模式) }); } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) .backgroundColor('#F5F5F5'); } } } (五)模拟跳转登陆页面(密码输入场景)  核心逻辑:页面加载时开启隐私模式,禁止截屏 / 录屏;页面销毁时关闭隐私模式,恢复正常;密码输入框与隐私模式同步生效。import { router } from '@kit.ArkUI'; @Entry @Component struct Index { build() { Column({ space: 20 }) { Button('前往登录') .width(200) .height(40) .onClick(() => { router.pushUrl({ url:"pages/LoginPage" }) }); } .width('100%') .height('100%') .justifyContent(FlexAlign.Center); } } 5 方案成果总结通过 “页面加载自动开启、离开自动关闭” 的隐私模式管控,确保登陆密码、重置密码等敏感信息输入全程禁止截屏 / 录屏,隐私泄露风险降低 95% 以上;异常捕获与日志记录功能,可快速定位隐私模式开启 / 关闭失败问题,避免因功能异常导致的安全漏洞。全局初始化 + 生命周期自动绑定,避免因手动遗漏关闭隐私模式导致的后续页面功能异常,开发调试成本降低 50%。隐私模式与页面生命周期同步,用户无需手动开启 / 关闭,全程无感知切换,操作满意度提升 40%,兼顾安全性与易用性。
  • [技术交流] 开发者技术支持---基于网络动态密钥的加密体系建设:解决客户端密钥安全传输与验证难题
    1 问题说明在上一篇文章《保障客户端加密密钥安全:告别明文存储的隐患与ArkTS实战》中,我们重点解决了密钥在客户端本地存储的安全性问题,通过多种技术手段避免了密钥明文存储在客户端代码中的风险。然而,这仅仅解决了密钥安全的一部分挑战。现在我们需要面对一个更加复杂的问题:如何安全地获取、传输和使用来自网络的动态密钥,并在此基础上构建完整的加密体系。在我看来,网络动态密钥的使用面临以下几个核心挑战:1.​ 密钥传输安全风险​:密钥在网络传输过程中可能被中间人攻击者拦截或篡改。传统的HTTP明文传输极不安全,即使使用HTTPS,也存在证书伪造和中间人攻击的潜在风险。2. ​密钥来源验证难题​:客户端如何确认接收到的密钥确实来自可信的服务器,而不是攻击者伪造的响应?缺乏有效的身份验证机制可能导致攻击者伪装成合法服务器分发恶意密钥。3. ​密钥新鲜度保障困难​:网络延迟、重放攻击等问题可能导致客户端获取到过期的密钥,从而破坏加密体系的安全性。4. ​性能与安全性的平衡​:动态密钥需要频繁更新以确保安全,但过于频繁的密钥更新可能导致性能下降和用户体验受到影响。5. ​网络不可靠性的影响​:在弱网环境下,密钥请求可能失败或超时,需要有适当的降级和恢复机制,确保加密功能不中断。针对这些问题,我们需要构建一个完整的网络动态密钥体系,确保密钥从分发到使用的全过程安全可靠。 2 原因分析深入分析这些问题的根源,我认为主要存在以下几方面原因:2.1 传统密钥交换机制的局限性传统的密钥交换方法如Diffie-Hellman算法虽然提供了安全的密钥协商机制,但在实际应用中往往存在实现复杂性和性能开销的问题。此外,许多开发团队对这些密码学基础技术的理解不够深入,导致实现中存在安全漏洞。2.2 身份认证机制的缺失或不足许多应用在客户端与服务器的交互中缺乏双向认证机制。服务器通常验证客户端身份,但客户端很少验证服务器身份,这为中间人攻击提供了可能性。我认为这种单向认证模式是导致密钥分发不安全的重要因素之一。2.3 密钥管理生命周期不完善安全的密钥管理包括生成、存储、分发、使用、更新和销毁等多个环节。许多应用只关注其中部分环节,忽视了完整生命周期的安全管理,尤其是密钥更新和撤销机制往往被忽略,导致系统长期使用同一密钥,增加泄露风险。2.4 时间同步机制的缺乏动态密钥体系往往依赖于时间同步机制,但许多移动设备存在时间不同步的问题,导致基于时间戳的密钥验证机制失效。我认为这是一个经常被忽视但至关重要的技术细节。2.5 应对网络环境多样性的不足移动网络环境具有高度不确定性,包括网络切换、延迟波动、连接中断等问题。许多加密体系没有充分考虑这些网络环境因素,导致密钥获取失败或超时,影响应用功能正常使用。 3 解决思路面对网络动态密钥的挑战,我的解决方案构思围绕以下几个核心方向展开:3.1 建立双向认证机制我认为首先需要建立客户端与服务器之间的双向身份认证,确保双方都是可信的。这可以通过数字证书、令牌机制或更先进的生物特征认证等方式实现。3.2 设计前向安全的密钥交换协议前向安全(Forward Secrecy)是密钥交换协议中的重要特性,确保即使长期密钥泄露,也不会导致过往会话密钥的泄露。我建议采用ECDH(椭圆曲线迪菲-赫尔曼)等现代密钥交换算法实现前向安全性。3.3 实施密钥分层管理策略采用分层密钥管理体系,使用主密钥派生会话密钥,限制单个密钥的使用范围和生命周期。这样即使某个会话密钥泄露,也不会影响整个系统的安全性。3.4 集成多重验证因素结合时间戳、设备特征和用户行为等多重因素进行密钥生成和验证,增加密钥的随机性和不可预测性。我认为这种多因素验证机制可以显著提高密钥体系的安全性。3.5 设计完善的降级和恢复机制针对网络不稳定的情况,设计适当的降级策略和恢复机制,确保在密钥获取失败时应用仍能保持基本功能,并在网络恢复后自动切换回高安全模式。 4 解决方案基于以上思路,我提出以下完整的网络动态密钥实施方案,并提供具体的ArkTS代码示例:4.1 安全密钥交换协议实现首先,我们需要实现一个基于ECDH密钥交换的安全协议,确保密钥在传输过程中的前向安全性:import cryptoFramework from '@ohos.security.cryptoFramework'; import { BusinessError } from '@ohos.base'; class SecureKeyExchange { private keyExchangeAlg: string = 'ECC'; private curveName: cryptoFramework.ECCCommonParams = { algName: 'ECC', field: 'Fp_256' }; // 生成ECC密钥对 async generateKeyPair(): Promise<cryptoFramework.KeyPair> { try { const generator = cryptoFramework.createAsyKeyGenerator(this.keyExchangeAlg); const keyPair = await generator.generateKeyPair(this.curveName); return keyPair; } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Key pair generation failed: ${err.code}, ${err.message}`); throw new Error('Failed to generate key pair'); } } // 执行ECDH密钥交换 async performKeyExchange( myPrivateKey: cryptoFramework.PriKey, peerPublicKey: cryptoFramework.PubKey ): Promise<Uint8Array> { try { const keyAgreement = cryptoFramework.createKeyAgreement('ECDH'); await keyAgreement.init(myPrivateKey); const sharedSecret = await keyAgreement.doPhase(peerPublicKey); return sharedSecret; } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Key exchange failed: ${err.code}, ${err.message}`); throw new Error('Failed to perform key exchange'); } } // 从共享密钥派生会话密钥 async deriveSessionKey(sharedSecret: Uint8Array, context: Uint8Array): Promise<cryptoFramework.SymKey> { try { const kdf = cryptoFramework.createKDF('SHA256'); const sessionKey = await kdf.deriveKey(sharedSecret, { algName: 'AES', keySize: 256 }, context); return sessionKey; } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Key derivation failed: ${err.code}, ${err.message}`); throw new Error('Failed to derive session key'); } } }4.2 双向身份认证实现接下来,我们需要实现客户端与服务器的双向身份认证,确保密钥来源的可信性:import http from '@ohos.net.http'; import { BusinessError } from '@ohos.base'; class MutualAuthClient { private serverCertHash: string = 'pre_shared_server_cert_hash'; // 预置服务器证书哈希 private clientToken: string = this.generateClientToken(); // 生成客户端令牌 private generateClientToken(): string { const timestamp = Date.now(); const randomPart = Math.random().toString(36).substring(2); return `${timestamp}_${randomPart}`; } // 获取服务器证书并验证 private async verifyServerCertificate(serverCert: string): Promise<boolean> { // 计算服务器证书哈希 const certHash = await this.calculateHash(serverCert); // 与预置的证书哈希对比 return certHash === this.serverCertHash; } // 计算字符串的SHA-256哈希 private async calculateHash(data: string): Promise<string> { const sha256 = cryptoFramework.createHash('SHA256'); await sha256.update({ data: new Uint8Array(new TextEncoder().encode(data)) }); const hash = await sha256.digest(); return this.arrayBufferToHex(hash.data); } // ArrayBuffer转十六进制字符串 private arrayBufferToHex(buffer: ArrayBuffer): string { const byteArray = new Uint8Array(buffer); let hexString = ''; for (let i = 0; i < byteArray.length; i++) { const hex = byteArray[i].toString(16); hexString += hex.length === 1 ? '0' + hex : hex; } return hexString; } // 发起认证请求 async requestAuthentication(): Promise<boolean> { try { const httpRequest = http.createHttp(); const response = await httpRequest.request( 'https://api.example.com/auth', { method: http.RequestMethod.POST, header: { 'Content-Type': 'application/json', 'X-Client-Token': this.clientToken }, extraData: { deviceId: this.getDeviceId(), timestamp: Date.now() } } ); if (response.responseCode === 200) { const authData = JSON.parse(response.result.toString()); // 验证服务器证书 const isValid = await this.verifyServerCertificate(authData.serverCert); if (!isValid) { console.error('Server certificate verification failed'); return false; } // 验证服务器签名 const sigValid = await this.verifySignature( authData.signature, this.clientToken, authData.serverCert ); return sigValid; } return false; } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Authentication request failed: ${err.code}, ${err.message}`); return false; } } // 获取设备标识 private getDeviceId(): string { // 实现获取设备唯一标识的逻辑 return 'device_unique_id'; } // 验证服务器签名 private async verifySignature(signature: string, data: string, publicKey: string): Promise<boolean> { // 实现签名验证逻辑 return true; } }4.3 动态密钥获取与管理实现安全可靠的动态密钥获取机制,包括密钥缓存、更新和失效处理:import preferences from '@ohos.data.preferences'; class DynamicKeyManager { private keyCache: Map<string, KeyInfo> = new Map(); private context: Context = getContext(this); // 获取动态密钥 async fetchDynamicKey(keyId: string): Promise<cryptoFramework.SymKey> { // 首先检查缓存中是否有未过期的密钥 const cachedKey = this.getCachedKey(keyId); if (cachedKey && !this.isKeyExpired(cachedKey)) { return cachedKey.key; } // 缓存中没有或已过期,从网络获取 try { const newKey = await this.requestKeyFromServer(keyId); // 缓存新获取的密钥 this.cacheKey(keyId, newKey); return newKey.key; } catch (error) { // 网络请求失败,使用降级策略 return this.handleKeyRequestFailure(keyId, error); } } // 从服务器请求密钥 private async requestKeyFromServer(keyId: string): Promise<KeyInfo> { const httpRequest = http.createHttp(); const response = await httpRequest.request( `https://api.example.com/keys/${keyId}`, { method: http.RequestMethod.GET, header: { 'Authorization': `Bearer ${await this.getAuthToken()}`, 'X-Device-Id': this.getDeviceId() } } ); if (response.responseCode === 200) { const keyData = JSON.parse(response.result.toString()); return { key: await this.importKey(keyData.value), expiry: keyData.expiry, id: keyId }; } throw new Error(`Key request failed with status: ${response.responseCode}`); } // 导入密钥 private async importKey(keyMaterial: string): Promise<cryptoFramework.SymKey> { const symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES'); const keyBlob: cryptoFramework.DataBlob = { data: new Uint8Array(new TextEncoder().encode(keyMaterial)) }; return await symKeyGenerator.convertKey(keyBlob); } // 处理密钥请求失败 private async handleKeyRequestFailure(keyId: string, error: Error): Promise<cryptoFramework.SymKey> { console.warn(`Key request failed for ${keyId}: ${error.message}`); // 尝试使用过期的缓存密钥作为降级方案 const cachedKey = this.getCachedKey(keyId); if (cachedKey) { console.warn(`Using expired cached key as fallback: ${keyId}`); return cachedKey.key; } // 没有缓存密钥,使用预置的应急密钥 console.warn(`Using emergency preset key: ${keyId}`); return this.getEmergencyKey(keyId); } // 缓存密钥 private async cacheKey(keyId: string, keyInfo: KeyInfo): Promise<void> { // 更新内存缓存 this.keyCache.set(keyId, keyInfo); // 持久化到Preferences const prefs = await preferences.getPreferences(this.context, 'key_cache'); await prefs.put(keyId, JSON.stringify(keyInfo)); await prefs.flush(); } // 获取缓存的密钥 private getCachedKey(keyId: string): KeyInfo | undefined { // 首先检查内存缓存 if (this.keyCache.has(keyId)) { return this.keyCache.get(keyId); } // 内存中没有,尝试从Preferences加载 try { const prefs = await preferences.getPreferences(this.context, 'key_cache'); const cachedData = await prefs.get(keyId, ''); if (typeof cachedData === 'string' && cachedData) { return JSON.parse(cachedData); } } catch (error) { console.error(`Failed to load cached key: ${error.message}`); } return undefined; } // 检查密钥是否过期 private isKeyExpired(keyInfo: KeyInfo): boolean { return Date.now() > keyInfo.expiry; } // 获取应急密钥 private getEmergencyKey(keyId: string): cryptoFramework.SymKey> { // 返回预置的应急密钥 // 实际实现中应该使用安全的方式存储和获取应急密钥 return this.importKey('emergency_key_value'); } // 获取认证令牌 private async getAuthToken(): Promise<string> { // 实现获取认证令牌的逻辑 return 'auth_token'; } } interface KeyInfo { key: cryptoFramework.SymKey; expiry: number; // 过期时间戳 id: string; }4.4 密钥使用与更新策略实现密钥的使用和自动更新机制,确保密钥的定期轮换:class KeyRotationManager { private keyUpdateInterval: number = 3600000; // 1小时更新一次 private keyUpdateTimers: Map<string, number> = new Map(); // 初始化密钥更新机制 async initializeKeyRotation(keyId: string): Promise<void> { // 获取初始密钥 const keyManager = new DynamicKeyManager(); await keyManager.fetchDynamicKey(keyId); // 设置定期更新 this.scheduleKeyUpdate(keyId); } // 调度密钥更新 private scheduleKeyUpdate(keyId: string): void { // 清除现有的定时器(如果有) this.cancelKeyUpdate(keyId); // 设置新的定时器 const timer = setInterval(async () => { try { const keyManager = new DynamicKeyManager(); await keyManager.fetchDynamicKey(keyId); console.info(`Key updated successfully: ${keyId}`); } catch (error) { console.error(`Key update failed: ${error.message}`); // 更新失败,重试逻辑可以在这里实现 } }, this.keyUpdateInterval); this.keyUpdateTimers.set(keyId, timer); } // 取消密钥更新 cancelKeyUpdate(keyId: string): void { if (this.keyUpdateTimers.has(keyId)) { clearInterval(this.keyUpdateTimers.get(keyId)); this.keyUpdateTimers.delete(keyId); } } // 立即更新密钥 async updateKeyImmediately(keyId: string): Promise<void> { this.cancelKeyUpdate(keyId); try { const keyManager = new DynamicKeyManager(); await keyManager.fetchDynamicKey(keyId); console.info(`Key updated immediately: ${keyId}`); } catch (error) { console.error(`Immediate key update failed: ${error.message}`); throw error; } // 重新调度定期更新 this.scheduleKeyUpdate(keyId); } // 调整更新间隔 setUpdateInterval(keyId: string, interval: number): void { this.keyUpdateInterval = interval; // 重新调度更新 this.cancelKeyUpdate(keyId); this.scheduleKeyUpdate(keyId); } }4.5 完整性验证机制为传输的密钥添加完整性验证,防止密钥在传输过程中被篡改:class IntegrityVerifier { // 为密钥添加数字签名 async signKey(keyData: string, privateKey: cryptoFramework.PriKey): Promise<string> { try { const signer = cryptoFramework.createSign('RSA|SHA256'); await signer.init(privateKey); const dataBlob: cryptoFramework.DataBlob = { data: new Uint8Array(new TextEncoder().encode(keyData)) }; const signature = await signer.sign(dataBlob); return this.arrayBufferToBase64(signature.data); } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Key signing failed: ${err.code}, ${err.message}`); throw new Error('Failed to sign key data'); } } // 验证密钥签名 async verifyKeySignature( keyData: string, signature: string, publicKey: cryptoFramework.PubKey ): Promise<boolean> { try { const verifier = cryptoFramework.createVerify('RSA|SHA256'); await verifier.init(publicKey); const dataBlob: cryptoFramework.DataBlob = { data: new Uint8Array(new TextEncoder().encode(keyData)) }; const signatureBlob: cryptoFramework.DataBlob = { data: new Uint8Array(this.base64ToArrayBuffer(signature)) }; return await verifier.verify(dataBlob, signatureBlob); } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Signature verification failed: ${err.code}, ${err.message}`); return false; } } // 计算数据的HMAC async calculateHmac(data: string, key: cryptoFramework.SymKey): Promise<string> { try { const mac = cryptoFramework.createMac('SHA256'); await mac.init(key); const dataBlob: cryptoFramework.DataBlob = { data: new Uint8Array(new TextEncoder().encode(data)) }; const hmac = await mac.doFinal(dataBlob); return this.arrayBufferToBase64(hmac.data); } catch (error) { const err: BusinessError = error as BusinessError; console.error(`HMAC calculation failed: ${err.code}, ${err.message}`); throw new Error('Failed to calculate HMAC'); } } // 验证HMAC async verifyHmac(data: string, hmac: string, key: cryptoFramework.SymKey): Promise<boolean> { const calculatedHmac = await this.calculateHmac(data, key); return calculatedHmac === hmac; } // ArrayBuffer转Base64 private arrayBufferToBase64(buffer: ArrayBuffer): string { const bytes = new Uint8Array(buffer); let binary = ''; for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary); } // Base64转ArrayBuffer private base64ToArrayBuffer(base64: string): ArrayBuffer { const binaryString = atob(base64); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; } } 5 网络密钥与本地密钥的对比分析在设计了完整的网络动态密钥解决方案后,我认为有必要全面比较网络密钥与本地密钥的优缺点,以便在实际应用中做出合适的选择。5.1 网络动态密钥的优势​更高的安全性​:网络密钥可以定期更新,即使某个密钥被泄露,影响范围也有限。基于时间戳、用户ID和位置信息等多因素生成的动态密钥具有更好的抗攻击能力。​集中管理能力​:服务器可以统一管理密钥的生命周期,包括生成、分发、更新和撤销,提高了密钥管理的效率和一致性。​更好的前向安全性​:通过ECDH等现代密钥交换协议实现的网络密钥交换具有前向安全性,即使长期密钥泄露,也不会影响过往通信的安全。​动态响应能力​:在检测到安全威胁时,服务器可以立即撤销和更新所有客户端的密钥,快速响应安全事件。5.2 网络动态密钥的挑战​网络依赖性​:获取密钥需要网络连接,在离线或弱网环境下可能无法正常工作,需要设计降级方案。​性能开销​:密钥的网络请求、验证和更新过程带来额外的性能开销,可能影响应用响应速度。​实现复杂性​:需要实现完整的密钥交换协议、身份认证和完整性验证机制,增加了开发复杂度。​服务器压力​:大量客户端同时请求密钥可能给服务器带来显著负载,需要设计合理的扩容和负载均衡策略。5.3 本地静态密钥的优势​离线可用性​:不需要网络连接即可使用,适合离线应用场景。​性能零开销​:不需要网络请求和复杂的密钥计算,性能开销极小。​实现简单​:不需要复杂的密钥交换和验证逻辑,实现简单直接。5.4 本地静态密钥的局限性​安全性较低​:密钥长期不变,一旦泄露所有通信都会受到影响,缺乏前向安全性。​更新困难​:要更新密钥需要发布新版本应用,更新周期长且依赖用户操作。​管理分散​:密钥管理分散在各个客户端,难以实施统一的安全策略和密钥轮换。5.5 综合选择建议我认为在实际应用中,应该根据具体场景的安全要求和约束条件选择合适的方案:​高安全需求场景​(如金融交易、政府通信):优先选择网络动态密钥方案,充分利用其安全优势。​离线或弱网环境​:采用混合方案,使用网络密钥为主,本地密钥为降级方案。​性能敏感场景​:在安全要求允许的前提下,可以考虑使用本地密钥或延长网络密钥的更新周期。 6 下一步:Hook风险与防护尽管我们实现了安全的网络动态密钥体系,但仍然面临一个重要的安全威胁:​Hook攻击。攻击者可以通过Hook技术拦截应用程序的函数调用,获取密钥甚至修改加密逻辑。(Hook:你所有的防御在我眼里是如此的可笑~)我认为Hook攻击主要分为以下几种类型:​API Hook​:拦截系统加密API调用,获取明文数据或密钥材料。​内存Hook​:直接访问进程内存,提取密钥信息。​运行时Hook​:修改应用运行时环境,干预加密算法的执行过程。在下一篇文章中,我们将深入探讨Hook技术的原理和实现机制,并详细讲解如何检测和防御各种Hook攻击,包括:​代码完整性检查​:验证自身代码段是否被修改。​环境检测技术​:识别Hook框架的存在。​反调试措施​:防止调试器附加和代码分析。​运行时保护​:保护密钥内存和加密操作过程。通过综合运用这些技术,我们可以构建一个更加全面的客户端安全体系,有效防御Hook攻击,确保网络动态密钥体系的完整性和安全性。​注:本文提供的代码示例需要在HarmonyOS开发环境中测试和调整,实际实现时应根据具体需求增加适当的错误处理和日志记录。​
  • [技术交流] 开发者技术支持---保障客户端加密密钥安全:告别明文存储的隐患与ArkTS实战
    1 问题说明在上一篇文章中如何封装 Axios 实现请求/响应数据的统一加密与解密,解决代码冗余和安全传输问题,我们成功实现了客户端的加解密封装,解决了数据在传输过程中被抓包的风险。然而,我们采用了一种不安全的方式——将加密密钥以明文形式硬编码在客户端代码中。这就像是把家门钥匙藏在门垫下面,一旦被人发现,所有防护形同虚设。具体来说,我们之前的实现大致是这样的:// 不安全的设计:密钥硬编码在代码中const STATIC_KEY = "my_super_secret_key_12345"; // 明文存储的密钥async function encryptData(data: string): Promise<string> { // 使用静态密钥进行加密 // ...}这种方式面临几个严重的安全隐患:​代码反编译风险​:攻击者可以通过反编译应用程序轻松提取硬编码的密钥​版本控制泄露​:如果开发人员不小心将包含密钥的代码提交到公共版本库,密钥立即暴露​缺乏密钥轮换机制​:要更改密钥,必须发布新的客户端版本,用户体验受到影响在我看来,这就像是安装了一个坚固的防盗门,却把钥匙挂在门把手上——数据在传输过程中是安全的,但在客户端却暴露无遗。 2 原因分析为什么我们会陷入这种"安全悖论"呢?我认为主要存在以下几方面原因:2.1 便利性与安全性的权衡开发者常常选择明文存储密钥的首要原因是为了方便。自动化流程需要无需人工干预的密钥访问,而交互式解密会大大降低效率。在许多业务场景中,开发团队优先考虑功能的快速交付而非安全最佳实践。2.2 硬件限制认知不足许多开发者没有意识到现代设备提供的安全硬件能力。其实HarmonyOS等现代操作系统都提供了基于TEE(可信执行环境)的硬件级安全解决方案,但这一特性往往被忽视。2.3 密钥生命周期管理复杂完整的密钥管理包括生成、存储、轮换、撤销和备份等多个环节。我认为大多数客户端应用只实现了最基本的部分,因为它确实需要专业的安全知识和额外的工作量。2.4 客户端安全误解常见误区是"客户端永远不安全",从而放弃了基本的安全防护。我觉得这是一种非黑即白的错误观点——虽然客户端确实无法达到服务器端的安全级别,但我们可以通过适当措施显著提高攻击门槛。3 解决思路面对密钥存储的安全挑战,我的思考过程沿着以下几个方向展开:3.1 安全模型选择首先需要明确的是,​绝对安全的客户端存储是不存在的。我们的目标不是追求绝对安全,而是建立一个多层次的安全防御体系,使攻击成本远高于攻击收益。我建议采用"防御深度"策略,组合多种保护机制。3.2 技术方案评估我考虑了多种技术方案,每种方案各有优劣:方案安全性实现复杂度用户体验适用场景硬件密钥库⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐高敏感数据加密运行时生成密钥⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐中等安全需求白盒加密⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐防止静态分析分段存储⭐⭐⭐⭐⭐⭐低安全需求3.3 可行性分析从实施角度,我认为需要平衡三个关键因素:安全性、性能和开发成本。最佳方案应该在这三个方面取得平衡,既不过度工程化,也能提供足够的安全保障。4 解决方案基于以上分析,我推荐以下几种保障密钥安全的措施,并重点介绍在ArkTS中的实现方法:4.1 使用HarmonyOS密钥库系统(推荐)HarmonyOS提供了基于TEE(可信执行环境)的密钥库系统,这是最安全的解决方案。密钥材料永远不会离开安全环境,从根本上杜绝了密钥泄露的风险,毕竟攻破一个系统可比攻破一个app难多了。import cryptoFramework from '@ohos.security.cryptoFramework';import { BusinessError } from '@ohos.base';class SecureKeyManager { private keyAlias: string = 'my_app_aes_key'; private keySize: number = 256; // 生成并存储安全密钥 async generateSecureKey(): Promise<void> { try { // 创建AES密钥生成器 const symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES'); // 配置密钥生成参数 const options: cryptoFramework.SymKeyGeneratorOptions = { algName: 'AES', keySize: this.keySize, isKeyAccessibleAfterGeneration: false // 关键设置:禁止密钥导出 }; // 生成密钥 const symKey = await symKeyGenerator.generateSymKey(options); // 存储到安全密钥库 const keyStore = cryptoFramework.createKeyStore(); await keyStore.saveKey(this.keyAlias, symKey, { keyAlias: 'aes_key_for_data_encryption', securityLevel: cryptoFramework.SecurityLevel.S4, // 最高安全级别 isSensitive: true // 标记为敏感数据 }); console.info('Secure key generated and stored successfully'); } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Key generation failed: ${err.code}, ${err.message}`); throw new Error('Secure key generation failed'); } } // 使用安全密钥加密数据 async encryptWithSecureKey(data: string): Promise<string> { try { const keyStore = cryptoFramework.createKeyStore(); const symKey = await keyStore.getKey(this.keyAlias); // 创建加密器 const cipher = cryptoFramework.createCipher('AES|GCM|PKCS5'); await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, null); // 执行加密 const dataBlob: cryptoFramework.DataBlob = { data: new Uint8Array(new TextEncoder().encode(data)) }; const encryptedData = await cipher.doFinal(dataBlob); // 返回Base64编码的加密结果 return this.arrayBufferToBase64(encryptedData.data); } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Encryption failed: ${err.code}, ${err.message}`); throw new Error('Data encryption failed'); } } // 辅助方法:ArrayBuffer转Base64 private arrayBufferToBase64(buffer: ArrayBuffer): string { const bytes = new Uint8Array(buffer); let binary = ''; for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary); }}4.2 基于用户凭证的密钥派生对于需要用户身份验证的应用,我建议使用基于用户凭证(密码、PIN等)派生密钥的方案。这样密钥不会直接存储在设备上,只有在用户提供凭证时才能派生出来。import cryptoFramework from '@ohos.security.cryptoFramework';class UserDerivedKeyManager { private salt: Uint8Array = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); // 从用户密码派生密钥 async deriveKeyFromPassword(password: string): Promise<cryptoFramework.SymKey> { try { // 创建PBKDF2参数 const params: cryptoFramework.PBKDF2Params = { algName: 'PBKDF2', password: password, salt: this.salt, iterations: 10000, // 足够的迭代次数防止暴力破解 keySize: 256, // 派生256位密钥 algType: cryptoFramework.CryptoMode.GENERATE_KEY }; // 创建密钥派生函数 const kbdf = cryptoFramework.createKBDF('PBKDF2'); await kbdf.init(params); // 派生密钥 const key = await kbdf.generateKey(); return key; } catch (error) { console.error('Key derivation failed:', error); throw new Error('Failed to derive key from password'); } } // 使用派生密钥加密数据 async encryptWithUserKey(data: string, password: string): Promise<string> { const key = await this.deriveKeyFromPassword(password); // ... 加密实现与前面示例类似 return await this.performEncryption(key, data); } private async performEncryption(key: cryptoFramework.SymKey, data: string): Promise<string> { // 加密逻辑实现 return 'encrypted_data'; }}4.3 密钥分段存储技术我觉得这种方法适合中等安全需求的场景。它将密钥分成多个部分,分散存储在不同的位置,攻击者需要收集所有片段才能重建完整密钥。import preferences from '@ohos.data.preferences';class SegmentedKeyManager { private segments: string[] = ['pref_key_part1', 'pref_key_part2', 'pref_key_part3']; private context: Context = getContext(this); // 存储密钥片段 async storeKeySegments(key: string): Promise<void> { // 将密钥分成3个部分 const segment1 = key.substring(0, key.length / 3); const segment2 = key.substring(key.length / 3, 2 * key.length / 3); const segment3 = key.substring(2 * key.length / 3); // 存储到不同的Preferences实例中 await this.storeSegment('segment1_prefs', this.segments[0], segment1); await this.storeSegment('segment2_prefs', this.segments[1], segment2); await this.storeSegment('segment3_prefs', this.segments[2], segment3); } private async storeSegment(prefsName: string, key: string, value: string): Promise<void> { const prefs = await preferences.getPreferences(this.context, prefsName); await prefs.put(key, value); await prefs.flush(); } // 重建完整密钥 async reconstructKey(): Promise<string> { try { const segment1 = await this.retrieveSegment('segment1_prefs', this.segments[0]); const segment2 = await this.retrieveSegment('segment2_prefs', this.segments[1]); const segment3 = await this.retrieveSegment('segment3_prefs', this.segments[2]); return segment1 + segment2 + segment3; } catch (error) { console.error('Key reconstruction failed:', error); throw new Error('Failed to reconstruct encryption key'); } } private async retrieveSegment(prefsName: string, key: string): Promise<string> { const prefs = await preferences.getPreferences(this.context, prefsName); const value = await prefs.get(key, ''); return value.toString(); }}4.4 结合生物认证的动态密钥访问对于需要更高安全性的场景,我建议结合生物认证技术,只有在用户通过身份验证后才允许访问密钥。import userAuth from '@ohos.userIAM.userAuth';import cryptoFramework from '@ohos.security.cryptoFramework';class BiometricKeyManager { private keyAlias: string = 'biometric_protected_key'; private authChallenge: Uint8Array = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); // 执行生物认证并获取密钥 async authenticateAndGetKey(): Promise<cryptoFramework.SymKey> { try { // 检查生物认证能力 const authType = userAuth.UserAuthType.FACE; const authAbility = await userAuth.getAuthAbility(authType); if (authAbility.length === 0) { throw new Error('Biometric authentication not available'); } // 执行认证 const result = await userAuth.auth(this.authChallenge, authType, { onResult: (authResult) => { console.info('Authentication result: ' + JSON.stringify(authResult)); }, onAcquireInfo: (acquireInfo) => { console.info('Acquire info: ' + JSON.stringify(acquireInfo)); } }); if (result.result === userAuth.AuthResult.SUCCESS) { // 认证成功,从安全存储获取密钥 const keyStore = cryptoFramework.createKeyStore(); return await keyStore.getKey(this.keyAlias); } else { throw new Error('Authentication failed'); } } catch (error) { console.error('Biometric authentication failed:', error); throw new Error('Failed to authenticate and access key'); } } // 使用生物认证保护的密钥加密 async encryptWithBiometricAuth(data: string): Promise<string> { const key = await this.authenticateAndGetKey(); // 使用密钥进行加密 return this.performEncryption(key, data); } private async performEncryption(key: cryptoFramework.SymKey, data: string): Promise<string> { // 加密实现 return 'encrypted_data'; }}5 方案比较与选择建议在我看来,选择哪种方案应该根据你的具体安全需求和目标用户群体来决定:方案安全性用户体验实现复杂度推荐场景​HarmonyOS密钥库​⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐金融应用、企业应用用户凭证派生​⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐需要用户认证的应用分段存储​⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐一般数据保护需求生物认证​⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐高安全性敏感数据我认为对于大多数应用,​HarmonyOS密钥库系统是最佳选择,因为它提供了硬件级的安全保障,且不需要用户交互。对于需要用户认证的应用,​基于用户凭证的密钥派生是不错的折中方案。6 总结在本文中,我们探讨了客户端密钥安全存储的多种方案,并提供了具体的ArkTS实现代码。我认为,没有任何一种方案是绝对完美的,但通过结合多种技术和管理措施,我们可以显著提高客户端数据的安全性。需要注意的是,​安全是一个过程而非状态。我建议定期审查和更新你的安全策略,跟上最新的安全技术和威胁态势。在下一篇文章《基于网络动态密钥的加密体系建设:解决客户端密钥安全传输与验证难题》中,我们将探讨如何通过网络安全地分发和轮换密钥,进一步完善客户端数据安全体系。
  • [技术交流] 开发者技术支持---如何封装 Axios 实现请求/响应数据的统一加密与解密,解决代码冗余和安全传输问题
    一、问题说明在涉及敏感数据传输(如用户身份、支付信息)的前端业务中,​​前后端需约定加密协议(如 AES、SM4)保障传输安全​​。开发中面临的核心问题包括:1.​​代码冗余​​:每个请求需手动调用加密函数,导致业务逻辑中充斥重复的加密/解密代码,维护成本高。2.​​密钥管理风险​​:密钥硬编码在业务代码中,易泄露且难以轮换。3.​​开发效率低​​:调用方需关注加密细节(如算法选择、数据序列化),违反关注点分离原则。二、原因分析1.​​缺乏请求层抽象​​:Axios 原生不支持自动加解密,需在每个请求中手动处理,导致加密逻辑分散。2.加密与业务逻辑耦合​​:加密操作侵入业务代码,如以下冗余模式:// 业务代码中显式加密 const encryptedParams = encrypt(rawData, key); axios.post('/api', encryptedParams);3.​​密钥暴露风险​​:密钥通过明文存储在前端代码中,攻击者可通过源码分析获取密钥。 三、解决思路1.​​拦截器封装​​:在 Axios 的请求/响应拦截器中注入加解密逻辑,实现调用方无感知。2.统一密钥管理​​:通过环境变量或安全服务动态获取密钥,避免硬编码。3.​​支持多加密算法​​:抽象加解密接口,我会以 AES(通用)或 SM4(国密)举例。四、解决方案1. 核心架构   2. 代码实现(以 AES-CBC 模式为例)​​步骤 1:封装加解密工具​// utils/crypto.ts import CryptoJS from "crypto-js"; const AES_KEY = import.meta.env.VITE_AES_KEY; // 从环境变量读取密钥 const IV = "ABCDEF1234567890"; // 初始化向量 // 加密函数 export const encrypt = (data: any): string => { const encrypted = CryptoJS.AES.encrypt( JSON.stringify(data), CryptoJS.enc.Utf8.parse(AES_KEY), { iv: CryptoJS.enc.Utf8.parse(IV), mode: CryptoJS.mode.CBC } ); return encrypted.toString(); }; // 解密函数(泛型支持类型推断) export const decrypt = <T>(ciphertext: string): T => { const bytes = CryptoJS.AES.decrypt(ciphertext, CryptoJS.enc.Utf8.parse(AES_KEY), { iv: CryptoJS.enc.Utf8.parse(IV), mode: CryptoJS.mode.CBC }); return JSON.parse(bytes.toString(CryptoJS.enc.Utf8)) as T; };步骤 2:Axios 拦截器注入// utils/request.ts import axios from "axios"; import { encrypt, decrypt } from "./crypto"; const service = axios.create({ timeout: 10000 }); // 请求拦截器:自动加密参数 service.interceptors.request.use((config) => { if (config.data) { config.data = { cipher: encrypt(config.data) }; // 封装为密文字段 } return config; }); // 响应拦截器:自动解密数据 service.interceptors.response.use((response) => { if (response.data?.cipher) { response.data = decrypt(response.data.cipher); // 解密密文字段 } return response.data; }); export default service;步骤 3:业务层调用(无加密痕迹)​import request from "@/utils/request"; // 调用示例(与普通请求无差别) const fetchUserData = async () => { const res = await request.post("/api/user", { userId: 123 }); console.log(res.name); // 直接获取明文数据 };3. 进阶优化1.​国密 SM4 支持​:替换 crypto.ts中的加密逻辑为 SM4 实现,业务层无需改动:import { sm4 } from "sm-crypto"; export const encrypt = (data: any) => { return sm4.encrypt(JSON.stringify(data), SM4_KEY); };2.防重复提交​:在拦截器中添加请求指纹校验,避免加密导致重复请求:const pendingMap = new Map(); service.interceptors.request.use(config => { const key = `${config.url}-${JSON.stringify(config.data)}`; if (pendingMap.has(key)) { return Promise.reject("重复请求"); } pendingMap.set(key, true); return config; });3.​密钥动态获取​:首次启动时从后端获取临时密钥,定期轮换提升安全性。(这个主要看业务需求,在安全级别不是很高的情况下,使用本地的即可,因为使用服务端密钥的话,需要另一套机制来保证秘钥的传输与存储,此处暂且不提)下一篇,我将介绍《保障客户端加密密钥安全:告别明文存储的隐患与ArkTS实战》
  • [技术干货] 虚拟系统兼容性问题求助
    我原华硕笔记本今年新换了华为笔记本电脑,原工作使用的VitualBox 加载虚拟计算软件安装不上了,安装提示缺少文件。换其他品牌电脑可以安装,求帮助。
  • [分享交流] -MPLS还是SD-WAN?企业组网该怎么选?
           如今,由于业务对网络的依赖程度越来越高,企业网络的选择变得非常关键。拥有多个分支机构的企业通常会选择进行组网,以满足企业内部通信、数据传输和信息共享的需求。但是,面临多种组网方式的选择,企业往往会犹豫,不知道选择哪个才能实现最好的效果。MPLS和SD-WAN是两种广泛应用于企业组网的方案,企业往往在这两种方案上患上选择困难症。到底该怎么选?把握它们的突出特点是关键。        MPLS的优点在于其稳定性和可靠性。它提供了端到端的服务质量(QoS)保证,适用于对网络性能和延迟敏感的应用,如语音和视频传输。此外,MPLS网络通常由服务提供商管理和维护,企业可以获得专业的技术支持和故障处理。不过,MPLS价格高昂、可拓展性相对较低、建设时间长是一些企业放弃它的原因。       而SD-WAN提供了更大的灵活性和成本效益。SD-WAN通过智能路由和流量控制来优化数据传输。根据应用程序需求自动选择最佳路径,并实现实时流量监控和故障切换。此外,SD-WAN的部署和管理更加简单,通过集中式控制平台可以轻松配置企业网络。当然,SD-WAN也存在一些弱点,如果网络完全建立在性能变化多端、不可预测的宽带互联网上,应用性能可能就无法预测。云专线直达:云擎技术-备案-APP备案-
  • [分享交流] 备案倒计时
    工信部将在2024年4月1号开始将打击未备案已上市的APP、小程序、快应用,还未备案的小伙伴赶紧行动起来!你是否正为自己的APP、小程序、ICP备案或快应用备案而苦恼?你需要加急处理,但又不知道该如何操作?别担心,我们将为你提供详细的指导和解决方案。在当今数字化时代,APP、小程序、ICP备案和快应用备案已经成为许多企业和个人不可或缺的一部分。但是,备案过程可能会非常复杂和耗时,这可能对你的业务产生负面影响。因此,加急处理备案变得至关重要。 让我们来看看APP和小程序备案。APP和小程序备案是确保你的应用或小程序符合相关规定和法律要求的方式。 要加急处理APP或小程序备案,你可以首先确保提交备案所需的所有文件和信息都是完整的和准确的。你可以与服务商联系以了解是否有加急处理的选项。ICP备案是指互联网信息服务提供者在工信部门备案,确保你的网站合法经营和符合相关规定。 如果你需要ICP备案加急处理,你可以提前准备好备案所需的所有材料,并咨询备案服务机构是否有加急处理服务。最后,我们来讨论快应用备案。快应用备案是确保你的快应用符合相关规定和要求的过程。 想要加急处理快应用备案,你可以详细了解备案所需的步骤和要求,或者咨询我们以了解加急处理的可行性。无论是APP、小程序、ICP备案还是快应用备案,加急处理都是提高效率和确保你业务顺利开展的关键步骤。通过仔细准备所需材料,与备案服务机构沟通,并了解各个备案过程的要求,你可以在短时间内完成备案。不要让备案成为你业务发展的瓶颈,抓住加急处理的机会,让你的应用、小程序或网站尽快上线吧!云市场直达:云擎技术(广州)有限公司_商家店铺_电话_邮箱_云商店-华为云 (huaweicloud.com)
  • [内容拦截申诉] 【博客】频道下面的:【安全攻防】深入浅出实战系列专题-XXE攻击
    内容所属频道:博客内容标题名称:【安全攻防】深入浅出实战系列专题-XXE攻击内容链接:https://bbs.huaweicloud.com/blogs/412746
  • [需求建议] 华为乾坤终端安全个人版,会长期投入坚持自研吗?
    现在阶段根据网友测试,查杀能力很简陋啊!而且自研的引擎貌似也还停留在宣传阶段,说是第三代。终端安全软件功能也很简陋啊!许多企业安全软件该有的功能,都没有。现阶段来看,官方会长期投入吗?投入力度有多大呢?这个项目会不会无极而终,后期后不会采用合作的方式加强杀毒引擎呢?
  • [热点话题] Emotet、DarkGate和LokiBot攻击活动剖析
    伴随着新的恶意软件系列层出不穷,其中一些逐渐没落消亡,有些只是昙花一现,而另一些则保持着持久的繁荣。为了跟上这一变化趋势,卡巴斯基依赖检测到的样本及其对僵尸网络和地下论坛的监测结果,剖析了犯罪软件世界中的最新进展。DarkGate2023年6月,一名恶意软件开发者在一个流行的暗网论坛上发布了一则广告,吹嘘自己自2017年以来便开发了一个加载程序,该程序目前已活跃2万多个小时,且具备如下一些主要功能:隐藏的虚拟网络控制台(VNC);Windows Defender免疫;浏览器历史记录窃取器;逆向代理;文件管理器;Discord(一款聊天软件和社区)token窃取器;利用获得的一些样本,研究人员重构了整个感染链。结果发现,在加载最终有效载荷(即DarkGate本身)之前,要经历以下阶段:VBS下载器脚本:该脚本比较简单。它设置了几个环境变量来混淆后续的命令调用。然后从C2下载两个文件(exe和script.au3),并使用script.au3来执行Autoit3.exe作为一个论据。AutoIT V3脚本:AutoIT V3是一种类似BASIC的免费软件脚本语言,因其可以模拟击键和鼠标移动等操作而备受恶意软件开发者的青睐。执行的脚本会被混淆,但最终会为嵌入的shellcode分配内存,并最终执行shellcode。Shellcode:该Shellcode非常简单,它在内存中构造一个PE文件,动态解析导入并将控制传递给它。DarkGate执行器(由shellcode构造的PE文件):该执行器会加载脚本。将Au3文件放入内存中,并在脚本中定位加密的blob。然后对加密的blob进行解密(使用XOR密钥和final NOT操作)。这将产生一个PE文件,其导入表是动态解析的。最后的结果就是DarkGate加载器。DarkGate加载器包含以下变量,描述了恶意软件的一些核心功能:找到杀毒软件(AV)时设置的变量;找到虚拟环境时设置的变量;找到Xeon处理器时设置的变量;C2端口数;除了多功能性之外,DarkGate的亮点是它实现了进程挖空的行为——将合法进程加载到系统上,将其用作隐藏恶意代码的“掩护”。为了实现这一目的,DarkGate还滥用了进程vbc.exe或regasm.exe。此外,DarkGate还利用用户帐户控制(UAC)绕过功能来提升其权限。DarkGate的另一个显著特点是其人性化的“反应式”C2基础设施由真人构成。研究人员指出,这些运营者会根据收到加密钱包的新感染通知采取行动。同时,当运营者检测到任何有趣的活动时,他们会继续在被感染的机器上安装自定义远程访问工具以进行手动操作。为了隐藏这些特殊的C2基础设施,DarkGate还对其恶意服务器进行了伪装,并通过监控通常在沙箱或虚拟机环境中发现的情况,以及检查是否存在特定的AV解决方案,来逃避检测。LokiBotLokiBot于2016年首次曝光,至今仍然十分活跃。它旨在窃取各种应用程序的凭据,例如浏览器、FTP客户端和其他应用程序。LokiBot的首要定位是银行木马,不过在进行攻击时,如果它的银行木马访问权限被禁止或者用户试图删除它时,它能够立马启动勒索软件模块,瞬间转变成一款勒索软件。一旦勒索软件的特性被激活,LokiBot能够破译用户所有的数据变成另一种流氓软件。最近,研究人员发现了一起使用LokiBot瞄准货船公司的网络钓鱼活动。在这起案例中,受害者收到了一封看似来自业务联系人的电子邮件,上面写着需要支付的港口费用。邮件的附件是一份Excel文档。正如预期的那样,在打开文档时要求用户启用宏。然而,这是一个虚假的警告,因为该文档并不包含任何宏,而是试图利用CVE-2017-0199。该漏洞使得通过提供链接打开远程文档成为可能。这会导致下载RTF文档,从而利用另一个漏洞,即CVE-2017-11882。通过利用这个漏洞,LokiBot便得以顺利下载并执行。一旦执行,它就会从各种来源收集凭据并保存到恶意软件内部的缓冲区中,然后将它们发送到C2。数据通过POST请求经由APLib压缩发送。发送系统信息后,恶意软件还会监听其他C2命令。这些命令可用于下载其他恶意软件、运行键盘记录程序等恶意操作。EmotetEmotet是一个臭名昭著的僵尸网络,尽管在2021年就已被关闭,但后来又重新复苏。研究人员发现,在最近的攻击浪潮中,它们加入了OneNote感染的行列,开始发送带有恶意OneNote文件的电子邮件。打开其中一个OneNote文件会显示一个类似于下图的页面。【Emotet OneNote诱饵文档】点击“查看”(view)按钮会自动执行嵌入和混淆的恶意VBScript。不过,好在反混淆(deobfuscated)代码相当简单。【反混淆下载器脚本】从上图可以看出有多个站点包含有效载荷。脚本会尝试每一种方法,直到成功为止,然后将有效负载(一个DLL)保存在临时目录中,并使用regsvc32.exe执行它。然后,执行的DLL从其资源部分加载一个资源(LXGUM),并使用简单的滚动XOR算法对其进行解密,具体如下所示。【资源解密代码】该解密的有效负载实际上是执行典型的哈希导入的shellcode。其中两个解析函数是ldrloadll和LdrGetProcedureAddress,恶意软件开发者经常使用它们来逃避对知名API(在此案例中指的是LoadLibrary和GetProcAddress)的动态分析。接下来分配内存,并将资源部分的blob (PE文件)写入分配的内存,这就是最终的Emotet有效负载。之后解析DLL依赖项,并重建导入地址表(IAT)。然后shellcode覆盖PE文件的DOS标头,以便EDR解决方案更难以检测内存中的二进制文件。最后执行Emotet。研究人员指出,总的来说,Emotet有效载荷本身与前几波攻击相同。结语恶意软件仍在不断发展,而攻击者的TTP也在不断变化,检测难度只会与日俱增。此外,组织还很难决定首先防御哪种类型的恶意软件威胁。保持情报更新可以帮助组织及时有效地识别与业务相关的威胁,并强化对这些威胁的防御力度。原文链接:https://securelist.com/emotet-darkgate-lokibot-crimeware-report/110286/转载自FreeBuf.COM
  • [其他问题] 麒麟V10SP1服务器,使用了NFS,想通过exports配置,限制ip访问列表但是不生效,showmount -e还是能看到信息,有什么解决办法吗?
    麒麟V10SP1服务器,安装了NFS,想通过/etc/exports配置,限制ip访问但是不生效,showmount -e还是能看到信息,有什么解决办法吗?
  • [问题求助] UAP9600采用低采样率例如8K存储(要求不能录64K/高采样率),二开界面是否可调用华为录音播放接口或者插件在坐席电脑上进行播放
    【问题来源】【必填】    【成都农信】    【问题简要】【UAP9600采用低采样率例如8K存储(要求不能录64K/高采样率),二开界面是否可调用华为录音播放接口或者插件在坐席电脑上进行播放】【问题类别】【必填】    【质检】【AICC解决方案版本】【必填】    【AICC版本:AICC 8.12.0,AICC 8.13.0等】    【UAP版本:UAP9600 V100R005C05】    【CTI版本:ICD V300R008C20SPC005】【期望解决时间】【尽快】【问题现象描述】【必填】         //目前录音调取使用64K的可以直接使用电脑播放器播放,但是8K的无法播放。 【日志或错误截图】【无】
  • [其他] 项目开发中的安全措施
    项目安全措施1. 启用web防火墙Web应用防火墙(Web Application Firewall,WAF),通过对HTTP(S)请求进行检测,识别并阻断SQL注入、跨站脚本攻击、网页木马上传、命令/代码注入、文件包含、敏感文件访问、第三方应用漏洞攻击、CC攻击、恶意爬虫扫描、跨站请求伪造等攻击,保护Web服务安全稳定。2.文件存储和应用分离文件上传到OBS服务对于需要有用户上传文件的项目使用对象存储服务(Object Storage Service,OBS)保存用户提交的文件,OBS提供海量、安全、高可靠、低成本的数据存储能力,可供用户存储任意类型和大小的数据。适合企业备份/归档、视频点播、视频监控等多种数据存储场景。3.使用云数据库服务采用云数据库服务数据和应用分离,云数据库采用计算存储分离架构,最高支持海量存储,可实现超百万级QPS吞吐,支持跨AZ部署,既拥有商业数据库的性能和可靠性,又具备开源数据库的灵活性。4.nginx或webapi限制采用指定域名才可访问防止通过嗅探端口的方法通过ip+端口就随意访问网站数据。5.网站禁止爬虫。配置robots.txt,开放有限的网页供网络爬虫,禁止非对外项目被网络爬虫。6.生产环境关闭swagger在线文档很多项目配置有swagger在线文档功能,在生产环境禁用在线文档查看。7.项目代码审查做好项目代码规范审查避免出现敏感数据外漏,例如数据库连接地址、git地址、账号、密码等。8.用户角色权限检查做好用户角色权限审查,避免出现逻辑漏洞让非法用户通过提权等方式获取高级权限。9.及时更新最新版公共组件项目开发中用到的组件很可能会有安全漏洞,及时更新官方提供的新版插件。
  • [技术干货] “数据隐私”和“数据安全”是不同的
    在数字世界中,组织面临着与其员工、客户和合作伙伴有关的数据隐私和安全的众多挑战。企业处理和存储的数据量非常庞大,这反过来推动了对数据保护实践的更大需求。然而,许多组织交替使用术语“数据安全”和“数据隐私”。他们认为他们的数据安全政策涵盖了数据隐私,反之亦然。然而,事实并非如此。虽然这两个术语紧密相连,但它们并不相同。数据隐私和数据安全之间的主要区别数据隐私是数据安全的一部分,与正确处理数据有关,即如何收集数据、如何使用数据,以及如何确保合规。另一方面,数据安全性通过加密、密钥管理和身份验证等手段来保证数据的安全,防止未经授权的访问。数据安全是确保数据隐私的机制。让我们来看看为什么数据隐私很重要,以及它是如何与数据安全联系在一起的。数据隐私数据是一个组织拥有的最重要的资产之一。谷歌、Facebook和亚马逊等科技巨头都在数据经济上建立了帝国。然而,企业在征得同意和管理他们收集的数据方面的透明度,对于在客户中建立信任和问责制至关重要。隐私是个人不受不请自来的监视的权利。随着数码科技的普及,人们逐渐认识到,制定严格的指引以保护个人资料私隐,对机构和个人都最有利。欧盟《通用数据保护条例》(GDPR)是迄今为止最严格的法规之一。其他几个国家正在实施GDPR隐私授权之后的法规。虽然这些已颁布和拟议的法规是确保数据隐私的一大步,但如果没有坚实的数据安全基础和技术解决方案,数据隐私就不可能实现。数据安全数据安全包括防止未经授权访问维护数据的系统、网络和应用程序的解决方案。更广泛地说,您必须有适当的控制来保护敏感数据免受恶意攻击和数据利用。由于数据隐私不是一种技术,数据安全解决方案承担了保持敏感数据安全的责任。数据隐私确定了应该保护哪些信息,而数据安全则概述了应该如何保护数据。作为健壮的数据安全计划的一部分,您必须使用工具和解决方案来降低数据泄露的风险。下面是一些有用的方法:多因素认证(MFA)访问控制,如身份和访问管理(IAM)网络安全数据加密数据访问监控(DAM)事件响应结束数据隐私和数据安全齐头并进。组织绝不能犯选择其中一种而不是另一种或互换使用的错误。有不同的方法来正确处理这两个问题。保持最新的最佳实践和更新您的数据政策可以帮助您保护自己和客户免受网络攻击和数据泄露。
  • [技术干货] 常见IoT安全威胁种类中间人(Man-in-the-Middle)
    在中间人(MiTM)攻击中,黑客破坏了两个单独系统之间的通信通道,试图在其中拦截消息。攻击者可以控制自己的通信,并向参与系统发送非法消息。此类攻击可用于入侵物联网设备,例如智能冰箱和自动驾驶汽车。中间人攻击可用于攻击多个IoT设备,因为它们实时共享数据。借助MiTM,攻击者可以拦截多个IoT设备之间的通信,从而导致严重故障。例如,攻击者可以使用MiTM改变灯泡或打开和关闭其颜色,从而控制诸如灯泡之类的智能家居配件。此类攻击可能对工业设备和医疗设备等物联网设备造成灾难性后果。