安全与密钥
移动端安全不是「加个 ProGuard 就结束」。参考工程把 日志、Token、备份、一次性 secret 写成可检查的约定。
日志策略
| 构建类型 | 网络日志 | 其它 |
|---|---|---|
| Debug | OkHttp 可为 BASIC | 可有高层诊断 |
| Release | HttpLoggingInterceptor.Level.NONE | 不打 URL/头/体/Token |
AppLogger 会脱敏常见 token/secret 模式;release 下不附加 throwable 细节。
禁止出现在 release 日志中的内容(示例):
- access / refresh token
- admin token
- 设备 secret、bootstrap token
- 邀请、邮箱验证、 密码重置等 dev token
- 完整请求/响应体中的敏感字段
Token 处理
| 数据 | 存储 | 使用 |
|---|---|---|
| Access / Refresh | DataStore only | 拦截器与登出/刷新 |
| Admin token | DataStore only | 仅 /internal/admin/* → X-Admin-Token |
| 进程内 access 快照 | 内存 | 避免每个请求都读 DataStore |
要点:
- Token 不进 Room
- 诊断页只显示 admin token 是否已配置,不显示值
- Refresh 单飞:并发 401 只触发一次刷新
- Refresh 失败 → 清空会话
备份与导出
AndroidManifest:android:allowBackup="false"(以仓库为准)- 备份 / 数据提取 XML 排除
files/datastore/ - 避免会话与 admin token 被系统备份到云端再恢复到别的设备
一次性设备 Secret
创建设备等接口可能返回 仅显示一次 的 secret:
- 只放在 ViewModel / UI 状态
- 一次性对话框展示并可复制
- 关闭对话框或离开路由后清除
- 不写 Room、不进普通列表缓存
验收:离开详情再回来,不应再次看到同一 secret。
Room 缓存审计原则
缓存表可以存业务标识(如 keyId),但:
- 不存 access/refresh/admin token
- 不存 device secret / bootstrap token
- 不把「仅一次」的凭证持久化成「永远可查」
改 Entity 时做一次敏感字段扫视。
错误信息展示
| 字段 | 用途 |
|---|---|
ErrorResponse.error.message | 普通 UI 可展示 |
code / details | 诊断、debug、Admin;不要默认弹给最终用户 |
发布前安全核对(摘要)
- Release 网络日志为
NONE - R8 / 优化按工 程配置开启(AGP 9 gradual R8 等以
gradle.properties为准) - 无 keystore、签名密码进 Git
- 端点默认仍是团队约定环境;本地 override 仅 opt-in
- 备份关闭 / DataStore 排除
- 一次性 secret 路径人工点过一遍
完整条目见仓库 docs/SECURITY_NOTES.md 与 docs/RELEASE_CHECKLIST.md。
下一步:构建与发布。