4. 威胁建模与代码审查的衔接
威胁建模的产出如果只停留在设计文档里,代码审查就会退化成“凭经验找坏味道”。真正有效的衔接,是把设计阶段识别出的威胁,翻译成代码中可定位、可验证的审查要点。本节围绕 STRIDE、攻击树和数据流图(DFD)三种常用手段,说明它们如何落到具体的代码审查动作上,并给出审查用例与威胁的映射方法。
一、三种建模手段在代码审查中的分工
STRIDE 提供威胁分类的“检查维度”,攻击树提供攻击者视角的“路径分解”,DFD 提供数据在系统中的“流动与信任边界”。三者不是并列的三种画法,而是互补的三层信息:DFD 回答“数据从哪里来、经过谁、到哪里去”,STRIDE 回答“每个环节可能出哪类问题”,攻击树回答“攻击者会怎样组合这些环节达成目标”。
在代码审查中,DFD 的价值最高,因为它能直接对应到代码里的入口、处理函数、存储访问和外部调用。审查前应先把 DFD 上的每个进程、数据存储、外部实体和数据流标注出来,再对每个元素套用 STRIDE 分类,最后用攻击树把高风险路径展开成具体审查项。
二、从设计威胁推导审查要点的具体方法
推导过程可以固定为四步,但每步的产出必须落到代码位置,而不是停留在威胁名称。
第一步:把 DFD 元素映射到代码入口。例如 DFD 中的“进程 P1 订单接口”,在代码中对应 OrderController.getOrder();数据存储“订单库”对应 OrderRepository.findById();外部实体“浏览器”对应 HTTP 请求参数。映射表应记录元素名、代码文件、函数名、行号范围。
第二步:对每个元素套用 STRIDE 并筛选适用项。不是每个元素都适用全部六类。数据存储通常重点看 T(篡改)、I(泄露)、D(容量耗尽);进程重点看 S(仿冒)、E(提权)、T(参数篡改);数据流重点看 I(窃听)、T(中间人篡改)。筛选后只保留真正适用的威胁,避免审查清单膨胀。
第三步:用攻击树把威胁展开为攻击路径。以“越权读取他人订单”为例,根节点是攻击目标,子节点是达成路径:未校验 orderId 归属、SQL 未参数化导致注入、缓存键未含用户标识导致串号。每条路径对应一个可审查的代码点。
第四步:把攻击路径翻译成审查用例。审查用例不是“检查是否有越权”,而是“检查 getOrder() 中 orderId 是否与当前会话 userId 做归属校验,校验失败是否返回 403 而非 200”。
三、审查用例与威胁的映射方法
映射的核心是建立“威胁编号—审查用例编号—代码位置—验证方式”四元组。建议在威胁建模阶段就给每个威胁分配唯一编号,如 T-ORD-001,审查用例编号与之对应,如 TC-ORD-001。这样在代码审查记录中可以直接回溯到设计威胁,避免遗漏。
| 威胁编号 | STRIDE 类别 | DFD 元素 | 审查用例 | 代码位置 | 验证方式 |
|---|---|---|---|---|---|
| T-ORD-001 | S 仿冒 | 进程 P1 订单接口 | 检查接口是否强制校验会话令牌,令牌失效是否拒绝 | OrderController.getOrder() | 构造无 token / 过期 token 请求,预期 401 |
| T-ORD-002 | E 提权 | 进程 P1 订单接口 | 检查 orderId 是否与当前 userId 做归属校验 | OrderService.loadOrder() | 用 A 用户 token 请求 B 用户订单,预期 403 |
| T-ORD-003 | T 篡改 | 数据流 P1→P2 | 检查支付金额是否由服务端重新计算,是否信任客户端传入 | PaymentService.create() | 篡改请求中 amount 字段,预期以服务端价格为准 |
| T-ORD-004 | I 信息泄露 | 数据存储 订单库 | 检查异常返回是否包含 SQL 语句或堆栈 | GlobalExceptionHandler | 触发数据库异常,预期返回通用错误码 |
| T-ORD-005 | D 拒绝服务 | 进程 P1 订单接口 | 检查查询接口是否有分页上限与频率限制 | OrderController.list() | 请求 pageSize=100000,预期被截断或拒绝 |
四、落地时的关键细节
第一,DFD 的信任边界必须与代码中的鉴权切面一致。如果 DFD 上标注了 B1 边界,代码中却没有对应的拦截器或过滤器,说明设计意图未落地,这本身就是高优先级审查发现。
第二,攻击树的叶子节点要能直接对应到一行或一段代码。如果某个叶子节点无法定位到代码,说明它要么是架构级问题,要么需要进一步分解。
第三,审查用例要包含“预期结果”和“实际结果”两栏。例如 T-ORD-002 的预期是 403,实际若返回 200 且包含他人订单数据,则判定为未通过,并记录为缺陷。
通过上述衔接,威胁建模不再是设计阶段的“一次性文档”,而是代码审查的输入源。审查人员拿到的不再是模糊的“注意安全”,而是带编号、带代码位置、带验证方式的审查用例。这样既提高了审查的针对性,也让威胁建模的产出真正被消费掉。