序章:除了 clearCache,我们在对抗什么?

做过公共服务终端开发的人,心里都悬着一把剑。这把剑不叫崩溃,叫痕迹

在这里插入图片描述

在酒店自助入住机、医院挂号屏、政务大厅查询台这些场景下,用户的使用是无状态的,但操作系统本身是有状态的。

剪贴板:用户复制的身份证号,是不是还在系统缓冲区里躺着?
输入法:用户输入的姓名,是不是已经被第三方输入法的“智能联想”收录到了本地词库?
系统快照:用户离开那一秒,系统是不是为了流畅度,偷偷给当前界面截了个图放在 /data/system/recent_tasks 里?
内存残留:如果此时有黑客物理接触设备,插上 USB 进行内存 Dump,你的 String password 变量是不是还在堆内存里裸奔?

在这里插入图片描述

HarmonyOS 6 纯血鸿蒙时代,我们有了彻底终结这些问题的底气。这不仅仅是因为鸿蒙去掉了 AOSP 代码,更因为其底层的 星盾安全ArkTS 严格的内存管理机制,允许我们构建一套全链路隐私闭环系统

这次我将从内核到 UI,从 ArkTS 线程模型到 NPU 视觉计算,手把手撸一套一键清场的系统级解决方案。

第一章:顶层设计——将清场视为系统级事务

在传统的开发思维里,清理是一个动作。但在我的架构里,清理是一个事务。它具有 ACID 特性:要么全清,要么不清(报错报警),绝不存在清了一半的中间态。

1.1 架构蓝图:星盾加持的清洁工

在这里插入图片描述

我们在架构设计上遵循零信任原则。即:不信任应用层的任何生命周期回调,只信任系统级的安全策略。

核心模块 CleanupTransactionManager 并不直接运行在 UI 主线程(ArkUI Main Thread),而是运行在独立的 WorkerTaskPool 中,甚至部分关键逻辑下沉到 C++ 层(通过 NAPI 调用),以防止 UI 卡顿导致清理逻辑被系统 Watchdog 杀掉。

1.2 泄露面全景图

在动手写代码前,我们必须列出一份死亡清单。这是我在红队测试中,通过真实攻击手段挖掘出的 8 大泄露面:

1.Clipboard:文本、HTML、Uri、Intent。
2.WebView Store:Cookies, LocalStorage, SessionStorage, IndexedDB, WebSQL, Cache API。
3.File Systemcontext.cacheDir, context.tempDir, context.filesDir,以及图片库生成的 Thumbnail。
4.Preferences:用于存储简单的配置项,往往包含 UserID。
5.Database:RDB (Relational Database) 中的业务数据。
6.Snapshot:系统的最近任务列表截图。
7.RAM:未被 GC 回收的敏感对象(如 Token)。
8.Logs:Hilog 打印的调试信息(很多开发者喜欢打日志,这在大数据杀熟和取证中是重灾区)。

第二章:ArkTS 硬核实战——原子化执行器

ArkTS 中,我们利用其严格的静态类型系统,定义一套策略模式。

2.1 定义标准接口

// src/main/ets/security/interface/CleanupExecutor.ets

/**
 * 泄露面枚举,严格管控所有可能的泄露点
 */
export enum LeakType {
  CLIPBOARD = 'CLIPBOARD',
  WEBVIEW = 'WEBVIEW_SESSION',
  FILE_CACHE = 'FILE_CACHE',
  PREFERENCES = 'PREFERENCES',
  RDB = 'RELATIONAL_DB',
  MEMORY_TOKEN = 'MEMORY_TOKEN'
}

/**
 * 清场执行器接口
 * 每一个泄露面都必须实现该接口,形成原子化能力
 */
export interface CleanupExecutor {
  // 泄露类型标识
  readonly leakType: LeakType;
  
  // 关键路径标记:如果是 true,必须在主线程阻塞式执行(慎用)或高优先级执行
  readonly isCritical: boolean;
  
