3. 威胁建模与安全需求
威胁建模不是写一份“安全风险清单”,而是把系统设计、攻击者视角和工程验收标准连接起来的一套推理过程。它的核心价值在于:在代码尚未写完之前,先识别“哪些资产值得保护、攻击者会从哪里进入、哪些控制措施必须落地、如何证明这些措施真的有效”。对Web应用而言,威胁建模的产物最终必须落到可验证的工程任务上,例如“登录接口在连续失败5次后锁定账户10分钟,并记录来源IP与时间戳”,而不是停留在“防止暴力破解”这样的口号。
一、先确定建模对象与信任边界
威胁建模的第一步是画出数据流图(DFD),明确外部实体、处理过程、数据存储和数据流,并用信任边界标出权限或安全等级发生变化的位置。以典型的“用户登录并访问订单”场景为例:浏览器是外部实体,Web前端与API网关处于DMZ,认证服务与订单服务位于内网,MySQL与Redis属于数据存储。信任边界至少包括:互联网到DMZ、DMZ到内网、内网到数据层。攻击者一旦跨过某条信任边界,就可能在新的信任域内获得额外能力,因此边界上的每个接口都值得重点分析。
二、用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-01 | S 仿冒 | 认证服务 | 连续失败5次锁定10分钟 | Redis计数器+TTL+锁定错误码 | 脚本连续6次错误密码,第6次返回锁定 |
| T-02 | E 提权 | 订单服务 | 订单查询校验归属 | 在DAO层增加user_id条件 | 用户A请求用户B订单返回403 |
| T-03 | I 信息泄露 | API网关 | 生产错误不返回堆栈 | 统一异常处理器+错误码映射 | 构造异常请求检查响应体 |
| T-04 | D 拒绝服务 | API网关 | 登录接口每IP每分钟60次 | 网关限流规则+429响应 | 压测工具触发限流并观察429 |
六、验证与复盘
安全需求是否真正落地,要靠验证来回答。验证方式包括:单元测试中断言锁定逻辑、集成测试中模拟越权请求、在预发环境用脚本触发限流并观察429、检查日志字段是否完整。若验证失败,应回到威胁模型确认是控制缺失、实现错误还是需求本身不完整。复盘时重点关注:哪些威胁被遗漏、哪些控制被绕过、哪些验证用例没有覆盖真实攻击路径。通过“建模—需求—任务—验证—复盘”的闭环,威胁建模才能从文档变成工程能力。