4.9

View in English

4.9 安全软件开发生命周期

概述与动机

大多数安全缺陷并不神秘。它们是普通的错误:缺失的授权检查、被错误信任的输入、无人更新的依赖项、粘贴到配置文件中的密钥。真正让它们代价高昂的,是被发现的时机。在编写需求时发现的缺陷,只需要一次对话就能解决。同样的缺陷若在上线前一周的渗透测试中才被发现,就要临时手忙脚乱;若在生产环境中才被发现,代价就是一次事件、一次披露,以及信任的流失。安全软件开发生命周期(SSDLC)是这样一门学问:通过在计划、设计、构建、评审、发布和运维软件的每一个阶段都内建安全性,而不是在最后附加一次安全测试,来尽早、持续地捕获这些缺陷。

本章是第 4 部分的流程主线。它将第 4.2 章的应用层防御(如何编写能抵御攻击的代码)与第 4.4 章的运行时纪律(如何在防御被测试时进行检测和响应)联系起来,继承第 4.1 章(安全基础与文化)的思维方式,并产出第 4.6 章(合规与治理)转化为审计证据的成果。那些章节讲的是”是什么”和”为什么”,本章讲的是”何时”和”如何”:在交付流程的哪个环节该有哪项控制、谁负责它,以及它把守着哪道关卡。

对于大型团队而言,回报在于杠杆效应。当数百名工程师各自独立决定要做多少安全工作时,最薄弱的一环就决定了你真实的风险敞口。一套明确的生命周期能让安全路径成为默认路径,从而让一名普通工程师无需超常发挥就能交付相对安全的软件。对企业而言,这种一致性降低了每一次审计和集成的成本。对政府而言,由于公民无法选择其他机构来处理其税务或福利数据,一套文档化的生命周期往往是运营的法定前提,本章中的框架正是对应着这一义务。

关键原则

  • 安全左移:在最便宜的阶段发现并修复缺陷,而那永远是最早的阶段。
  • 让安全成为流水线的属性,而不是某个人的属性:将关卡自动化,使安全路径成为便捷路径。
  • 让每个阶段都有明确的负责人和关卡,从需求到运维,都有清晰的通过条件。
  • 管理风险而非打勾清单:按可利用性和影响力对真正重要的缺陷排定优先级,多次持续的小检查胜过一次缓慢的周期末审计。
  • 把依赖项和构建系统视为攻击面的一部分,因为攻击者正是这样看待它们的。
  • 度量整个项目,因为一个无法度量的生命周期,就是一个无法改进的生命周期。

建议

在整个生命周期中实现安全左移

Shift-left testing(左移测试)是指将验证提前到交付流程中更早的阶段,靠近决策发生的那一刻,而不是发布前的那一刻。把这个理念应用到安全上,就重新定义了目标:你不是在最后把安全”测”进去,而是从一开始就把安全”设计”和”构建”进去,然后持续验证。这背后的经济账十分明显:在规划会议中重写一条需求几乎是零成本的;在代码已经存在之后重做一个设计缺陷要花费数天;在生产环境中修补一个漏洞则要付出一次事件的代价。不过左移也有一种失败模式:把一堆安全工具丢给开发者,就以为大功告成。做得好的左移,会为每一项早期检查配上相应的支持,让每个发现都附带上下文、修复建议和责任人。

编写安全需求和滥用案例

安全始于任何代码之前,始于你如何框定这项工作。除了说明系统应该做什么的功能需求之外,还要编写说明系统绝不能做什么、必须保证什么的安全需求:哪些数据是敏感的、谁有权访问、必须记录哪些日志、适用哪些法规。然后用滥用案例(abuse case)和误用案例(misuse case)来补充你的用户故事:简短描述恶意行为者会如何试图破坏每项功能。当一个用户故事说”客户重置自己的密码”时,滥用案例会追问”攻击者重置了别人的密码”,而这个问题会推动出关于速率限制、令牌过期和验证的真实需求。这能在缺陷还只是屏幕上的文字时,就把整类缺陷暴露出来。让滥用案例与用户故事保持关联,使其贯穿设计、评审直至完成定义(definition of done)。