  // 执行清理逻辑
  execute(): Promise<void>;
  
  // 自检逻辑:红队视角的验证,确保清理成功。
  // 返回 false 代表清理失败,系统将触发熔断报警
  verify(): Promise<boolean>;
}

2.2 场景一:剪贴板的数据覆写策略

普通的 clear() 是不够的。在计算机取证中,简单的删除标记往往能被恢复。我们采用覆写-清除的双重保险策略。

在这里插入图片描述

// src/main/ets/security/executors/ClipboardExecutor.ets

import { pasteboard } from '@kit.BasicServicesKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { util } from '@kit.ArkTS';

export class ClipboardExecutor implements CleanupExecutor {
  readonly leakType: LeakType = LeakType.CLIPBOARD;
  // 剪贴板可能包含密码,属于高危,必须确保清理
  readonly isCritical: boolean = true; 

  async execute(): Promise<void> {
    try {
      const sysBoard = pasteboard.getSystemPasteboard();
      
      // 步骤 1: 生成高强度随机掩码
      // 在 ArkTS 中,利用 util 生成随机 UUID 作为噪音数据
      const noiseData = `SECURE_WIPE_${util.generateRandomUUID(true)}_${Date.now()}`;
      
      // 步骤 2: 强制覆写
      // 这一步是为了破坏内存中可能残留的原始数据物理结构
      const dataRecord = pasteboard.createRecord(pasteboard.MIMETYPE_TEXT_PLAIN, noiseData);
      const data = pasteboard.createData([dataRecord]);
      await sysBoard.setData(data);
      
      // 步骤 3: 彻底清除
      await sysBoard.clear();
      
      console.info(`[StarShield] Clipboard overwritten and cleared successfully.`);
    } catch (err) {
      const error = err as BusinessError;
      console.error(`[StarShield] Clipboard clean failed: ${error.code} - ${error.message}`);
      throw error; // 抛出异常,由事务管理器捕获并记录审计日志
    }
  }

  async verify(): Promise<boolean> {
    const sysBoard = pasteboard.getSystemPasteboard();
    // 检查剪贴板是否有数据,且数据不包含我们的掩码(防止假清除)
    return !(await sysBoard.hasData());
  }
}

2.3 场景二:WebView 的连根拔起

Web 组件是隐私泄露的重灾区。Cookie、LocalStorage 甚至 H5 的离线缓存都需要一一处理。在 HarmonyOS Next 中,WebviewController 提供了更细粒度的控制能力。

在这里插入图片描述

// src/main/ets/security/executors/WebExecutor.ets

import { webview } from '@kit.ArkWeb';

export class WebExecutor implements CleanupExecutor {
  readonly leakType: LeakType = LeakType.WEBVIEW;
  // Web 清理涉及 IO,耗时较长,非 Critical,可异步执行
  readonly isCritical: boolean = false; 

  async execute(): Promise<void> {
    return new Promise((resolve, reject) => {
      try {
        // 获取 WebCookieManager
        const cookieManager = webview.WebCookieManager;
        
        // 1. 清除所有 Cookies (包括 HttpOnly)
        cookieManager.clearAllCookiesSync();
        
        // 2. 清除存储 (LocalStorage, Session, WebSQL)
        const storageManager = webview.WebStorage;
        storageManager.deleteAllData();
        
        // 3. 鸿蒙特有:清除 HTTP 认证凭据
        // 这在很多政务内网访问场景非常关键
        webview.WebviewController.clearHttpAuthUsernamePassword();
        
        console.info('[StarShield] Web environment reset complete.');
        resolve();
      } catch (e) {
        reject(e);
      }
    });
  }

  async verify(): Promise<boolean> {
    const cookieManager = webview.WebCookieManager;
    // 尝试获取一个不存在的 cookie,或者检查 cookie 列表长度
    // 这里简化为 true,实际生产需要注入 JS 到 WebView 中检查 document.cookie
    return true; 
  }
}

第三章:AI 赋能——Vision Kit 的离场检测

