7.1 安全设计:需求、威胁建模与可验证控制
安全开发生命周期不是在发布前增加一次扫描,而是让每个阶段产出下一阶段可以验证的安全决策。在依赖这些设计的实现铺开前发现缺陷,通常能减少后续返工;但“左移”不代表把全部安全责任推给开发者。
安全需求必须可测试
“系统要安全”无法验收。应写成带主体、资源、动作和失败行为的约束:
只有项目 owner 能导出完整成员列表;
导出前要求最近 10 分钟内完成 phishing-resistant reauthentication;
导出下载凭据绑定主体与对象,由服务端原子消费一次,15 分钟过期;
策略服务不可用时拒绝创建新导出;
每次尝试记录主体、项目、结果和 policy version。这里的 10/15 分钟是示例产品要求,不是通用安全标准。普通对象存储预签名 URL 通常在到期前可反复使用;若要求“一次性”,需要服务端记录消费状态并处理并发下载。还要定义网络中断后的恢复策略,例如单次兑换受限下载会话,而非把一次 HTTP range 请求就算整份文件已下载。
需求还要覆盖滥用、资源耗尽、隐私、审计、恢复和依赖失效,不只覆盖机密性。安全验收条件进入 backlog,与功能一起排序和测试。
威胁建模从系统图开始
一张可用的数据流图标出:
- 外部主体与工作负载身份;
- 进程、数据存储、队列和第三方;
- 数据流的协议、认证和敏感级别;
- 网络、租户、管理面和组织信任边界;
- 密钥、token 与个人数据的产生和销毁位置。
STRIDE 可以帮助提问:Spoofing、Tampering、Repudiation、Information Disclosure、Denial of Service、Elevation of Privilege。它是检查清单,不是风险评级;同一威胁的优先级仍由可利用性、业务影响、数据敏感度和现有控制决定。
以滥用路径验证控制链
入口:猜测 report_id / 批量导出 / 缓存污染 / 后台任务
前置:普通租户账户或被盗 service token
控制:tenant-scoped query + policy check + cache namespace
检测:跨租户 deny、异常枚举速率
恢复:撤销 token、隔离导出、审计已访问对象若控制只存在于 Web controller,队列 consumer 和导出任务仍可能绕过。Threat model 的价值是沿完整数据流寻找不一致执行点。
Trust boundary 要有明确协议
跨边界接口要定义:
- 双方身份与 credential 来源;
- message schema、长度、版本和规范化编码;
- authorization 与 tenant 传播;
- timeout、重试、幂等和重放窗口;
- 错误信息和敏感字段日志规则;
- 对端失陷时本端的最坏损失。
“内部 API”不是安全属性。边界两侧应独立验证输入,不把上游已经校验当作永恒保证。
选择控制时区分预防、检测与恢复
预防:参数化查询、最小权限、强认证
检测:异常授权拒绝、密钥使用审计、完整性验证
恢复:会话撤销、密钥轮换、备份恢复、功能降级只设计预防会把一次绕过变成长期潜伏;只做检测则让响应团队一直追赶。高风险威胁至少要有独立的预防和发现路径,以及演练过的恢复动作。
设计评审产出可追溯决策
最终文档不需要几十页模板,但应保留:范围和版本、关键图、威胁、控制、残余风险、owner、验证方法和待办。接受风险必须有期限和业务责任人,不能以“以后加 WAF”结束。
架构变更触发增量复审:新身份提供者、新数据用途、新外部集成、信任边界移动和高权限后台任务,都可能让旧结论失效。
把示例拆成验收用例:非 owner 拒绝;再认证超过 10 分钟拒绝;并发兑换同一下载凭据只有一个成功;策略服务不可用时任务不进入队列。检查输出和持久化副作用,不能只断言返回了 403。下一课继续处理发现、修复与部署验证。
参考资料
- NIST, SP 800-218 SSDF 1.1
- OWASP, Threat Modeling Cheat Sheet
- MITRE, Threat Modeling