3.2 数据存储与加密


3.2 数据存储与加密

本节摘要:M1 的根因几乎总在「不该存的全存了 + 存了也不加密」。SOURCE 3.3 要求用 Android Keystore / iOS Keychain 管密钥、限制数据生命周期、安全擦除。本节含 EncryptedSharedPreferences 与 Keychain 属性对照。

本节导航

  1. 区分 Keystore 硬件密钥与「自己写 AES」
  2. 配置 iOS Keychain Accessible 级别
  3. 实现注销时安全擦除与 allowBackup=false

一、该存什么、存多久

数据 建议 禁止
Access Token 加密 + 短 TTL 明文 SharedPreferences
密码 不存(用 Token) 任何形式本地存密码
生物模板 系统 API 管 自建指纹文件
缓存图片 沙箱内、非敏感 SD 卡明文

二、Android:Keystore + EncryptedSharedPreferences

val masterKey = MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() // 密钥在 Android Keystore,prefs 文件本身加密

Keystore 要点(SOURCE 3.3):

  • 私钥不可导出
  • 可设 setUserAuthenticationRequired(true) 绑定生物识别
  • Android 6+ 硬件 backed 因设备而异,用 KeyInfo.isInsideSecureHardware 检测

Manifest:

<application android:allowBackup="false" ...>

三、iOS:Keychain 与 Data Protection

let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kSecAttrAccount as String: "access_token", kSecValueData as String: tokenData ] SecItemAdd(query as CFDictionary, nil)

WhenUnlockedThisDeviceOnly:不解锁不可读、不 iCloud 同步、不换机迁移 — 适合会话类 secret。

安全存储数据流

安全存储数据流

安全存储数据流

四、生命周期与擦除

  • 用户注销:clear() prefs + SecItemDelete + 内存 zeroize
  • 卸载:系统删沙箱;注意 SD 卡遗留文件
  • 临时文件:用后 delete(),支付类 App 禁写外部存储
检查 命令/方法
备份面 adb backup 应失败或空
明文 MobSF 搜 passwordtoken 字符串
Root 读 加密后 prefs 为乱码

⚠️ 常见坑:只加密 value,key 仍暴露业务含义 — 攻击者知道搜哪个 key。

💡 关键直觉密钥不进 APK、不进代码 — 由 Keystore/Keychain 生成与持有。

一节小结

  • 能不存就不存;必须存则 Keystore/Keychain
  • allowBackup=false 与 Keychain Accessible 级别是基线
  • 注销必须可验证地擦除所有会话材料
  • MobSF 静态扫描辅助发现 M1

下一节:通信 TLS、Pinning 与认证授权服务端化。

深化:加密选型与密钥生命周期

「存了加密」不等于「安全」,关键是三件事:算法对不对、密钥在哪、生命周期怎么管。算法上,对称加密用 AES-GCM(自带认证,防篡改),绝不用 ECB 或 DES;哈希口令用 bcrypt 或 argon2,绝不裸 MD5/SHA1。密钥必须由系统安全容器生成与持有:Android Keystore、iOS Keychain,私钥不可导出。自己硬编码一个 key 再混淆,等于把钥匙藏在代码里。

// 正确:密钥由 Keystore 生成,不可导出 val masterKey = MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() // 错误:硬编码密钥(混淆也救不了) // val key = "MySecretKey123456"

生命周期管理包含:会话结束后清除 Token、注销时删除所有密钥与缓存、卸载时确认无外部存储残留、App 更新时密钥迁移策略。密钥的「不可导出」属性是安全容器存在的意义——攻击者即使拿到设备,也拿不到 key material,只能逐次调用加密接口。

环节 要求 常见错误
算法 AES-GCM ECB/DES
密钥 Keystore/Keychain 硬编码
传输 密钥不出设备 密钥发到服务端
生命周期 注销即删 残留旧版本数据

数据分级的落地步骤

数据分级是存储安全的前提,落地分四步。第一步盘点:列出 App 所有写入磁盘的数据项;第二步分类:按敏感度分公开、半敏感、高敏感、极高四档;第三步定策:每档确定存储方式、加密要求、生命周期;第四步验证:用扫描与取证手段确认实际落盘与策略一致。实际项目中,Token、支付信息、生物特征这类高敏感数据最容易出问题——既要有加密存储,也要在注销与过期时彻底清除。

# 分级落地示例 数据: 用户昵称 -> 半敏感 -> 可明文但禁日志 数据: 会话 Token -> 高敏感 -> Keystore/Keychain,注销即删 数据: 银行卡 PAN -> 极高 -> 不落客户端,仅内存短用

验证是容易被跳过的最后一步。很多团队策略写得很全,实际代码还是把 Token 写进了普通 SharedPreferences。用 MobSF 或手工 grep 抽查发布包,策略与实现一致才算落地。

步骤 动作 验收
盘点 列出所有落盘数据 清单完整
分类 四档分级 无遗漏项
定策 存储+生命周期 策略明确
验证 抽查发布包 实现一致

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U