上周我参加一场需求评审会,产品经理兴致勃勃地讲完“文档在线分享”的新功能,我随口问了一句:“分享链接会带上用户身份吗?如果链接被人转发出去,拿到的第三方能不能直接访问文档?”会议室安静了两秒,产品经理开始翻需求文档,最后来了一句:“这个……我们还没想过。”
这就是需求阶段威胁建模存在的意义。安全左移这几年在行业内被反复强调,但很多人对“左移”的理解停留在“早一点做安全测试”或者“早点接入扫描工具”,真正把安全思考推进到需求阶段的团队其实很少。需求阶段做一次威胁建模,成本可能只是开一次会,但它能把大量“做完再返工”的隐患直接掐灭在源头。这篇文章我就把自己在多个项目里实践需求阶段威胁建模的完整方法、踩过的坑和可直接复用的模板整理出来,给正在推动安全左移落地的同学一个参考。
1. 为什么要在需求阶段做威胁建模
1.1 安全左移的本质是降低修复成本
安全左移的核心逻辑其实非常朴素:越早发现问题,修复成本越低。IBM 的一份经典数据虽然不是绝对精确,但它描述的量级关系至今仍被业界广泛引用——如果需求阶段修复一个缺陷的成本是 1,设计阶段大概是 20 倍,编码阶段 40 倍,测试阶段 60 倍,发布上线之后可能到 100 倍甚至更多。
这个成本曲线背后是真实的工程逻辑。需求阶段的一个安全决策失误,影响的不是一个函数的写法,而是整个功能的形态。拿“文档分享”来说,如果在需求阶段没有明确“分享链接必须绑定访问者身份”这条约束,后续的设计会围绕“匿名链接+访问密码”来做,编码、测试全部跟着这个假设走。等上线后被人抓包或者遍历ID发现漏洞,你改的就不只是一行代码,而是改需求、改设计、改接口、改前端流程,甚至要处理已经泄露的数据。我见过不少项目,安全漏洞本身不复杂,复杂的是在错误的需求假设上叠加了几层设计之后,修复成本被放大了好几倍。
所以安全左移不是把安全测试提前一点那么简单,它真正应该做的是把“安全需求”当作和功能需求同等重要的一等公民,在需求阶段就想清楚这个功能需要防御什么。而威胁建模,就是系统化完成这个思考过程最有效的手段。
1.2 需求阶段建模与设计阶段建模的分工
很多团队一提到威胁建模,条件反射想到的是数据流图、STRIDE、攻击树这些设计阶段的东西。需求阶段做的威胁建模跟设计阶段的粒度完全不同,两者解决的问题也不一样。
设计阶段的威胁建模回答的问题是:“这个技术方案安全吗?”你需要拿到具体的架构设计,画出详细的数据流图,标记每个组件、每条数据通路、每个信任边界,然后分析攻击者可能从哪里打进来。这个阶段的分析可以直接指导具体的安全方案选型,比如是加签名还是加加密,是走网关还是走应用层校验。
需求阶段的威胁建模回答的问题是:“这个业务场景需要考虑哪些安全风险?”它不关心具体技术实现,只关心业务逻辑、角色权限、数据敏感度、外部交互方式这些“需求层”的信息。举一个例子,一个支付相关的业务,需求阶段要识别出“账号可能被盗用发起支付”这个风险,并把它转化为“必须有身份核验机制”这样的安全需求;至于身份核验是上双因素认证还是行为风控,那是设计阶段才需要考虑的。
这两个阶段是上下游关系:需求阶段的输出一份安全需求清单,在设计阶段被进一步细化成具体的安全方案。如果跳过需求阶段直接在设计阶段建模,很多问题在设计阶段看起来是“方案怎么加固”的问题,实际上根源是“需求当初就没想清楚”,这时候能做的往往只是打补丁。
1.3 方法选型:轻量优先
威胁建模的框架不少,OCTAVE、PASTA、TRIKE、VAST,每个都有完整的方法论支撑,但我不建议一上来就选重框架。OCTAVE偏向组织级风险评估,PASTA强调与业务目标对齐、攻击者视角的分析,这些方法论本身没问题,只是对需求评审来说太重了——全套跑下来需要几天时间,还要专门的培训,大多数团队根本坚持不了。
我实践下来最顺手的是“场景拆解 + 简化版数据流图 + STRIDE清单 + 风险排序”的组合。STRIDE(伪造、篡改、否认、信息泄露、拒绝服务、权限提升)虽然不是为需求阶段量身定做的,但它的好处在于每种威胁类型都对应一类明确的安全目标,可以直接翻译成安全需求。比如你识别出“信息泄露”威胁,对应的安全需求就是“机密性保护”;识别出“伪造”威胁,对应的安全需求就是“身份认证”。这个映射非常自然,即使是没有安全背景的产品经理,带着一份STRIDE提问清单也能参与讨论。
2. 核心细节解析与实操要点
2.1 需求阶段威胁建模需要的五个输入
做一次需求阶段的威胁建模,不需要等所有需求细节都冻结,但要确保五个输入基本齐备,否则讨论容易变成空谈。
第一个是业务需求描述。至少要有一页纸的需求摘要,能把业务场景讲清楚:谁在用、用来干什么、核心流程是什么。第二个是角色权限模型,系统里有哪些角色,每个角色能做什么、不能做什么,这决定了信任边界和越权分析的起点。第三个是数据分类清单,系统里流动的数据哪些是公开的、哪些是敏感的个人信息、哪些是严格受限的商业数据,这个直接决定“信息泄露”类威胁的严重程度。第四个是既有系统架构的简要信息,新功能是搭在已有系统上还是全新系统,有没有已有的认证、权限、审计基础设施可以复用。第五个是合规要求,比如涉及个人信息就先标记出来,涉及金融交易的就要考虑强认证和交易日志。
这些输入不需要规范的文档,一张半成品需求幻灯片、几个人口头对齐一下也可以。关键是别跳过角色和数据分类,这两个是我见过被忽略最多、后来出问题最多的点。
2.2 STRIDE逐项提问:从威胁到需求
STRIDE 六个字母对应的威胁类型,在需求阶段可以被转化成一组非常接地气的提问清单。不要一上来就念威胁建模理论,就拿业务场景一个一个问:
伪造(Spoofing):这个场景里需要确认“操作者是谁”吗?如果不需要确认身份,那意味着任何人都能以任意身份做事,这是不是业务能接受的?如果需要确认,现有的手段(登录、API密钥、数字签名)够不够?
篡改(Tampering):业务数据有没有可能被未经授权地修改?修改之后会造成什么后果?比如订单金额被改了、日志被改了、配置被改了,分别是什么影响?
否认(Repudiation):用户做了某个关键操作之后,系统有没有能力证明“是他做的”?没有不可否认性的话,用户投诉、纠纷、审计的时候会不会扯皮?
信息泄露(Information Disclosure):数据会不会被不该看到的人看到?这个“不该看到的人”不仅是外部攻击者,还包括内部越权、合作伙伴滥用、测试环境泄露等。
拒绝服务(Denial of Service):服务不可用会造成什么损失?业务上能不能接受?有没有对资源消耗失控的担心?这里的重点不是搞一套DDoS防御方案,而是看这个功能在业务上的可用性优先级。
权限提升(Elevation of Privilege):低权限用户有没有可能做到高权限用户才能做的事?普通用户能不能访问管理接口、修改他人数据、执行管理操作?
每识别出一条威胁,就直接标记它对应的安全属性:认证、完整性、不可否认性、机密性、可用性、授权。这个映射关系简单直观,是需求阶段威胁建模最有生产力的环节——因为每个威胁都能直接变成一个后续可追踪的安全需求条目。
2.3 风险排序:安全需求也要分优先级
需求阶段威胁建模最常见的失控方式,是识别出了二十几条威胁,然后每条都想做成安全需求,最后产品经理直接拒绝配合。安全需求不是多多益善,它要跟功能需求抢开发资源,必须排序。
我在团队里默认用“可能性 x 影响”两维打分,每个维度分高、中、低三档,得出总的优先级。不引入复杂的CVSS、DREAD评分体系,原因很简单:需求阶段很多细节还没定,打分太精确反而是伪精确。可能性看这个威胁是否容易被触发——是否需要特殊条件、是否需要攻击者具备很高权限或者很多前置知识;影响看触发后对业务、客户、资产的实际损失。
排序结果直接映射到需求的优先级体系里。最高优先级的(高风险且高可能性)必须成为本迭代的安全验收条件;中风险的进产品backlog,在后续迭代按计划消化;低风险的记录下来留档,每季度复盘一次,看业务变化后是否升级。这样安全需求就和普通需求一样进入了迭代排期,而不是游离在流程之外的“安全待办清单”。
2.4 安全需求怎么写才不像废话
需求阶段威胁建模的最终产出是安全需求,但它太容易写成废话了。我见过无数条类似“系统应具备良好的安全性,防止恶意攻击”这样的描述,这种需求没有任何验证手段,提了等于没提。
能落地的安全需求至少要包含三个要素:可验证的行为描述、验证方式、对应当前威胁。行为描述要说清楚系统在什么条件下必须做什么,而不是空泛地说“具备某某能力”。举例来说,如果识别出“文档分享链接被转发导致越权访问”的信息泄露威胁,对应的安全需求不建议写成“分享链接应安全”,而是写成“当用户访问分享链接时,系统必须校验其登录状态,且仅当该用户拥有文档的查看权限时才允许加载内容”。
更进一步,我喜欢用Gherkin格式把安全需求写成验收条件,这样开发、测试、安全三方都清楚什么算“做到了”。
功能: 文档分享的越权访问控制 场景: 访问分享链接时校验访问权限 当 未登录用户访问带令牌的分享链接 那么 系统跳转到登录页面 并且 登录后若该用户不在分享名单中 那么 系统返回403且不加载文档内容这种写法的好处在于,它直接进入开发验收和测试用例,不会变成一份无人问津的安全文档。安全需求跟着用户故事走、跟着测试走,才真正在流程里活下来了。
3. 实操过程与核心环节实现
3.1 会前准备与人员要求
需求阶段威胁建模建议开成一次专题会,时长控制在90分钟以内,太长大家注意力会散,产出质量反而不高。参会人员不需要多,必须到的是产品经理(讲清楚业务需求)、技术负责人(了解系统边界和约束)、测试负责人(在验收条件上把关)。如果团队里有安全工程师或者有安全经验的老研发,负责引导讨论和记录结果;没有的话也不用等,让技术负责人拿着检查清单引导也行。
会前必须给参会人发一份一页纸的会议材料,内容包括:本次要分析的功能简介、参与的角色列表、涉及的数据类型、一个新功能最小可用的系统边界图。这个材料不需要写得多工整,重点是让所有人到会之前脑子里有一个确定的讨论对象,而不是现场开始同步需求背景——那会浪费大量时间。
会议最大的一坑是“什么需求都往里塞”。威胁建模会非常容易跑偏成功能评审会、技术方案讨论会。在会议开始前就要明确:不讨论功能要不要做、不讨论技术怎么实现,只回答一个问题——这个场景存在哪些安全风险,哪些必须转化为需求。
3.2 标准流程六步走
我在实际项目中把流程固定成六个步骤,跑顺之后基本不会出大错。
第一步,业务场景拆解。让产品经理把主要业务场景讲一遍,最好拆成“某人通过某方式完成某件事”这样的句式。拆出三到五个核心场景就够了,不要追求穷举所有边角场景。
第二步,画简版数据流图。在白板上画出主体、数据、处理过程和存储,这个阶段不需要高大上的建模工具,方框和箭头足够了。画图的核心目的不是画得漂亮,而是让全体参会者对齐信息流转路径——很多越权问题的根源就是对数据路径理解不一致。
第三步,标记信任边界。在图上标出“线上用户不可信”“内部服务之间可信”“数据库边界可信”这些边界线。信任边界标错了,后面所有分析都会错。有个很常见的错误是默认“内部系统互相调用可以信任”,实际上内部接口被任意调用导致越权的案例多了去了。
第四步,用STRIDE逐项过。按照伪造、篡改、否认、信息泄露、拒绝服务、权限提升的顺序,逐个场景提问,每识别出一个威胁就记录一条。这一步最考验引导者的节奏,不要在一个威胁上无限深挖,先保证六类都过一遍。
第五步,风险登记和排序。把识别出的威胁填入风险登记表,用可能性x影响算法快速排序,分类到高、中、低三档。
第六步,把高、中优先级威胁转化为带验收条件的安全需求,写进需求文档或用户故事,指定负责人。
3.3 完整示例:在线文档分享功能
我用“在线文档分享”这个例子走一遍完整流程,方便大家直接参考。
业务场景拆解后得到三个核心场景:用户A将文档主动分享给用户B;用户A生成一个分享链接,任何人获得链接即视为被授权;用户A取消分享后链接失效。这个拆解本身就能发现业务逻辑上的风险点——第二个场景中“获得链接即视为被授权”,这就是一个需要重点审视的需求假设。
画简版DFD:文档所有者、Web应用、文档存储服务、访客,两条主要数据流分别是“所有者上传并设置分享权限”和“访客通过链接拉取文档”,信任边界画在Web应用对外一侧,存储服务与Web应用之间视为内部边界。
STRIDE逐项过一遍后,识别出以下核心威胁:
| 威胁类型 | 威胁描述 | 可能性 | 影响 | 优先级 |
|---|---|---|---|---|
| 信息泄露 | 分享链接被转发给未授权人员,对方可以直接访问文档 | 高 | 高 | 高 |
| 伪造 | 访客伪造所有者身份获取更高权限 | 中 | 高 | 高 |
| 篡改 | 文档内容被未授权访客修改(如果支持在线编辑) | 中 | 高 | 中 |
| 否认 | 所有者否认发起了分享,导致纠纷时定责困难 | 低 | 中 | 中 |
| 权限提升 | 普通访客通过修改链接参数访问管理接口 | 中 | 高 | 高 |
针对高优先级威胁,直接生成安全需求并写清楚验收条件。比如“分享链接必须绑定访问者身份,链接本身不构成授权凭证”这条需求,它的验收条件就是:未登录用户访问分享链接时,系统必须跳转登录;已登录但不在文档分享名单中的用户,系统返回403且不加载文档内容;文档所有者在分享管理页面取消分享后,所有已有链接在1分钟内全部失效。
值得特别说的是“链接不再作为授权凭证”这个需求,它本质上是从业务逻辑层面消除了一整类威胁。如果设计阶段才意识到这一点,几乎意味着推翻原有方案;而在需求阶段改,只是改一个产品规则。这就是安全左移在需求阶段最直观的收益。
3.4 工具选型参考:从白板到平台化
工具层面我的建议是分阶段选型。团队刚刚起步的时候,白板、Miro线上白板配一套模板就够了,重点是培养威胁建模的思维习惯和团队默契。工具越复杂,门槛越高,团队反而越不愿意用。
等团队跑顺了流程、画DFD有稳定需求之后,可以引入微软的Threat Modeling Tool或者OWASP的Threat Dragon。前者是桌面软件,免费,内置STRIDE分析,能根据DFD自动生成威胁清单,适合Windows环境下的快速建模;后者开源、跨平台,存成JSON方便版本管理,更适合研发团队通过代码仓库管理威胁模型。
再往上走,像IriusRisk这类商业平台支持风险库、需求追踪、与Jira对接,适合在几十个团队的大规模组织里统一管理威胁建模资产。但我不建议在地基没打好的时候直接上这类平台,工具永远只是手段,如果团队没有威胁建模的思考习惯和一套稳定的流程,工具只会成为第二个没人维护的文档仓库。
最终的工具决策可以用一个简单标准判断:这个工具是否让“识别一个威胁、记录一个风险、追踪一条需求”变得更顺畅。工具带来的额外开销如果超过了识别威胁本身的开销,那就是选错了。
4. 常见问题与排查技巧实录
4.1 团队没有安全人员怎么办
没有专职安全人员是绝大多数团队要面对的现实,但这不意味着需求阶段威胁建模就做不了。我见过不少团队,第一次做威胁建模的时候完全由产品经理和技术负责人主导,安全背景反而是通过几次讨论一点点建立起来的。
做法是把威胁建模开成“教练式”的会:前两次由有经验的人(外部顾问、研发团队里安全技术比较好的老员工)带着做,STRIDE清单提前发给所有人,会上引导产品经理自己回答“如果链接被转发会怎样”这类问题。几次下来,产品经理自己就会养成在需求描述中补充安全约束的习惯。我在实操中发现,产品经理绝对能理解威胁建模的逻辑,前提是你不要用安全术语把他劝退。你用“什么情况下坏人能拿到不该拿的东西”来提问,比“评估Spoofing威胁的暴露面”效果好十倍。
另外,没有专职安全人员的团队更应该建立一份业务侧的常见威胁检查清单——比如所有上传功能都检查一次文件类型校验,所有分享功能都检查一次越权访问。这份清单可以来自团队经验、公开的行业清单、或者第一次威胁建模的会议记录,它能保证在没有专家的情况下,团队也能覆盖掉最常见的坑。
4.2 敏捷迭代太快,建模跟不上怎么办
敏捷团队最常见的抱怨是:威胁建模太慢了,一个迭代就两周,哪有时间专门开一次会。我的处理方式是分层:重大版本、新模块引入、涉及资金和敏感数据的功能变更,做一次完整的威胁建模;小的迭代增量,走一个15分钟的快速增量检查。
快速增量检查其实不复杂:把这次变更涉及的数据流和上一次威胁建模时的DFD对比一下,问问“这次有没有新增数据入口、有没有新增信任边界、有没有改变数据流向”。三个问题都回答“否”,那这次变更就不需要重新建模。只要回答有一个“是”,那就要对新增部分快速过一遍STRIDE。
这个方法跑顺之后,完整建模的需求频率会很低——可能一个季度只有几次,而每次增量检查只需要15分钟。威胁建模才不会被团队当成流程负担,而是真正成为迭代的一部分。
还有一个实践技巧:把威胁建模结果转化为安全用户故事放进backlog之后,研发团队消化安全故事和消化功能故事的节奏是相同的。不要试图在同一个迭代里既让开发写代码又处理一堆紧急安全需求,除非这个威胁的优先级确实高到必须立刻修。
4.3 安全需求后面没人落实怎么办
需求阶段识别出了风险、写好了需求,结果开发迭代过程中还是被当作“软需求”晾在一边——这是很常见的情况。问题的根源往往不在执行端,而是安全需求没有跟验证活动绑定起来。
我在实践中的做法是:每条安全需求都要绑定至少一个验证动作,并且把验证动作写进测试计划。比如前面提到的“分享链接必须绑定访问者身份”这条需求,对应的验证动作是一个集成测试用例,专门覆盖“已登录但未授权用户访问分享链接返回403”的场景。测试用例挂在需求ID下面,需求没有闭环,测试就有一项是红的。
另外一个“隐形”的原因是安全需求在需求文档里单独开了一个章节,而不是写在每个用户故事里。产品经理评审需求的时候只看到功能描述,看不到安全条件,开发拿到的用户故事里也没有。安全需求必须内嵌到相应的用户故事中,和功能条件一起被开发、测试共同审阅。我在团队里定的规矩是:一条用户故事如果带安全验收条件,必须以安全验收条件全部通过作为完成定义的一部分,否则这个故事不能算“完成”。
4.4 常见翻车场景与避坑
做得久了,会发现需求阶段威胁建模的翻车场景高度重复,我总结几个最常见的。
翻车一:把DFD当艺术创作。有些团队把大量精力花在画精细的数据流图上,恨不得把每个字段的流转路径都画出来,结果STRIDE分析只花了15分钟。这是典型的倒置。需求阶段的数据流图只需要到实体和存储级别,画图的目的不是图本身,而是用它来对齐讨论对象、定位边界。
翻车二:风险登记表成了“死亡台账”。识别出几十条威胁,开会时大家斗志昂扬,会后表格一存,再也没有人打开过。要避免这个问题,核心是把高优先级的威胁转化成需求动作而不是“风险记录”。“风险记录”给人感觉是可以暂缓,而“需求条目”是需要排期的。
翻车三:威胁清单不随需求演化。需求阶段做完建模,后续需求每次都变化,威胁模型还停留在三个月前的版本。威胁建模不是一次性的,至少在功能发生实质性变化的时候要回看一次。我在实践中每两周在迭代计划会之前花10分钟翻一下已有需求列表,看有没有需求变化触发了重新建模的条件。
翻车四:把威胁建模等同于“安全加固会议”。团队一旦发现安全痛点,就开始讨论“要不要上WAF”“要不要做加密”,这又跑到了设计阶段。需求阶段的核心产出是“要什么”,不是“怎么要”。“要不要在分享功能中引入访问者身份校验”是需求问题;“用JWT还是OAuth实现校验”是设计问题,两者分开讨论效率才高。
4.5 一轮摸爬滚打后的自查清单
如果你准备在团队里推行需求阶段威胁建模,建议先对照下面这张清单自检,把节奏先理顺再谈产出。
| 检查项 | 是否做到 |
|---|---|
| 有固定的迭代周期触发威胁建模,而不是想起来才做 | |
| 产品经理能清晰识别出涉及的敏感数据类型和角色权限 | |
| 每次需求评审都有STRIDE清单在手,不会遗漏威胁类型 | |
| 识别出的威胁经过可能性x影响排序,形成优先级 | |
| 高优先级的威胁被写成带验收条件的安全需求,进入迭代排期 | |
| 安全需求由开发、测试在完成定义中确认闭环 | |
| 需求变更后能及时复盘已有威胁模型,增量更新 |
如果七项里面有超过两项打不了勾,说明威胁建模还没有真正融入研发流程,先补最短板的一项,比全面铺开更有用。
最后分享一个尝试了很久才沉淀下来的习惯
带过几次需求阶段威胁建模之后,我最大的体会不是“方法有多复杂”,而是“坚持做简单的事效果反而最好”。一开始我们每个功能都试图把威胁识别做得很全,团队成员疲惫不说,产出质量也一般。后面我们做了一个调整:每次建模结束后,把这次识别出的威胁和对应的安全需求整理进一个团队共用的“安全需求库”,按业务类型分类。
这个库前期看起来平平无奇,但坚持两三个迭代之后,效果会非常明显——新项目做威胁建模时,产品经理可以直接从库里的同类业务调出历史安全需求做对照,替换掉一半从零开始思考的时间。威胁建模真正沉淀成了团队的资产,而不是每次从零起步的会议。如果你所在团队正准备开始安全左移的实践,我建议把这件事当成一个长期的建设工程来做:每一轮威胁建模,都是在为团队积累一份能不断复用、持续演化的安全需求知识库。