简介:安全威胁建模是在设计与编码阶段系统性识别系统安全风险的方法,核心是从资产、攻击面和信任边界出发,判断每个关键交互点“可能出什么问题”,并把结论转化为可执行的防护措施。本文讲清它的定义、组成、工作流程、典型适用项目,以及它与渗透测试、代码审计的区别与配合方式,帮助支付、账号、权限、数据敏感类项目的开发者建立“先想威胁、再写代码”的思维方式。
安全威胁建模(Security Threat Modeling,又称威胁建模)是一种结构化的安全分析方法。 它以业务场景和系统架构为对象,先梳理系统中哪些资产有价值、哪些位置可能成为入口、哪些位置是安全与不可信区域的交界;再针对这些位置系统地问一句“这里可能出什么问题”;最后把结论转化为明确的防护要求和验证方法。一句话概括:安全威胁建模是在写代码之前(或架构发生变化之后)回答“这个系统可能怎样被攻破、应该怎样防”的过程。
它解决的核心问题是:安全缺陷往往源于对威胁的“没想到”,而不是“不会写代码”。例如,一个查询接口如果只根据请求参数中的订单号返回订单,开发者在功能测试中所有用例都能通过。但在攻击者眼里,只要把订单号换成别人的,就能读到不属于自己的数据。这类问题无法靠功能测试发现,只能在设计层面识别。
可以从三个视角理解它:
需要澄清三点常见误解:
业界常见的安全手段大致有三类:
这些手段的共同前提是:功能已经实现,然后检查实现有没有问题。而相当一部分严重安全事故,根源在于设计阶段缺少对攻击者视角的考虑:接口默认“谁调用都返回数据”,权限只在页面层控制,敏感字段一路明文流转到日志和缓存。功能完全正确,安全上却是敞开的。
行业普遍认可“安全左移”理念:越早发现和修复安全问题,代价越低。需求与设计阶段的修改可能只涉及一行设计说明;编码阶段要改接口逻辑和单元测试;上线后再修复,往往要经历数据修补、版本发布、用户通知乃至合规上报。安全威胁建模正是安全左移的关键方法,它把“安全需求”从模糊的原则变成具体、可验证的设计约束。
传统安全评审常停留在“要加强身份认证”“要做好访问控制”等原则性表述,开发者在实现时缺少明确依据。威胁建模的价值在于把每一条原则落到具体的接口、数据流和代码位置,并配套明确的防护手段和验证方式,例如“凡是从请求体读取的资源标识,都必须校验其归属主体与当前登录用户一致”。这样形成的要求既可评审,也可测试。
资产是系统中有价值、一旦泄露、篡改、丢失或被滥用就会造成损失的对象。支付系统里是资金指令、账户余额和交易流水;账号系统里是口令摘要、会话凭证和手机号;数据敏感项目里是个人信息、健康记录、位置数据和密钥。建模的第一步是列出资产及其敏感级别,否则后续所有分析都缺少判断标准。
攻击面指所有外部输入能够进入系统的入口,常见包括:
攻击面越大,需要保护的环节越多。建模时应优先聚焦那些“外部可控输入直接影响敏感操作或敏感数据”的入口。
信任边界是系统中信任级别发生变化的界线。跨越边界的数据必须重新校验,不能因为“上游已经校验过”就默认安全。典型边界包括:
一个常见的设计缺陷是“边界只画在服务入口”:请求进入时做了认证,但后续内部调用、异步任务、定时任务再次访问数据时,不再校验调用者身份与数据归属,攻击者只要找到一条绕过入口的路径,就能直达数据。
威胁是攻击者利用攻击面、跨越信任边界后可能造成的损害。业界常用的分类方法之一是 STRIDE 模型,它把威胁分为六类:
| 类别 | 含义 | 典型表现 | 常见缓解方向 |
|---|---|---|---|
| 仿冒(Spoofing) | 冒充他人身份 | 盗用会话凭证、伪造回调来源 | 强身份认证、凭证绑定与轮换、来源签名校验 |
| 篡改(Tampering) | 非法修改数据或指令 | 篡改金额、数量、回调内容 | 完整性校验、签名验签、服务端重算 |
| 抵赖(Repudiation) | 否认已执行的操作 | 用户否认发起过某笔交易 | 完整审计日志、防篡改存储、操作留痕 |
| 信息泄露(Information Disclosure) | 数据被非授权读取 | 越权查询、日志打印敏感信息 | 最小权限、字段级访问控制、脱敏与加密 |
| 拒绝服务(Denial of Service) | 系统不可用 | 资源耗尽、请求洪泛 | 限流、配额、熔断降级、弹性伸缩 |
| 权限提升(Elevation of Privilege) | 获得超出授权的能力 | 普通用户执行管理操作 | 服务端强制鉴权、权限模型最小化 |
在具体项目中,可以针对每类资产和每个信任边界,逐一询问上述问题,形成威胁清单。支付、账号、权限、数据敏感类项目几乎每一类都高频出现,这也是它们最需要威胁建模的原因。
威胁被识别后,需要评估风险大小,通常考虑两个维度:
两者结合确定优先级:可能性高、影响大的威胁必须优先处理。缓解措施可以分为四类:
对于每一个被识别的威胁,还应定义对应的验证方式:单元测试覆盖哪些越权用例、集成测试模拟哪些恶意输入、线上监控哪些异常指标。没有验证方式的缓解措施,等于没有落地。
以“以代码库为输入的安全威胁建模能力”为例,其工作过程通常可以拆解为以下步骤:
第一步:界定范围与目标。 明确分析的是哪个服务、哪条业务流程、哪类资产,避免范围过大导致分析流于表面。例如,可以聚焦“订单创建与支付回调”这一条链路。
第二步:建模系统与数据流。 阅读代码,识别对外接口、内部服务、数据存储、外部依赖和异步任务,梳理“数据从哪里来、经过哪些处理、落到哪里、谁能访问”。数据流图上用不同线型标出信任边界,这一步是后续分析的基础。
第三步:识别攻击面与信任边界。 列出所有外部可控输入点,标注每个输入点跨越了哪些信任边界、最终影响到哪些资产。重点关注三类位置:
第四步:枚举威胁并评估风险。 针对每个攻击面与边界组合,结合业务规则提出威胁假设,评估可能性与影响,形成带优先级的威胁清单。
第五步:给出缓解措施与验证方式。 为每条高优先级威胁给出具体、可落地的防护建议和验证方法,并明确残余风险。
整个流程可以用一段简化的伪代码表示威胁识别的“检查清单”,它体现了“每个入口都要回答同样几个问题”的思路:
for each 外部输入入口 in 系统:for each 跨越的信任边界 in 入口的数据流:问: 攻击者能否伪造来源? -> 需要来源校验/签名验证问: 攻击者能否篡改内容? -> 需要完整性校验/服务端重算问: 操作是否可追溯? -> 需要审计日志问: 是否会泄露超出授权的数据? -> 需要字段级访问控制与脱敏问: 是否可被高频调用拖垮? -> 需要限流与配额问: 是否能借该入口获得更高权限? -> 需要服务端强制鉴权输出: 威胁清单(入口, 边界, 威胁类型, 风险等级, 缓解措施, 验证方式)
对应的代码层面,一个典型的越权威胁及其修复方式如下:
# 存在威胁的实现(示意):信任客户端传入的身份标识def get_order(request):user_id = request.get("user_id") # 来自请求体,属于不可信输入order_id = request.get("order_id")return db.query_order(user_id, order_id)# 威胁:仿冒 + 信息泄露。攻击者把 user_id 改成他人即可读取他人订单# 改进后的实现(示意):以服务端会话身份为唯一依据,并校验资源归属def get_order(request, session):current_user = session.user_id # 只信任服务端会话order_id = request.get("order_id")order = db.query_order_by_id(order_id)if order is None:return not_found()if order.owner_id != current_user:audit_log.record("unauthorized_access", current_user, order_id)return forbidden() # 默认拒绝,而非默认放行return order
这条修改同时覆盖了“仿冒”和“信息泄露”两类威胁:身份只来自可信来源,资源访问以归属关系为准,异常尝试被记录以支持追溯。
资金类业务对正确性、完整性和不可抵赖性要求极高。需要重点建模的威胁包括:金额与数量被篡改(服务端必须基于自身订单数据重新计算应付金额,不能信任客户端传入的价格)、支付回调被伪造(必须校验来源签名与报文完整性)、重复支付与重复回调导致的资金损失(需要幂等与唯一流水约束)、退款与分账越权(需要严格的权限校验和双人复核机制)。此外,任何涉及资金的操作都应具备完整的审计留痕。
账号系统是攻击者最集中的目标。需要关注的威胁包括:口令暴力破解与撞库(需要失败次数限制、验证码、异常登录检测)、会话固定与会话劫持(需要登录后重新生成会话标识、设置合理过期时间与安全属性)、凭证传输与存储安全(口令必须以加盐哈希方式存储,敏感配置不得硬编码)、账号枚举(登录与找回流程的响应需保持一致的模糊性)、第三方登录与单点登录的信任校验(需要校验签名、有效期和签发方)。
权限类缺陷往往表现为“横向越权”(访问同级其他用户的数据)和“纵向越权”(普通用户执行管理操作)。建模时应关注:权限判断是否只在入口做了一次、后续内部调用是否仍然校验、缓存中是否残留了已撤销权限的数据、后台管理接口是否与普通接口有清晰的隔离、多租户场景下查询条件是否强制带租户标识。
涉及个人信息、健康数据、位置数据、企业机密的项目,需要额外关注数据全生命周期的安全:采集是否最小化、传输是否加密、存储是否加密与脱敏、展示是否按需遮蔽、日志中是否出现明文敏感字段、导出与共享是否经过授权、删除请求是否能真正生效。日志与监控链路常被忽视,却是敏感信息泄露的高发位置。
公网 API、开放平台、Webhook 接收端点直接暴露在攻击面最前端,需要重点评估:接口鉴权与签名机制、请求频率限制、输入校验与大小限制、错误信息是否泄露内部结构、依赖组件是否存在已知漏洞。部署在云上时,还应结合“责任共担模型”梳理边界:云平台负责底层基础设施安全,使用者负责身份认证配置、访问控制策略、数据加密和日志审计等部分。对象存储访问权限配置不当、数据库公网暴露、密钥硬编码在配置文件里,都是云环境中常见的高危问题。
对于内部工具、个人项目、原型验证类的小系统,不必在项目初期投入完整建模流程。但以下节点值得做一次轻量建模:
轻量做法可以只回答三个问题:有哪些敏感数据、有哪些外部入口、每个入口是否校验了身份与归属。这比完全不做要好得多。
| 活动 | 核心问题 | 发生时点 | 关注点 | 主要产出 |
|---|---|---|---|---|
| 安全威胁建模 | 可能出什么问题 | 设计阶段、架构变更时 | 架构、数据流、业务规则 | 威胁清单、缓解要求、验证方式 |
| 静态代码扫描 | 代码写法有没有已知缺陷模式 | 编码与持续集成阶段 | 代码实现 | 缺陷报告与修复建议 |
| 动态扫描与渗透测试 | 运行中的系统能否被攻破 | 测试与上线前 | 已部署系统 | 漏洞报告、修复复测 |
| 安全风险评估 | 整体风险是否可接受 | 周期性或重大变更时 | 资产、流程、合规性 | 风险登记册、处置计划 |
| 威胁情报 | 外部正在发生什么攻击 | 持续 | 攻击手法与趋势 | 预警信息与检测规则 |
四者的关系是互补而非替代:威胁建模负责“事前想清楚”,静态与动态测试负责“事中查出来”,风险评估负责“事后管得住”,威胁情报提供外部视角。一个成熟的安全体系通常是“威胁建模 + 自动化扫描 + 定期渗透测试 + 持续监控”的组合。
安全威胁建模的本质,是在开发过程中系统性引入攻击者视角:先明确保护什么,再明确从哪里可能被攻击、在哪里必须设防,然后逐条判断可能出什么问题、风险有多大、怎么防、如何验证。它把安全从“上线前的最后一道检查”变成“设计阶段的第一项输入”。
对于支付、账号、权限、数据敏感类项目,它带来的价值最直接:把越权、篡改、仿冒、泄露等高发威胁提前拦截在设计阶段,显著降低后期修复成本和安全事件概率。对于普通小项目,它不需要完整的流程,但“列出敏感数据、列出外部入口、逐入口校验身份与归属”这种轻量做法,同样是有效的安全习惯。
理解它的定义、组成与流程之后,开发者可以在写下一段处理资金、凭证或用户数据的代码之前,先花几分钟问自己:这段代码可能被怎样滥用? 这就是安全威胁建模真正的落地方式。