在设计阶段设置威胁建模关卡

威胁建模(threat modelling)是一种在构建之前系统性审视设计以发现潜在问题的实践:识别资产、绘制数据如何跨越信任边界流动、枚举威胁,并决定缓解措施。这是你能做的杠杆效应最高的安全活动,因为它作用于一个改动成本仍然低廉的设计阶段。对任何涉及身份验证、敏感数据、资金或新增信任边界的功能,把它作为一道轻量级关卡,逐一检视欺骗、篡改、否认、信息泄露、拒绝服务和权限提升这几类威胁()这份检查清单以首字母缩写 STRIDE 为人所知。让仪式感与实际需要相称:一小时的会议,配上白板、数据流图和滥用案例,就能捕获一份正式文档所能发现的大部分问题。记录下发现的威胁、选定的缓解措施,以及有意识接受的风险;这份记录既是第 4.6 章所需的设计阶段证据,也是第 4.2 章安全设计的起点地图。将它与重大设计变更挂钩,否则它就会退化成一份写过一次、再无人问津的文档。

采用安全编码标准与安全默认值

为你所使用的每种语言和框架,给工程师提供具体的安全编码标准:如何参数化查询、如何编码输出、如何校验输入、如何处理密钥、该调用哪个加密库、绝不能自行实现哪个加密库。将其与”安全默认”的构建块配对:共享库让安全的选择成为默认选择,让不安全的选择变得困难,这样工程师就能免费获得输出编码或参数化查询,而不必靠记忆。最好的标准是你的工具链能够强制执行的标准,这样违规行为会导致检查失败,而不是依赖评审者是否碰巧注意到。参照一份广为人知的弱点目录来梳理标准,确保覆盖真正导致数据泄露的那些类别,并剔除那些制造的噪音多于价值的规则。

在代码评审中明确纳入安全

代码评审(第 2.5 章)天然是一道安全关卡,因为由另一个人阅读变更,正适合发现缺失的授权检查或被信任的输入。要把安全维度明确化,而不是寄望于评审者自己记得:在评审模板中加入一份简短的安全检查清单,聚焦在输入处理、授权、密钥、加密和依赖变更这些高风险领域。将涉及身份验证或支付路径等敏感代码的变更,路由给具备安全深度的评审者,并对这些路径打上标记以实现自动路由。在人工评审之前先运行自动化检查,让评审者把注意力放在逻辑和设计意图(那些具有上下文和新颖性的部分)上,而不是工具已经能捕获的代码风格级别的发现。

在流水线的正确位置设置正确的自动化关卡

有几类安全工具应该嵌入到你的持续集成与交付流水线(第 8.1 章)中,了解每一类工具适合的位置,能避免你期望一种工具去做另一种工具的工作。静态应用安全测试(SAST)在不运行代码的情况下分析源代码,能在每次提交时捕获注入、不安全 API 使用等缺陷。软件成分分析(SCA)检查你的第三方和开源依赖项是否存在已知漏洞和许可证问题,它是第 2.18 章依赖与供应链管理在流水线中的延伸。密钥扫描(secret scanning)用于查找被意外提交的凭证、令牌和密钥,它既应该出现在提交环节(通过 pre-commit hook),也应该作为流水线中的最后一道防线。基础设施即代码(IaC)扫描会检查你的 Terraform、CloudFormation 或 Kubernetes 清单是否存在不安全配置,能在一个开放的存储桶还只是一份 diff 时就将其捕获。

