4. 威胁建模与代码审查的衔接

威胁建模的产出如果只停留在设计文档里,代码审查就会退化成“凭经验找坏味道”。真正有效的衔接,是把设计阶段识别出的威胁,翻译成代码中可定位、可验证的审查要点。本节围绕 STRIDE、攻击树和数据流图(DFD)三种常用手段,说明它们如何落到具体的代码审查动作上,并给出审查用例与威胁的映射方法。

一、三种建模手段在代码审查中的分工

STRIDE 提供威胁分类的“检查维度”,攻击树提供攻击者视角的“路径分解”,DFD 提供数据在系统中的“流动与信任边界”。三者不是并列的三种画法,而是互补的三层信息:DFD 回答“数据从哪里来、经过谁、到哪里去”,STRIDE 回答“每个环节可能出哪类问题”,攻击树回答“攻击者会怎样组合这些环节达成目标”。

在代码审查中,DFD 的价值最高,因为它能直接对应到代码里的入口、处理函数、存储访问和外部调用。审查前应先把 DFD 上的每个进程、数据存储、外部实体和数据流标注出来,再对每个元素套用 STRIDE 分类,最后用攻击树把高风险路径展开成具体审查项。

从设计威胁到代码审查要点的衔接链路 ① 数据流图(DFD):定位数据路径与信任边界 外部实体 浏览器/客户端 进程 P1 订单接口 进程 P2 支付服务 数据存储 订单库 订单请求 支付调用 写入 信任边界 B1 ② STRIDE:对每个 DFD 元素套用威胁分类 S 仿冒 T 篡改 R 抵赖 I 信息泄露 D 拒绝服务 E 提权 示例:P1 订单接口 → 需检查身份校验(S)、参数签名(T)、审计日志(R)、错误信息脱敏(I)、限流(D)、权限判断(E) ③ 攻击树:把高风险路径展开为可审查的代码点 目标:越权读取他人订单 未校验 orderId 归属 SQL 未参数化 缓存键未含用户标识

二、从设计威胁推导审查要点的具体方法

推导过程可以固定为四步,但每步的产出必须落到代码位置,而不是停留在威胁名称。

第一步:把 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 且包含他人订单数据,则判定为未通过,并记录为缺陷。

建议在代码审查工具中把威胁编号作为标签打在对应的代码行上。这样每次修改该行代码时,审查工具可以自动提示关联威胁,形成“设计威胁—代码变更—回归审查”的闭环。
不要把 STRIDE 六类全部机械地套到每个元素上。过度覆盖会导致审查清单冗长、重点模糊。应先按 DFD 元素类型筛选适用威胁,再结合攻击树确定优先级,把审查精力集中在高风险路径上。

通过上述衔接,威胁建模不再是设计阶段的“一次性文档”,而是代码审查的输入源。审查人员拿到的不再是模糊的“注意安全”,而是带编号、带代码位置、带验证方式的审查用例。这样既提高了审查的针对性,也让威胁建模的产出真正被消费掉。