卷 IV · CH 16 · 深度 16/18

医疗安全首先是少拥有数据

加密只能保护某些状态下的数据;真正减少风险的是缩短数据路径、寿命、可见范围与权限。

THREAT MODELKEYSTOREDATA MINIMIZATION
▷ 先答一下

数据库已经加密,下面哪个泄露面仍然存在?

  1. 通知预览里的患者姓名
  2. 崩溃日志里的消息正文
  3. 应用处于解锁状态时的屏幕内容
  4. 以上全部

先画数据流,再谈密码学

列出敏感数据从哪里进入、在哪些位置短暂停留、发往哪里、何时删除:相机传感器、临时文件、内存 Bitmap、上传队列、数据库、日志、通知、剪贴板、备份、分析 SDK、服务端。每多一份副本,就多一套访问控制、清理和审计责任。

Celo 的公开安全页面强调患者信息不永久存储在设备、临床照片不进入个人相册,并在应用活动时才把信息短暂放在手机内存。这些是产品声明,不等于我们知道其内部实现;但作为面试案例,它展示了一个重要原则:数据保留政策本身就是安全架构

可以带走的判断:先删除不必要的数据与权限,再保护确实必须拥有的那一小部分。

Android 沙箱是起点,不是终点

内部存储默认受应用沙箱隔离,私有数据不应放公共外部存储。需要密钥时,用 Android Keystore 生成或保护密钥材料,让密钥具备不可导出、可要求用户认证等属性;不要把密钥、API token 或服务端秘密硬编码进 APK。

Keystore 不会自动加密数据库,也不会阻止已解锁应用读取明文。常见设计是用 Keystore 保护一个数据加密密钥,再由经过审计的库完成文件或数据库加密。密钥轮换、设备迁移、备份恢复、锁屏变化与认证失效都要设计,不是调用一次 API 就结束。

认证、授权与设备完整性是三件事

生物识别回答「当前操作者能否解锁本地凭据」,不是服务端权限。服务端仍要对每个会话与附件做授权。Credential Manager 统一密码、passkey 与联合登录入口;短寿命访问令牌、可撤销会话与安全刷新流程限制泄露后果。

Play Integrity 可以给服务端风险信号,但不能成为唯一身份凭证,更不能假设设备没 root 就绝对可信。客户端运行在用户控制的设备上,关键授权必须在服务端执行。

最容易漏的是「顺手可见」

  • 通知:锁屏默认隐藏敏感正文,只给「有一条新消息」;提供用户与组织政策。
  • 截图与最近任务:敏感页面可用 FLAG_SECURE,同时理解它不是对所有录屏和外部相机的绝对防护。
  • 日志:禁止正文、令牌、患者 ID 进入 Logcat、崩溃报告与分析事件;建立统一脱敏器。
  • 剪贴板:不要默认开放敏感字段复制;确有需求时缩短寿命并给清晰提示。
  • 备份:检查自动备份与数据提取规则,确保敏感文件不被意外云备份或设备迁移。
  • 网络:只用 TLS,Network Security Config 禁止明文;证书固定有轮换与误封风险,只有威胁模型确实需要时采用。
威胁模型回答按「资产—攻击者—入口—控制—剩余风险」说:患者消息是资产;丢失设备、恶意应用、越权用户与日志平台是攻击面;控制包括最小落盘、Keystore、服务端授权、锁屏隐藏、导出组件限制、审计与远程撤销。
⌨ 自己跑一遍

给敏感 Activity 加最小屏幕保护,并把日志改成只记录不可逆、无业务含义的诊断 ID:

截图:被系统阻止
最近任务缩略图:隐藏
日志:event=message_sync diagnosticId=7f3a,无正文
class SecureChatActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
        super.onCreate(savedInstanceState)
        setContent { SecureChatApp() }
    }
}

fun logSyncFailure(diagnosticId: String, cause: Throwable) {
    Log.w(
        "MessageSync",
        "event=message_sync diagnosticId=$diagnosticId type=${cause::class.simpleName}"
    )
}

在 Android Studio 模拟器运行,尝试系统截图与最近任务预览;再触发失败并检查 Logcat。确认日志里没有消息正文、会话 ID、令牌、文件 URI 或患者信息。

▸ 在现实里

应用内相机的价值不只在 UI 顺手,而在数据边界:图片不经过个人相册、可在拍摄时绑定患者同意、临时文件可控清理。权限也应最小化:能用系统 Photo Picker 就不要索取整个媒体库访问。

✗ 这个直觉是错的

「用了 HTTPS 和加密数据库,就符合医疗安全要求。」它们只覆盖传输与静态存储的部分风险,不处理越权、通知、日志、备份、截图、分析 SDK 与保留期限。

安全是端到端的数据生命周期与组织流程。客户端控制、服务端授权、审计、事故响应和法规要求要一起设计。

◇ 面试收口

答案是第 4 项。数据库加密只保护数据库处于静态且密钥不可用时的部分场景。通知、日志与已解锁 UI 都可能暴露明文,所以需要分别的最小化、访问控制与显示政策。

这一章的一句话

安全不是给所有副本上锁,而是先让敏感数据尽量没有副本。

下一章把「我觉得没问题」换成证据:同一段代码需要单元测试证明状态转换、Compose 测试证明用户语义、Macrobenchmark 证明发布构建速度,生产 Vitals 证明真实设备没有失控。