动态应用安全测试(DAST)从外部对运行中的应用程序进行测试,就像攻击者探测端点一样,适合在稍后针对已部署的测试环境或预发布环境运行。交互式应用安全测试(IAST)在功能测试期间对运行中的应用程序进行插桩,从内部观察它,将静态洞察与动态覆盖相结合,并降低误报率。一条经验法则是:SAST、SCA、密钥扫描和 IaC 扫描把守构建关;DAST 和 IAST 验证运行中的系统。把每一项都调校到只在真正重要的问题上失败、在其余问题上仅作警告,因为一个”狼来了”的关卡,是团队会关闭的关卡。

在交付团队中嵌入安全倡导者

一个中央安全团队无法为数百名工程师的每一次变更做评审,而一个以遥远把关者姿态运作的安全职能,会变成团队绕开的瓶颈。安全倡导者(security champion)模式通过在每个交付团队中嵌入一名具备安全意识的工程师来解决这个问题:这不是一名全职专家,而是一名获得额外培训、拥有与中央团队直接沟通渠道、并被明确授予时间来在本地提升安全标准的开发者。安全倡导者主持威胁建模会议、为本团队的技术栈维护编码标准、分诊工具发现的问题,并把中央政策转化为本团队的实际做法,从而在不线性增加人头的情况下扩展中央团队的覆盖范围。要以实践社区、认可和真正的时间投入来投资这些倡导者,否则这个角色就会退化成组织架构图上的一个名字。

将项目锚定在一个成熟的框架上

你不需要从零发明一套生命周期,因为成熟的框架凝结了数十年的经验,并为审计人员提供了共同的语言。Microsoft 安全开发生命周期(SDL)是一个基于实践的模型,诞生于 Microsoft 自身的惨痛教训,为每个阶段规定了具体的活动。OWASP SAMM(软件保障成熟度模型)和 BSIMM(软件安全成熟度构建模型)是评估模型:SAMM 是规范性的,为你提供一个需要努力达到的成熟度目标;而 BSIMM 是描述性的,告诉你大量真实企业实际在做什么,从而让你进行对标。NIST 安全软件开发框架(SSDF),以特别出版物 800-218 的形式发布,是一套简洁的、以结果为导向的实践,正日益成为美国政府软件供应链要求的基础。选择其中一个作为你的骨干,而不要把四者混合在一起徒增混乱。框架是地图,不是疆域本身:采纳适合你风险状况的实践,并记录你实施了哪些,因为这份记录正是第 4.6 章和第 10.2 章(风险、审计与保障)所需要的。

把安全纳入完成定义,并按 SLA 运行修复流程

一道关卡只有成为”完成”含义的一部分,才能真正守住。扩展你团队的完成定义,使一项变更在其安全条件被满足之前不能算完成:没有未处理的高危扫描发现、若设计发生变化则威胁模型已更新、密钥得到妥善管理、依赖项不存在已知的严重漏洞。这让安全成为一项常规的验收标准,而不是一次特殊事件。对于那些逃逸到生产环境中的发现,运行一套带有明确修复服务水平协议(SLA)的漏洞管理流程:一个按严重程度设定的最长修复时间,使严重缺陷以天为单位衡量,而低严重度缺陷则在一个更长、但仍受跟踪的窗口内衡量。按真实风险排定优先级,将严重度评分与可利用性和暴露面结合起来,这样你就会先修复那个面向互联网、可被利用的缺陷,而不是那个藏在三层防火墙后面的理论缺陷。在一个系统中跟踪每一项发现直至关闭,并像对待任何其他运营指标一样报告其滞留时长。一个无人衡量的 SLA,只是一个愿望。

端到端保护供应链完整性

