一、敏感数据分类与存储边界

在移动端组件内,密码、、密钥、会话ID 等数据一旦泄露,可直接导致用户身份冒用、数据窃取或服务端信任链被攻破。因此,第一步必须清晰识别哪些数据是“敏感”的,并明确其存储边界:是否需要持久化、存活多久、何时清除。

1.1 敏感数据类型全览

下表列出组件内最常见的四类敏感数据及其典型特征:

数据类别 典型示例 存储边界要求 清除要点
密码 用户登录密码、支付密码 不应明文持久化;仅用于一次性认证后丢弃 退出登录或密码修改时立即从内存和存储中擦除
JWT、OAuth 2.0 access_、refresh_ 短期 可内存缓存;长期 需加密存储 过期或用户登出时立即失效并删除
密钥 对称加密密钥、API 签名私钥、证书私钥 必须使用专用安全硬件(如 Keychain/Keystore)持久化 密钥轮换时删除旧版本,应用卸载时随安全域删除
会话ID Session ID、CSRF 通常为内存态,不落盘;如持久化需绑定设备环境 会话超时或用户主动退出时立即销毁

1.2 持久化 vs 非持久化数据

区分数据的持久化需求是设计存储策略的核心。错误的持久化判断是数据泄露的主要根源。

维度 非持久化数据 持久化数据
存储位置 内存、进程堆栈、寄存器 Keychain/Keystore、加密文件、沙箱数据库
生命周期 随组件销毁或进程结束自动释放 主动创建,需显式清除或过期策略
清除频率 无需手动清除(GC 或进程回收) 每次认证状态变更、版本升级、卸载时必须检查
泄露风险 低(攻击时间窗口短) 高(攻击者可能获得物理镜像)
典型数据 一次性密码、内存中的会话对象 长期 refresh_、设备密钥
关键原则: 仅当数据跨组件重启或应用重启后仍必须使用时,才考虑持久化;否则一律使用非持久化内存存储。

1.3 生命周期与清除策略

每一类敏感数据都必须拥有明确的 创建 — 使用 — 清除 生命周期,并由代码强制实施。生命周期管理包含三个阶段:

  • 创建阶段: 从网络响应或本地认证结果中提取敏感数据,立即存入安全容器(如 Keychain item 或内存 SecureString),不得在日志、全局变量或 UserDefaults 中明文滞留。
  • 使用阶段: 通过指针或安全句柄访问,避免多次复制;使用完毕立即调用清除方法(如 SecItemDelete、Array.Clear、SecureString.Dispose)。
  • 清除阶段: 触发清除的事件包括:用户登出、 过期、应用进入后台(选择性清除)、应用卸载、密钥轮换。清除必须同步执行并覆盖原数据。
调试提示: 在开发阶段启用敏感数据审计日志(仅限 Debug 构建),可以定期输出所有未正确清除的敏感数据对象,帮助定位遗漏的清除点。

1.4 数据分类与生命周期框架图

敏感数据分类与生命周期框架 密码 密钥 会话ID 持久化? (生命周期判定) 否(非持久化) 是(持久化) 内存 / 安全容器 组件销毁自动释放 Keychain / Keystore 加密持久存储 强制清除阶段 登出 / 过期 / 轮换 / 卸载 → 同步覆盖清除 核心约束:明文永不落盘;持久化必加密;清除必覆盖原数据

1.5 实施检查清单

  • ✅ 已枚举组件内所有敏感数据,并标记其类别(密码//密钥/会话ID)。
  • ✅ 为每类数据决策了持久化必要性:需要跨进程/跨重启?→ Keychain;否则→内存。
  • ✅ 已在代码中为每条敏感数据定义清晰的清除触发器(登出、过期、卸载等)。
  • ✅ 确认不使用 UserDefaults、NSUserDefaults、SharedPreferences 存储敏感数据。
  • ✅ 在单元测试中增加了敏感数据生命周期验证(写后读、清除后读断言为空)。
常见错误: 将 refresh_ 存入 UserDefaults 并用 plist 持久化,导致应用备份或日志抓取时泄露。记住——持久化不等于 UserDefaults,安全持久化必须使用专用安全存储 API。