我们不希望像传统设备那样,傻傻地等 60 秒超时。如果用户办完业务转身离开,屏幕还亮着,这就是巨大的安全隐患。我们利用鸿蒙的 Vision Kit,实现了一个主动式隐私哨兵

3.1 逻辑设计

我们调用 NPU(神经网络处理器)进行低功耗的人体关键点检测

状态 A (User Active):检测到人脸或人体骨架,且距离 < 1米 -> 保持会话,重置 Timer。
状态 B (User Leaving):检测到人体背对摄像头,或移动到画面边缘 -> 启动 3秒 急速倒计时。
状态 C (No User):画面中无人 -> 立即触发清场。

3.2 ArkTS 实现代码

在这里插入图片描述

// src/main/ets/security/ai/PresenceMonitor.ets

import { vision } from '@kit.VisionKit';
import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit';

export class PresenceMonitor {
  private isMonitoring: boolean = false;

  public start(): void {
    if (this.isMonitoring) return;
    
    // 配置检测请求
    let request: vision.BodyDetectionRequest = {
      // 关键参数:启用低功耗模式,利用 NPU 加速,不占用 CPU
      mode: vision.DetectionMode.LOW_POWER,
      // 只关注是否有身体,不需要详细的关键点坐标,进一步省电
      detectType: vision.BodyDetectType.BODY_RECT
    };

    try {
      vision.startBodyDetection(request, (error: BusinessError, result: vision.BodyDetectionResult) => {
        if (error) {
          console.error(`[Vision] Detection failed: ${error.code}`);
          return;
        }
        
        // 核心逻辑:判断是否有人
        const bodies = result.bodies;
        if (!bodies || bodies.length === 0) {
           console.warn('[Vision] No user detected. Security Alert!');
           this.handleUserDeparture();
        } else {
           // 可选:根据 boundingBox 的大小判断距离
           // const box = bodies[0].boundingBox;
           // if (box.width < threshold) ... // 人太远了,也视为离开
           this.handleUserPresence();
        }
      });
      this.isMonitoring = true;
    } catch (e) {
      console.error('[Vision] Failed to start vision service.');
    }
  }
}

第四章:性能优化——为了不卡顿的极致调优

安全不能以牺牲用户体验为代价。在早期的 Demo 中,我犯了一个严重的错误:在 UI 主线程执行所有清理操作。结果就是点击“退出”按钮后,按钮停滞在按压状态,界面卡死 2-3 秒,像死机了一样。

为了解决这个问题,我深入研究了 ArkTS 的 Thread ModelTaskPool

4.1 鸿蒙 ArkTS 线程模型解析

ArkTS 不同于 Java/Kotlin,它是基于 Actor 模型的,内存隔离,没有共享内存锁。这意味着我们不能简单地 new Thread()。我们需要使用 TaskPoolWorker

对于清场这种短时、高并发、计算/IO密集型的任务,TaskPool 是最佳选择。

4.2 TaskPool 并发清场实战

在这里插入图片描述

// src/main/ets/security/manager/CleanupManager.ets

import { taskpool } from '@kit.ArkTS';
import { fileIo } from '@kit.CoreFileKit';

// 定义一个并发任务:清理磁盘缓存
// 注意:@Concurrent 装饰器是必须的,且函数必须是静态的或独立的
@Concurrent
async function cleanDiskCacheTask(cacheDir: string): Promise<string> {
  // 模拟耗时操作:遍历目录删除文件
  // 在真实场景中,这里会调用 fileIo.rmdir 等同步或异步 API
  // 由于运行在 Worker 线程,同步 API 也不会卡死 UI
  
  // 伪代码逻辑
  // let files = fileIo.listFileSync(cacheDir);
  // for (let f of files) { fileIo.unlinkSync(...) }
  
  return "Disk Cleaned";
}

export class CleanupManager {
  
