3. 威胁建模与安全需求

威胁建模不是写一份“安全风险清单”,而是把系统设计、攻击者视角和工程验收标准连接起来的一套推理过程。它的核心价值在于:在代码尚未写完之前,先识别“哪些资产值得保护、攻击者会从哪里进入、哪些控制措施必须落地、如何证明这些措施真的有效”。对Web应用而言,威胁建模的产物最终必须落到可验证的工程任务上,例如“登录接口在连续失败5次后锁定账户10分钟,并记录来源IP与时间戳”,而不是停留在“防止暴力破解”这样的口号。

一、先确定建模对象与信任边界

威胁建模的第一步是画出数据流图(DFD),明确外部实体、处理过程、数据存储和数据流,并用信任边界标出权限或安全等级发生变化的位置。以典型的“用户登录并访问订单”场景为例:浏览器是外部实体,Web前端与API网关处于DMZ,认证服务与订单服务位于内网,MySQL与Redis属于数据存储。信任边界至少包括:互联网到DMZ、DMZ到内网、内网到数据层。攻击者一旦跨过某条信任边界,就可能在新的信任域内获得额外能力,因此边界上的每个接口都值得重点分析。

Web登录与订单访问:数据流与信任边界威胁建模 浏览器 外部实体 DMZ 信任域 Web前端 静态资源/表单 API网关 路由/限流 内网 信任域 认证服务 JWT/会话 订单服务 业务逻辑 数据层 MySQL 用户/订单 Redis 会话/限流 HTTPS 内网调用 SQL/缓存 边界① 互联网→DMZ 边界② DMZ→内网 边界③ 内网→数据层 STRIDE 威胁映射(示例) S 仿冒 伪造JWT/会话 撞库登录 边界①/② T 篡改 改订单金额 改用户ID越权 边界②/③ R 抵赖 否认下单 日志缺失 边界② I 信息泄露 错误堆栈 订单遍历 边界①/③ D 拒绝服务 登录接口刷量 大分页查询 边界① E 提权 普通用户 访问管理接口 边界② 攻击树:获取他人订单数据 目标:读取他人订单 越权访问订单接口 篡改orderId参数 服务端未校验归属 返回订单详情

二、用STRIDE逐条推导威胁

STRIDE把威胁分为六类:仿冒(Spoofing)、篡改(Tampering)、抵赖(Repudiation)、信息泄露(Information Disclosure)、拒绝服务(Denial of Service)和提权(Elevation of Privilege)。使用时不要机械地给每个组件套六类,而应针对“数据流跨越信任边界”的位置逐条提问。例如在“浏览器→API网关”这条流上:攻击者能否伪造合法会话(S)?请求参数在传输中是否可被篡改(T)?服务端是否记录了足够的操作日志以支持事后追责(R)?错误响应是否泄露内部结构(I)?登录接口是否可被高频调用(D)?普通用户能否调用管理接口(E)?

以登录接口为例,一个可落地的威胁描述应包含攻击路径与影响,例如:“攻击者使用泄露的用户名列表,对/api/login发起每秒200次的密码尝试,若服务端无失败计数与锁定策略,则可在数小时内撞库成功,导致账户被接管。”这样的描述才能直接推导出安全需求。

三、用攻击树拆解攻击路径

攻击树从攻击者目标出发,逐层分解为子目标,直到叶子节点是可实施的具体动作。以“读取他人订单”为目标,可分解为:越权访问订单接口 → 篡改orderId参数 → 服务端未校验订单归属 → 返回订单详情。攻击树的价值在于暴露“防御断点”:只要在任意一层加入有效控制,整条路径就被切断。例如在服务端强制校验order.user_id == session.user_id,即可阻断该路径。攻击树还可以标注成本、难度和检测概率,帮助团队优先修复高收益路径。

四、从威胁到可验证的安全需求

威胁建模的终点是安全需求,而安全需求必须可验证。建议采用“控制目标 + 触发条件 + 预期行为 + 验证方式”的结构。例如针对撞库威胁,安全需求可写为:

  • 控制目标:限制单账户密码尝试频率。
  • 触发条件:同一账户连续登录失败达到5次。
  • 预期行为:锁定该账户10分钟,返回统一错误码ACCOUNT_LOCKED,并记录来源IP、时间戳、失败次数。
  • 验证方式:使用自动化脚本连续提交6次错误密码,第6次应返回锁定错误;检查日志中是否包含完整字段;10分钟后使用正确密码应可登录。

针对越权访问,安全需求可写为:订单查询接口必须校验当前会话用户与订单归属一致,否则返回403;验证方式为使用用户A的会话请求用户B的订单ID,响应必须为403且不包含订单任何字段。针对信息泄露,安全需求可写为:生产环境错误响应不得包含堆栈、SQL语句或内部路径;验证方式为构造异常请求,检查响应体与响应头。

威胁建模不是一次性活动。每次新增接口、修改认证逻辑或引入第三方服务时,都应更新数据流图与威胁清单,并把新增安全需求纳入迭代验收。

五、把安全需求转化为工程任务

安全需求进入研发流程时,需要拆成可分配、可估算、可验收的任务。以“登录锁定”为例,可拆为:后端在Redis中维护login_fail:{username}计数器,TTL设为600秒;认证服务在密码校验失败时执行INCR并设置过期时间;当计数≥5时直接返回锁定错误;网关层对/api/login增加每IP每分钟60次的限流;日志服务采集锁定事件并接入告警。每个任务都应有明确的验收标准,例如“Redis键在10分钟后自动消失”“限流触发时返回429”“告警在5分钟内到达值班群”。

为了让威胁建模结果可追踪,可以使用一张轻量级威胁登记表,字段包括:威胁编号、STRIDE类别、受影响组件、攻击路径、安全需求、工程任务、验证方法、状态。下表给出一个示例片段:

编号STRIDE组件安全需求工程任务验证方法
T-01S 仿冒认证服务连续失败5次锁定10分钟Redis计数器+TTL+锁定错误码脚本连续6次错误密码,第6次返回锁定
T-02E 提权订单服务订单查询校验归属在DAO层增加user_id条件用户A请求用户B订单返回403
T-03I 信息泄露API网关生产错误不返回堆栈统一异常处理器+错误码映射构造异常请求检查响应体
T-04D 拒绝服务API网关登录接口每IP每分钟60次网关限流规则+429响应压测工具触发限流并观察429

六、验证与复盘

安全需求是否真正落地,要靠验证来回答。验证方式包括:单元测试中断言锁定逻辑、集成测试中模拟越权请求、在预发环境用脚本触发限流并观察429、检查日志字段是否完整。若验证失败,应回到威胁模型确认是控制缺失、实现错误还是需求本身不完整。复盘时重点关注:哪些威胁被遗漏、哪些控制被绕过、哪些验证用例没有覆盖真实攻击路径。通过“建模—需求—任务—验证—复盘”的闭环,威胁建模才能从文档变成工程能力。

不要把威胁建模做成一次性合规文档。没有验证方法的安全需求无法验收,没有纳入迭代的威胁清单很快会过期。每次需求评审时至少问三个问题:新增了哪些信任边界?STRIDE中哪几类威胁被引入?对应的验证用例是什么?