攻击者越来越多地不再针对你的代码本身,而是针对代码所经过的路径:一个被攻破的依赖项、一个被投毒的构建步骤、一件在传输中被替换的未签名制品。这就是供应链攻击,防御它涉及多个阶段。生成软件物料清单(SBOM),这样你才能确切知道每个发布版本中包含什么。锁定并验证依赖项,并通过受控的内部注册表拉取它们,而不是直接从公共互联网获取。强化构建系统本身,因为一台拥有广泛权限的构建服务器是一个高价值目标;生成带有来源信息(provenance)的、经过签名、可验证的制品,让使用者能够确认自己运行的正是你构建的东西。这些接触点分别与第 2.18 章的依赖纪律和第 10.2 章的保障义务相连接。把你的构建和发布流水线当作生产基础设施来对待,因为那里一旦被攻破,会同时波及下游的一切。

度量项目并将结果反馈回去

你会改进你所度量的东西。跟踪那些能告诉你生命周期是否有效的先行指标:重大变更的威胁模型覆盖率、启用了预期关卡的流水线百分比、按严重程度划分的平均修复时间、逃逸缺陷率(本应被关卡捕获却出现在生产环境中的缺陷),以及能预示团队是否还信任某个工具的误报率。把结果反馈回去:逃逸的缺陷用于调校你的关卡,嘈杂的工具需要调校或替换,反复出现的缺陷类别应推动新的安全默认设置和培训。一个没有度量的生命周期,会逐渐漂移成一种仪式。

权衡:优点与缺点

方法优点缺点
流水线中的左移关卡修复成本最低;反馈快速、持续若不加调校,会导致工具泛滥和警报疲劳
设计阶段的威胁建模关卡在成本尚低时捕获设计缺陷需要技能和时间;若不持续更新会逐渐失效
嵌入团队的安全倡导者扩展安全能力;具备本地主人翁意识和上下文若资源不足或得不到认可,作用会被稀释
中央安全把关标准一致;责任清晰会成为团队绕开的瓶颈
框架锚定的项目(SDL、SAMM、SSDF)经过验证的实践;具备可供审计的通用语言存在流于形式的风险;照搬而不加判断
严格的修复 SLA有界的风险敞口;可衡量的问责若严重度评分不准,会被钻空子、走过场
阻断式关卡(使构建失败)强有力地保证不良代码不会上线因误报而中断交付;带来绕过关卡的压力

核心张力在于严谨与流畅之间。往流水线里投入太少,缺陷就会逃逸到代价高昂的地方;投入太多又不加调校,你要么在噪音中阻断交付,要么训练团队习惯性地点掉警告,直到这些关卡变得毫无意义。解决之道是无情地调校,并让每道关卡的强度与它所守护的风险相匹配:对泄露的凭证或已知的严重漏洞阻断构建,但对低严重度的代码风格问题只作警告。当团队感受到的摩擦与危险程度成正比时,安全路径就会一直是阻力最小的路径。