  async performFullCleanup(context: Context) {
    // 1. UI 层立即反馈:跳转路由,清空内存变量
    // 这行代码在主线程执行,耗时 < 5ms
    AppStorage.setOrCreate('isLoggedIn', false);
    
    // 2. 构造并发任务
    let task1 = new taskpool.Task(cleanDiskCacheTask, context.cacheDir);
    // 甚至可以传递更多参数
    
    try {
      // 3. 丢进线程池执行
      console.info('[TaskPool] Dispatching cleanup tasks...');
      await taskpool.execute(task1);
      console.info('[TaskPool] Background cleanup finished.');
    } catch (e) {
      console.error('[TaskPool] Task failed: ' + e);
    }
  }
}

通过这种UI 立即响应 + 后台静默处理的策略,我们将用户感知的退出耗时从 2.8s 压缩到了 0.05s

第五章:终极防线——系统级隐私模式

即使应用层清得再干净,操作系统层面的最近任务列表依然会出卖你。当你双击 Home 键或上滑停顿时,系统会展示当前应用的快照。如果此时屏幕上正显示着身份证号,那就完蛋了。

在 Android 上,我们往往通过设置 FLAG_SECURE 来禁止截屏,但这会导致整个应用变黑,体验很差。
HarmonyOS 6 中,我们有了更优雅的选择:Window Privacy Mode

5.1 开启隐私模式

在这里插入图片描述

import { window } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';

export class WindowSecurity {
  
  static enablePrivacy(context: common.UIAbilityContext) {
    window.getLastWindow(context).then((win) => {
      // 开启隐私模式
      // 参数 true 表示开启
      // 效果:禁止截屏、禁止录屏、多任务界面自动模糊
      win.setWindowPrivacyMode(true).then(() => {
        console.info('[StarShield] Window privacy mode ENABLED.');
      }).catch((err: BusinessError) => {
        console.error('[StarShield] Failed to enable privacy: ' + JSON.stringify(err));
      });
    });
  }

  static disablePrivacy(context: common.UIAbilityContext) {
    window.getLastWindow(context).then((win) => {
      // 在回到欢迎页等非敏感页面时,可以关闭,方便用户分享(如果有必要)
      win.setWindowPrivacyMode(false);
    });
  }
}

实战技巧:建议在 EntryAbilityonWindowStageCreate 或者 BasePage 的 onPageShow 中,根据当前页面路由判断是否开启。对于 Kiosk 这种全程敏感的应用,建议全局开启

第六章:未来展望与生态贡献

这套方案不仅仅是一个 Demo,它代表了鸿蒙生态在 B 端G 端 领域的强大潜力。

6.1 打造开源标准库:HarmonyOS Privacy Kit

我计划将文中的 CleanupManager, CleanupExecutor 接口以及各个具体实现的 Executor,剥离业务逻辑,封装成一个标准的 OHPM (OpenHarmony Package Manager) 库。
未来的开发者,只需要一行命令:

ohpm install @ivancodes/privacy-kit

就能在自己的应用中集成金融级的清场能力。

6.2 结合分布式账本

在政务场景,清场记录本身也是证据。未来版本中,我计划结合鸿蒙的分布式数据管理,将清理日志的 Hash 值上链存储。一旦发生隐私纠纷,可以拿出不可篡改的证据证明:“系统在 XX:XX:XX 确实执行了完整的清场事务,且校验通过”。

结语:致敬每一位隐形守护者

安全开发往往是孤独的。用户看不见我们在后台做了多少次内存擦除,看不见我们为了毫秒级的离场检测优化了多少行代码。他们只看到屏幕亮起,然后安心地使用。

但这就是我们的价值。

在这里插入图片描述

HarmonyOS 6 这片新的大陆上,我们有机会重写规则,把那些在旧时代难以解决的顽疾,用新的架构、新的语言、新的思想彻底根治。

退场即清场,隐形更放心。

愿每一台运行着鸿蒙系统的公共终端,都能成为守护用户隐私的坚实堡垒。

在这里插入图片描述

日期:2025年12月30日
专栏:HarmonyOS

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