绝大多数安全团队把精力放在防火墙、WAF和漏洞扫描上,却忽略了一个更致命的问题:业务逻辑本身的设计缺陷。这类漏洞不涉及SQL注入或XSS,而是直接利用网站的正常功能作恶,传统的自动化扫描器几乎无法发现它们。威胁建模恰恰是解决这类问题的最有效手段——它让你在写第一行代码之前,就能系统性地预判攻击者会如何滥用你的业务逻辑。

什么是威胁建模,为什么它对业务漏洞有效

威胁建模不是安全行业凭空造出来的概念,它的核心逻辑极其朴素:站在攻击者的角度,拆解你的系统,找出哪里最容易被攻击。具体到网站业务,就是把你网站上的每一个功能点都当成一个潜在的攻击面,然后问自己三个问题:攻击者想达到什么目的?他通过什么路径能达到?我们现有的防护措施能不能拦住他?

举个例子,一个电商网站的优惠券功能。开发团队关注的是券能不能正常发放、能不能正常抵扣。但攻击者关注的是:我能不能用一张券重复使用?我能不能修改券的面额?我能不能把别人的券拿来自己用?我能不能在退款时套现?这些都不是代码层面的漏洞,而是业务流程设计时留下的逻辑缺口。威胁建模要做的,就是在产品设计阶段就把这些攻击路径画出来,逐个堵上。

STRIDE模型在网站业务中的落地方法

STRIDE是目前应用最广的威胁建模框架,六个字母分别代表六类威胁:身份欺骗、数据篡改、抵赖、信息泄露、拒绝服务、权限提升。听起来很抽象,但映射到网站业务上非常具体。

身份欺骗在网站业务中就是越权访问。比如一个普通用户通过修改URL中的订单ID参数,就能看到其他用户的订单详情。这类问题的根源在于后端只校验了用户是否登录,没有校验这个订单是否属于当前用户。建模时你需要画出每一个涉及资源访问的接口,标注出“这个接口凭什么相信请求者有权访问这个资源”。

数据篡改的典型场景是客户端校验被绕过。很多网站把价格参数放在前端传回后端,攻击者用浏览器开发者工具把价格从999改成0.01,然后提交订单。后端如果直接接收了这个价格,就造成了严重的数据篡改漏洞。建模时要梳理所有从客户端接收的数据,标记哪些数据是“不可信的”,必须由后端重新从数据库或服务端会话中获取。

抵赖在业务场景中往往被忽视。用户下单后否认自己操作过,商家发货后用户声称没收到,这些都属于抵赖风险。建模时需要明确哪些操作必须留下不可篡改的审计日志,日志中要包含操作人、操作时间、操作内容、IP地址、设备指纹等信息,并且日志的存储要独立于业务系统,防止攻击者删除日志掩盖行踪。

信息泄露不只是数据库被拖库。一个搜索功能如果对输入的关键词响应时间有明显差异,攻击者就能通过时间差推断出数据库中是否存在某个数据,这叫侧信道信息泄露。还有更常见的,接口返回的数据字段过多,前端只展示了用户名和头像,但接口把用户手机号、身份证号、家庭地址全返回了,攻击者抓个包就能拿到这些敏感信息。建模时需要对每个接口的返回字段做最小化审查。

拒绝服务在业务层面的表现比流量攻击更难防范。一个发送验证码的接口如果没有限制,攻击者可以用脚本无限发送,短信费用瞬间飙升。一个复杂的报表查询接口如果允许用户自定义查询条件,攻击者可以构造一个让数据库全表扫描的查询,把服务器CPU打满。建模时需要对每一个资源消耗型的功能点做频率限制和资源隔离。

权限提升的路径往往出人意料。一个网站的管理后台做了严格的登录校验,但管理员的一个“预览”功能会生成一个临时链接给普通用户查看,这个临时链接里包含了管理后台的session信息,普通用户拿到这个链接后就获得了管理员权限。建模时需要追踪每一个权限传递的路径,确保权限不会以任何形式泄露给低权限用户。

数据流图:把威胁建模画出来

光靠脑子想很容易遗漏,数据流图是威胁建模最实用的工具。不需要复杂的UML工具,白板上画几个框框就能大幅提升分析的系统性。画法很简单:用方框表示外部实体,比如用户、第三方支付、短信服务商;用圆形表示处理过程,比如登录验证、下单、支付回调;用两条平行线表示数据存储,比如用户表、订单表;用箭头表示数据流向。

画完数据流图之后,每一条数据流都是一个信任边界。数据从一个区域流到另一个区域时,信任假设就发生了变化。比如用户浏览器发来的数据完全不可信,但很多开发人员潜意识里把它当成了可信数据。内部服务之间的调用往往被认为可信,但如果一个服务被攻破,攻击者就能在内网横向移动。把每一条跨越信任边界的数据流标注出来,然后逐一用STRIDE模型审查,这是最系统性的做法。

实际操作中不需要把整个网站画成一张巨大的图,那样反而没法分析。按功能模块拆开,一次分析一个核心业务流程。比如这周分析用户注册登录流程,下周分析订单支付流程。每个流程的数据流图通常不会超过十个节点,半小时就能画完,但能发现的问题往往让人出一身冷汗。

攻击树:从攻击目标倒推攻击路径

STRIDE是从系统设计出发向外看,攻击树则是从攻击目标出发向里看。两者结合使用效果最好。攻击树的画法是把攻击者想要达成的最终目标作为根节点,然后逐层分解:要达到这个目标,有哪些子目标?每个子目标又有哪些实现方法?