与团队讨论的问题

  1. 在我们今天的交付流程中,安全究竟发生在哪些阶段,又在哪些阶段只是假装发生? 大多数组织发现,自己真正的安全投入都集中在最后阶段()一次发布前扫描或一年一度的渗透测试,而需求、设计和评审阶段对安全的提及只停留在愿望层面。老老实实地逐阶段梳理你当前的流程,标记出哪些地方的安全活动有真正的负责人和真正的关卡,哪些地方只是一句口号。拿一个最近的功能,追溯从它第一条需求到部署的过程中,真正发生了哪些安全工作。你发现的缺口就是你的左移待办事项,而那些全是口号、没有关卡的阶段,正是缺陷悄悄流入你产品的地方。

  2. 当扫描工具报告一个发现时,接下来会发生什么,我们能证明吗? 本章中每一道关卡的价值,都取决于发现出现之后的工作流。走一遍真实的例子:一个 SAST 或 SCA 工具标记出了某个问题,然后谁会被通知,严重程度和可利用性如何评估,SLA 是什么,在哪里被跟踪,以及你如何知道它是被真正修复了而不是被搁置了?许多团队拥有令人印象深刻的工具,却对这些问题没有答案,这意味着他们的发现堆积成了一个大家都已学会忽视的队列。如果你无法拿出上个季度的发现清单及其按严重程度划分的修复时长,那么你拥有的只是一种扫描习惯,而不是一套漏洞管理项目,而这个区别,正是审计人员和攻击者都会去探查的地方。

  3. 哪一个框架是我们项目的唯一锚点,每个团队都能说清”安全的完成”对自己的工作意味着什么吗? 没有共同的骨干,每个团队都会即兴发挥自己对”足够安全”的定义,组织真实的安全态势就变成了上百个私人判断的平均值。共同决定以哪个成熟框架(Microsoft SDL、OWASP SAMM、BSIMM 或 NIST SSDF)作为参照,然后检查这个选择是否真正落地:一个交付团队能否说出他们完成定义中的安全条件,并指出执行每一条的关卡在哪里?比较三个不同团队的完成定义。趋同意味着生命周期是真实的;分歧则意味着你在幻灯片上有一个框架,在流水线中却各自为政。

  4. 哪些发现会阻断构建,哪些只会发出警告,这条界线是谁划定的? 每道关卡的强度都是一项政策选择,无论朝哪个方向出错,代价都不小:噪音上阻断,团队就会在交付压力下学会绕过或关闭关卡;一切都只警告,严重问题就会悄然溜过、无人阅读。对于大型组织,危险在于漂移()每个团队悄悄地重新调整自己的阈值,直到”流水线是绿的”在每个组内的含义都不一样,而你在中心位置根本无从知晓真实的风险敞口。拿出每个工具当前的通过或失败策略、团队实际经历的误报率,以及一个最近被覆盖(override)的发现及其原因。在企业和政府环境中,还要说明谁有权接受某项风险,以及这项接受被记录在哪里,因为一个没有指定责任人、没有书面理由的未阻断严重问题,正是审计人员会指出、攻击者会发现的那个缺口。

  5. 我们的安全倡导者是一项真实的能力,还是组织架构图上的一个名字,保持其真实所要付出的诚实成本是多少? 倡导者模式是一个小型中央团队触达数百名工程师的方式,但它会悄无声息地失效:角色被指派了,却没有保留时间,没有培训到位,一个季度之后就成了一个无人践行的头衔。与之竞争的力量始终是交付压力,因为倡导者的安全时间往往是截止日期临近时第一个被牺牲的东西,所以问题在于领导层是否真正为这段时间划出了保护圈,还是仅仅是一厢情愿。拿出已指定倡导者的名单、他们上个季度实际投入安全工作的小时数、他们获得的培训和社区支持,以及他们主持过的威胁建模会议。对于一个分布在许多团队和长期存续系统中的大型企业或政府机构而言,这项能力正是让生命周期在审计之间保持存活的关键,因此,若资源投入不足,应将其视为一项任由项目衰败的决定,而不是一次疏忽。

  6. 如果今天下午一个被广泛使用的依赖项出现了严重漏洞,我们能多快找到每一个受影响的服务并证明自己已经修复? 供应链风险敞口是那种把一个上游缺陷变成整个组织事件的失效模式,而答案完全取决于你之前是否打下了相应的基础:每个发布版本的软件物料清单(SBOM)、通过受控内部注册表拉取的、经过锁定和验证的依赖项,以及一个经过强化的构建系统。这里的张力在于投入与速度之间,因为生成和查询 SBOM、把每一个依赖项都路由经过注册表,都会增加摩擦,团队会为此心生不满,直到有一天它救了他们。拿出你当前的依赖项清单、你是否能按包名和版本跨所有服务进行查询、你的构建系统权限现状,以及你上一次演练快速打补丁是什么时候。对于负有法定报告义务和合同修复 SLA 的企业和政府机构而言,能否在几分钟而不是几周内回答”我们哪些系统包含这个组件”,正是可控披露与你从新闻中得知的数据泄露之间的分界线。

分行业视角

