医疗安全首先是少拥有数据
加密只能保护某些状态下的数据;真正减少风险的是缩短数据路径、寿命、可见范围与权限。
数据库已经加密,下面哪个泄露面仍然存在?
- 通知预览里的患者姓名
- 崩溃日志里的消息正文
- 应用处于解锁状态时的屏幕内容
- 以上全部
先画数据流,再谈密码学
列出敏感数据从哪里进入、在哪些位置短暂停留、发往哪里、何时删除:相机传感器、临时文件、内存 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 禁止明文;证书固定有轮换与误封风险,只有威胁模型确实需要时采用。
给敏感 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 证明真实设备没有失控。