以“盗取用户账户”为根节点,分解出来的子目标可能包括:获取用户密码、绕过密码验证、劫持用户会话。获取用户密码又可以分解为:暴力破解、撞库、钓鱼、从其他泄露数据库获取。绕过密码验证可以分解为:利用密码重置流程漏洞、利用记住我功能漏洞、利用OAuth回调漏洞。劫持用户会话可以分解为:XSS窃取Cookie、中间人攻击、Session固定攻击。

这样一层层分解下去,你会得到一个完整的攻击路径图谱。然后对每一条路径评估两个指标:攻击难度和攻击影响。攻击难度低且影响大的路径就是需要优先处理的风险。这个方法的优势在于它迫使你思考那些不明显的攻击路径,而不是只盯着最常见的几种漏洞类型。

业务逻辑漏洞的典型模式与建模要点

多年的安全事件积累下来,业务逻辑漏洞其实有规律可循。威胁建模时如果能有意识地对照这些模式,效率会高很多。

竞态条件是最容易被忽视的一类。用户同时发起多个请求,利用系统处理的时间差获利。比如提现功能,用户账户余额100元,同时发起两个提现100元的请求,两个请求在扣款前都读到了余额100元,都判定余额充足,结果用户提走了200元。建模时遇到涉及余额、库存、次数限制的操作,必须标注“需要原子化操作”,用数据库行锁或分布式锁保证并发安全。

工作流绕过是另一类高频问题。很多业务流程有严格的步骤顺序,比如先实名认证才能提现,先收货确认才能评价。但攻击者会尝试跳过中间步骤直接调用后续接口。建模时需要把每个业务流程的状态机画出来,明确每个状态允许执行哪些操作,后端在每个操作前必须校验当前状态是否合法。

负值攻击是参数校验的经典盲区。一个转账功能,开发人员校验了金额不能为空、不能超过余额,但没校验金额是否为正数。攻击者输入-1000元,系统从自己账户扣了-1000元,相当于给对方转了1000元的同时自己还增加了1000元。建模时对所有数值型参数都要标注“需要校验数值范围”,包括下限。

整数溢出在高级语言中相对少见,但在涉及金额计算时仍然需要关注。特别是使用了有符号整数且没有做溢出检查的场景,攻击者可以构造一个极大的数字导致溢出后变成一个很小的数字。建模时涉及金额、数量的计算,需要明确数据类型和范围检查策略。

将威胁建模嵌入开发流程的实操方案

威胁建模最大的落地障碍不是技术难度,而是流程阻力。开发团队觉得这是安全团队的事,安全团队觉得开发不配合。要解决这个问题,威胁建模必须轻量化、工具化,融入现有的开发节奏。

最有效的切入点是需求评审阶段。产品经理写完需求文档后,在评审会上花十五分钟做一个快速威胁建模。不需要画完整的图,就对着需求文档里的每个功能点问五个问题:谁可以用这个功能?他能操作什么数据?这个数据从哪里来?操作结果会影响什么?如果有人滥用这个功能会怎样?这五个问题问完,大部分明显的业务逻辑问题就会暴露出来。

技术方案评审阶段可以做更深入的数据流图分析。架构师在讲解技术方案时,把关键的数据流画出来,团队一起用STRIDE过一遍。这个过程本身也是技术方案的一次压力测试,很多方案设计上的缺陷在这个过程中会被发现,不限于安全问题。

代码审查阶段,把威胁建模产出的关键风险点作为审查清单。比如之前建模时标记了“订单金额必须从服务端获取”,代码审查时就专门检查订单相关的代码是否从请求参数中直接读取了金额。这种对照检查的方式比漫无目的地看代码效率高得多。

测试阶段,把攻击树产出的攻击路径转化为测试用例。安全测试不再靠测试人员的灵感和经验,而是有系统性的测试计划。每条攻击路径都有对应的测试用例,测试覆盖率可以量化。

一个完整的威胁建模示例

假设你运营一个内容付费平台,用户购买文章后可以阅读。我们用威胁建模来分析这个看似简单的功能。

数据流图包括:用户浏览器发起购买请求,应用服务器处理购买逻辑,调用第三方支付网关,支付网关异步回调通知支付结果,应用服务器更新订单状态并开通阅读权限,用户浏览器请求阅读文章,应用服务器校验权限后返回文章内容。

用STRIDE逐条分析。身份欺骗:用户A购买文章后,用户B通过修改请求中的文章ID能否读到文章?答案取决于权限校验是否绑定用户身份。数据篡改:支付回调的签名验证是否严格?如果签名算法被破解,攻击者可以伪造支付成功的回调。抵赖:用户声称自己付了钱但没看到文章,有没有支付流水和权限开通记录作为证据?信息泄露:文章内容通过接口返回,是否做了防抓取保护?攻击者能不能通过遍历文章ID抓取全部付费内容?拒绝服务:如果攻击者用脚本高并发请求文章内容,会不会把服务器带宽打满?权限提升:普通用户能否通过某个接口参数把自己升级为VIP?

攻击树以“免费阅读付费文章”为根节点。子目标包括:绕过支付流程、在支付前获取文章内容、利用退款机制、利用分享机制、从CDN或缓存中获取。绕过支付流程又可以分解为伪造支付回调、修改订单金额为0、利用优惠券漏洞实现零元购。在支付前获取文章内容可以分解为利用预览功能、利用搜索引擎快照、利用API版本差异。

分析完成后,产出风险清单,按优先级排序。高风险项必须在上线前解决,中风险项纳入迭代计划,低风险项记录在案持续监控。这个过程做完,你对这个功能的安全状况就有了清晰的认知,而不是上线后祈祷没人发现漏洞。

威胁建模不是一次性的活动。系统每增加一个新功能、每接入一个新的第三方服务、每修改一次权限模型,都需要重新评估。把它当成和写单元测试一样的日常实践,你的网站业务漏洞会大幅减少,而且是在它们变成线上事故之前。