初创公司。 没有安全团队,跑道也有限,把生命周期建入工具而不是建入人头:在每个拉取请求(pull request)上运行 SAST、软件成分分析和密钥扫描,只对高严重度发现使构建失败,让这些关卡保持可信。指定一名工程师担任安全倡导者,对任何涉及资金或个人数据的功能运行一次三十分钟的威胁建模会议。目前跳过繁重的文档和正式框架,但要保留流水线日志,因为一旦你的第一个企业客户问起 SOC 2,它们就会成为你的审计证据。

小型企业。 你没有安全专家,预算也很紧张,所以要依靠购买而非自建的安全默认设置:一个能为你运行依赖和密钥扫描的托管代码仓库、一个开启了关卡的托管流水线,以及默认设置合理的框架。采用一份借来的简短安全编码标准,而不是自己动手写;从一个成熟框架中挑选一套轻量级实践,而不是自创一套生命周期。优先选择那些开箱即用就能对真实风险使构建失败的工具,这样即便没有专人每天照看,安全也能守住。

企业。 在跨多团队的规模下,问题在于一致性和证据:锚定在一个框架上,把流水线关卡标准化,在每个交付团队中嵌入一名经过培训、与中央产品安全团队相连的安全倡导者。集中跟踪修复 SLA,并向风险委员会报告滞留情况;把威胁模型和扫描结果作为审计证据存档;让依赖项经过一个每个发布版本都会生成 SBOM 的内部注册表。目标是让一名在业务部门之间调动的工程师,无论去到哪里都遇到相同的关卡,任何发布版本都能从需求一路追溯到生产环境。

政府。 采购规则、透明义务和公众问责塑造着每一项选择,一套文档化的生命周期往往是运营的法定前提。对齐到一个公认的框架,例如 NIST SSDF,以及与你的运营授权(authorization-to-operate)要求相对应的相关控制目录(例如 NIST 800-53);让威胁建模成为强制性的、并由一个独立的保障职能进行评审;执行完整的一整套扫描关卡,并要求带有签名和来源信息的制品。把修复 SLA 写入合同,保留发现与修复的不可篡改记录,因为公务员要接手这些系统长达数十年,而审计轨迹正是让新团队能够安全地运营这些系统并向公众负责的依据。

示例

初创公司。 一家二十人规模的金融科技初创公司无法配备安全团队,于是把生命周期建入了工具和习惯。每个拉取请求都会运行 SAST、SCA 和密钥扫描,只在高严重度发现时使构建失败,从而保持关卡的可信度。一名工程师自愿担任安全倡导者,为任何涉及资金或个人数据的功能运行一次三十分钟的威胁建模会议,并维护一份一页纸的安全编码标准。完成定义中包含”没有未处理的严重发现”和”密钥存放在保险库中,而不是代码里”。当他们后来争取第一个企业客户并接受 SOC 2 审计时,流水线日志和修复跟踪器已经是他们所需要的证据。

企业。 一家拥有数千名工程师的全球性银行,将其项目锚定在 NIST SSDF 上,用 OWASP SAMM 衡量成熟度,并用 BSIMM 与同行进行对标。每个交付团队都有一名经过培训、与中央产品安全团队相连的安全倡导者。威胁建模是任何跨越信任边界的变更的必需关卡,其产出作为审计证据存档。流水线在构建阶段强制执行 SAST、SCA、IaC 扫描和密钥扫描,对预发布环境执行 DAST,依赖项只通过一个每个发布版本都会生成 SBOM 的内部注册表流转。修复 SLA 被集中跟踪并向风险委员会报告,因此一名在业务部门之间调动的工程师会遇到相同的关卡,审计人员也能将任何发布版本从需求追溯到生产环境。

