本节摘要:SOURCE 3.1 强调在需求阶段做威胁建模(STRIDE)、最小权限、输入验证、安全错误处理与可信 SDK 选型。本节给出 Android/iOS 清单化评审表,可直接贴进 PR 模板。
对「扫码登录」故事:
| STRIDE | 威胁 | 控制 |
|---|---|---|
| Spoofing | 伪造二维码 | 短时 nonce + 服务端轮询 |
| Tampering | 篡改 deep link | 签名验证 |
| Repudiation | 否认登录 | 审计日志 |
| Info Disclosure | 二维码含 token | 仅 session id |
| DoS | 刷登录接口 | 限流 |
| Elevation | 普通用户调 admin API | 服务端 RBAC |

Android 12+:精确位置、蓝牙权限拆分。Manifest 只声明必需权限;运行时 requestPermissions 带 rationale。
iOS:Info.plist 用途说明字符串必填,否则审核拒;PHPhotoLibrary 等敏感 API 需 NSPhotoLibraryUsageDescription。
// 安全错误:统一错误码,不泄露 SQL sealed class ApiResult { data class Err(val code: String, val userMsg: String) : ApiResult() }
| 项 | 问题 |
|---|---|
| 权限 | SDK 是否要多要通讯录 |
| 网络 | 是否向未知域名上报 |
| 更新 | 最近 CVE |
| 数据 | 是否缓存 PII 到本地 |
| 开源 | 许可证与维护状态 |
SOURCE 3.1:只使用信誉良好、及时更新的库;集成前读隐私政策与数据流图。
⚠️ 常见坑:Analytics SDK 默认采集 IMEI — Android 10+ 已限制,但仍可能违规采集。
💡 关键直觉:安全设计写在架构与 PR 检查项里,比事后 pentest 便宜一个数量级。
下一节聚焦本地敏感数据如何加密存储。
STRIDE 是结构化提问工具,但要真正落地,先画数据流图:用户、App、API、数据库,标出每个节点存储了什么数据、经过什么信道、谁能触碰。然后对每条边问 STRIDE 六个问题。画完数据流图,攻击面就清晰了:凡是有外部输入的地方,都是需要验证的边界。
# 数据流草图(示例:扫码登录) 用户 -> [扫码] -> App -> [POST /scan?nonce=] -> API -> [校验] -> 用户会话 App <- [session 短码] <- API # 对每条边标注:加密?鉴权?限流?日志?
第三方 SDK 评估是安全设计的盲区。评估五项:权限是否多余、是否向未知域名上报数据、是否缓存 PII、是否有高危 CVE、许可证与维护状态。集成后定期复查依赖清单,CVE 通报要能定位到具体 SDK 与版本。
| 评估项 | 问题 | 否决标准 |
|---|---|---|
| 权限 | 是否多要通讯录 | 用途不符 |
| 网络 | 是否外传数据 | 未披露上报 |
| 更新 | CVE 严重度 | Critical 未修复 |
| 数据 | 本地缓存 PII | 明文缓存 |
安全设计不该只在需求评审时出现,而应嵌入三个节点:需求阶段做威胁建模并产出数据流图与风险清单;设计评审阶段核对最小权限、输入验证、错误处理与 SDK 评估;开发阶段把检查项贴进 PR 模板,每个合并请求都过一遍。三步下来,安全从「专家救火」变成「流程的一部分」。评审的产出要可追溯:每一条风险有负责人、有期限、有验证方式,而不是会上一句「知道了」。
| 节点 | 动作 | 产出 |
|---|---|---|
| 需求 | 威胁建模 | 风险清单 |
| 设计 | 原则核对 | 评审意见 |
| 开发 | PR 检查项 | 通过记录 |
输入验证要覆盖所有入口:Deep Link、推送 payload、Intent 携带参数、剪贴板内容、二维码扫描结果。每个入口都做四件事:类型校验(期望的类型与格式)、长度限制、白名单过滤(如 scheme 白名单)、上下文校验(如跳转目标是否允许)。特别注意:验证逻辑要在客户端与服务端各做一次——客户端验证拦截普通用户,服务端验证才是真正的安全边界。
设计评审时问四个问题就够了:数据存在哪、怎么加密;网络怎么走、怎么验证证书;登录与授权谁在裁决;输入从哪来、验证了吗。四个问题覆盖存储、通信、认证、输入四大类,答不全就是评审没做透。评审产出是风险清单而不是「通过了」,每条风险都要落到负责人与期限。