政府。 一个国家税务机关在法定安全义务下运营,不能发布未经过既定生命周期的软件。它将自身实践对齐到 NIST SSDF 和 NIST 800-53 控制项,这些控制项对应着其运营授权要求。每一项面向公民的服务都要编写安全需求和滥用案例,威胁模型是强制性的,并由一个独立的保障职能(第 10.2 章)进行评审,每条流水线都强制执行完整的一整套扫描关卡,并要求带有签名和来源信息的制品。修复 SLA 是合同性的,发现与修复的不可篡改记录支撑着授权持续运营的审计。由于公务员要接手这些系统长达数十年,这套文档化的生命周期让一个新团队能够在其原作者早已离开之后,长期安全地维护一项服务。

商业论证:动机、投资回报率与总拥有成本

安全生命周期的回报,等于它所防止的数据泄露、事件和紧急返工的成本,减去构建这些关卡所需的、大部分为一次性的适度成本。经济账的方向始终一致:缺陷发现得越早,成本就越低。一个在威胁模型中被捕获的设计缺陷,只是一次白板讨论;同样的缺陷若在生产环境中被捕获,就会伴随披露、修复、监管风险敞口和声誉损害。由于自动化关卡是可复用的基础设施,其成本一次性支付,并摊销到未来的每一次变更中,而它们所防止的每一次事件,其代价原本都会远高于整套项目本身。

总拥有成本的大头不是工具许可费,而是调校和工作流。一个未经调校、向团队泛滥误报的生命周期会浪费工程注意力,滋生被忽视的警报,最终导致关卡被关闭()这比没有项目更糟,因为它制造了一种虚假的安全感。要为人的一面预留预算:倡导者的时间、分诊工作流,以及持续的调校。要向领导层证明其价值,把生命周期与他们已经在跟踪的指标联系起来:逃逸缺陷率、平均修复时间、审计发现,以及后期安全意外带来的周期时间成本。在受监管和政府环境中,一套文档化、被强制执行的生命周期往往是运营的前提条件,这就把安全从一个成本中心转变为一张经营许可证。

反模式与陷阱

  • 末端的安全表演: 用一次发布前扫描或年度渗透测试来代替整个生命周期,导致缺陷在代价最高时才被发现。
  • 没有工作流的工具泛滥: 购买了 SAST、DAST 和 SCA,却没有负责人、SLA 或分诊流程,导致发现堆积成一个人人忽视的队列。
  • 未经调校的关卡导致警报疲劳: 噪音很大的工具会标记一切,训练工程师习惯性点掉警告,直到关卡失去意义。
  • 把关者瓶颈: 一个必须批准每一次变更的中央团队,变成了团队绕开或怨恨的队列。
  • 有名无实的倡导者: 角色被指派了,却没有培训、时间或认可,最终退化成一个空头衔。
  • 只建一次的威胁模型: 一份在启动会上写就、随后设计不断变化却再未被更新的文档。
  • 照搬框架: 把 SDL 或 SSDF 的活动当作仪式来采纳,而不去适配真实风险,也不检查它们是否真正改变了结果。
  • 失控的供应链: 直接从公共互联网拉取未锁定、未验证的依赖项,没有 SBOM,构建系统权限过高。
  • 纸面上的 SLA: 无人衡量的修复截止日期,导致严重问题悄悄过期而不被察觉。

成熟度模型

  • 第 1 级,启动(Initiate): 安全是后期附加的,且大多是被动响应式的。测试基本只在发布前才发生(如果有的话),没有威胁建模,扫描是手动的或根本不存在,发现的处理是临时性的,供应链无人管理。一项功能是否安全,完全取决于是谁写的。
  • 第 2 级,发展(Develop): 基础实践开始出现,但落地并不均衡。一些流水线运行了 SAST 或 SCA 和密钥扫描,代码评审会提及安全,严重发现会被修复,但覆盖面参差不齐,威胁建模很少见,修复缺乏被跟踪的 SLA,每个团队都自行摸索自己的方法,各团队之间的标准差异很大。
  • 第 3 级,标准化(Standardize): 一套锚定在成熟框架上的、文档化的生命周期在全组织范围内被强制执行。安全需求和滥用案例、威胁建模关卡、安全编码标准、完整的一整套流水线关卡、完成定义中的安全内容、被跟踪的修复 SLA、安全倡导者,以及供应链控制,在各团队之间都是标准做法,因此一名工程师无论在哪工作,都会遇到相同的期望。
  • 第 4 级,管理(Manage): 项目被度量,并对照基线加以控制。先行指标被跟踪和报告:重大变更的威胁模型覆盖率、启用了预期关卡的流水线百分比、按严重程度划分的对照 SLA 的平均修复时间、逃逸缺陷率,以及每个工具的误报率。设定目标,偏差会触发行动,是否发布的决定基于证据而非意见,这样领导层就能看到生命周期是否真正守住了阵地,而不是想当然地假设它守住了。
  • 第 5 级,编排(Orchestrate): 项目持续改进和适应,并在整个组织范围内实现整合。逃逸的缺陷用于调校关卡,嘈杂的工具被剔除,反复出现的缺陷类别推动新的安全默认设置和培训,倡导者形成一个活跃的实践社区,供应链来源信息被端到端验证,安全规划被编织进交付和风险管理之中,使生命周期能够随着威胁态势和业务的变化而自我再平衡。

讨论思路

  1. 你的生命周期中,今天哪个阶段最薄弱,要在那里增加一道真正的关卡而不是一句口号,需要付出什么?
  2. 如果今天下午披露了一个依赖项的严重漏洞,需要多长时间才能让每个受影响的服务都完成修补,你又是如何知道的?
  3. 阻断构建的关卡与仅作警告的关卡之间的界线在哪里,又是谁决定哪些发现属于哪一侧?
  4. 你的安全倡导者是否获得了真正的时间和认可,还是这个角色只是一个正在悄悄衰败的头衔?
  5. 你能否为审计人员拿出上个月发布版本的威胁模型和扫描结果?
  6. 如果下个冲刺开始跟踪某一个指标,哪一个最能改变团队在安全方面的实际行为?

关键要点

  • 安全软件开发生命周期在每个阶段都构建并验证安全性,把缺陷左移到成本最低的地方,它是连接应用安全(第 4.2 章)与安全运营(第 4.4 章)的流程主线。
  • 让每个阶段都有负责人和关卡:安全需求和滥用案例、设计阶段的威胁建模关卡、安全编码标准、代码评审中的安全,以及完成定义中的安全。
  • 把每种自动化工具放在合适的位置:SAST、SCA、密钥扫描和 IaC 扫描把守构建关,DAST 和 IAST 验证运行中的系统,并调校每一道关卡,使其只在真正重要的问题上失败、不会”狼来了”。
  • 通过嵌入团队的安全倡导者来扩展项目规模,把它锚定在一个成熟框架上(Microsoft SDL、OWASP SAMM、BSIMM 或 NIST SSDF),并按明确的、可衡量的 SLA 运行修复流程。
  • 用 SBOM、经过验证的依赖项和一个经过强化的构建系统来端到端保护供应链,并度量整个项目,使其持续改进,而不是退化为一种仪式。

参考资料与延伸阅读

  • Michael Howard and Steve Lipner, The Security Development Lifecycle
  • Adam Shostack, Threat Modeling: Designing for Security
  • Gary McGraw, Software Security: Building Security In
  • National Institute of Standards and Technology, Secure Software Development Framework (SSDF), Special Publication 800-218
  • OWASP Foundation, Software Assurance Maturity Model (SAMM)
  • Synopsys, Building Security In Maturity Model (BSIMM)
  • OWASP Foundation, OWASP Application Security Verification Standard (ASVS)
  • Laura Bell, Michael Brunton-Spall, Rich Smith, and Jim Bird, Agile Application Security