网络安全反模式:忙碌工作生成器剖析与解决方案

本文探讨网络安全中常见的“忙碌工作生成器”反模式,通过案例分析揭示其成因,并提出基于根因分析和机制设计的解决方案,帮助团队构建可扩展的安全体系。

网络安全反模式:忙碌工作生成器

Apr 19, 2025 · 3025 words · 15 minute read

本文最初发表于Open Government Products的Substack。提醒:我正在与No Starch Press合作出版新书!《从零日到零日》是为想要进入漏洞研究领域的新手编写的实践指南。

优秀设计和平台工具的目标是让弹性和安全成为背景信息而非前台焦点。在理想世界中,安全是隐形的——开发者甚至不会意识到后台正在发生的安全事务。他们的工作流程不会感到更加繁琐。 Kelly Shortridge, 《安全混沌工程》

这是关于在Open Government Products构建网络安全解决方案第一原则系列博客的第一篇。来自攻击性安全背景,我发现最具有弹性的网络安全解决方案直接解决根本原因,从而产生高度可扩展的解决方案,减少组织的整体工作量。

然而,许多安全计划陷入一个常见陷阱:有前景的安全解决方案最终产生越来越多的工作,逐渐消耗大部分资源和注意力。最终,这分散了解决更重要安全问题的精力,因为团队忙于发送提醒、填写(数字)文书和处理豁免请求。

让我们看一个并非完全虚构的例子:

John Doe是NotBeyondCorp的一位有进取心的网络安全专家。在一系列由简单注入漏洞引发的安全事件后,他建议购买代码扫描解决方案来识别公司代码库中的这些漏洞。在初步试验中,他评估发现并找到了50多个可利用漏洞!受到结果的鼓舞,公司批准了该解决方案,John的任务是在整个组织中实施它。

兴奋的John开始推出该解决方案。尽管有一些小问题(比如破坏了一些CI/CD工作流程),他完成了推出。然而,他很快意识到很少有开发者响应漏洞发现。他们太忙了无法进行分类,或者不知道这个解决方案。

John尝试了一些生活质量改进,如调整检测规则和向开发者发送自动提醒。这些改进略微提高了参与度,但效果是短暂的。无论如何,不可能达到100%的真阳性率。发现的数量不断堆积,修复时间指标亮起红灯。John感到沮丧:开发者不知道漏洞很重要吗?

无计可施,John接近管理层按下大红按钮:强制对这些发现进行分类和修复时间表。被John清晰解释真阳性被利用可能出错的说明说服,管理层同意了。为了执行授权,John获得了几个人头来跟踪漏洞发现。

随着未解决的漏洞警报数量达到数千,John意识到他需要一个适当的工作流程来管理它们。他的团队创建了一个Jira工作流程来自动ping和提醒开发者(如果他们没有及时响应,还会提醒他们的经理)。

然而,并非一切都能整齐解决——有争议的发现、豁免请求和其他查询开始涌入,需要团队手动响应。有时,特别好斗的团队将问题升级到管理层,需要更多会议来解决冲突。团队为常见问题(如豁免请求)创建了更多Jira工作流程,但最终仍然需要人工批准。

John为授权辩护,但越来越失望。他倡议早期采摘的低 hanging fruit 已经解决,所以误报的比例也在增加。此时,大部分投入该倡议的工时涉及分类误报或处理官僚内斗。他想花更多时间改进解决方案或构建自己的内部集成,但总有另一个会议……

这是许多安全计划中常见的场景。虽然最初的意图是好的,结果一开始看起来有希望,但实际的投资回报随着时间的推移大大减少,特别是在考虑维护合规机构的机会成本时。

这是第一个网络安全反模式:忙碌工作生成器。

什么创造了忙碌工作生成器? 🔗

为了避免这种反模式,重要的是理解为什么这种反模式发生,以及导致它发生的偏见。

浅层因果关系 🔗

本质上,John的倡议旨在解决可利用漏洞。这是关键的但容易忽视。考虑到这一点,任何花在不可利用漏洞上的时间都是浪费时间和资源,偏离了这个目标。James Berthoty在一篇博客文章和附带的图表中简洁地解决了这个问题:

在我保护云原生应用程序的职业生涯中,我从未见过在我的SaaS应用程序上下文中容器图像的可利用CVE的证据。尽管有数千次漏洞扫描,数百万发现的漏洞,以及无数开发者小时修复这些东西。不幸的事实是,惊人的误报数量对安全在其组织中的合法性造成了真正的损害。

来源:James Berthoty, Latio Pulse

许多“左移”忙碌工作生成器的问题在于,很多成本转移给了开发者,这在评估计划有效性时没有准确捕捉。通常,运营的总成本要高得多。

部分动机是偏向于责怪人为错误——在这种情况下,“可利用漏洞发生是因为人类将它们引入代码库。”因此,负责的人类应该分类和修复识别的漏洞。表面上,这确实是真的。但表面级别的评估对组织不利。保持好奇并抓住机会实践五个为什么很重要:

  • 为什么注入漏洞发生?因为不受信任的输入直接用于构建数据库查询。
  • 为什么不受信任的输入被允许进入查询逻辑?因为开发者编写了原始SQL而没有使用参数化查询等安全抽象。
  • 为什么开发者能够在代码库中使用原始SQL?因为开发环境没有限制或控制数据库访问的实现方式。
  • 为什么不限制对不安全API或模式的访问?因为没有架构控制,如默认安全库或框架,消除不安全模式的使用。
  • 为什么系统没有设计为强制执行安全模式并禁止不安全模式?因为应用程序堆栈不包括使安全方式变得容易(或唯一)的护栏,如安全强化的ORM,或禁止原始查询构建的框架。

通过更深入的分析,你可以识别出许多比“又一个警报生成器”更多的潜在解决方案,给你比最低共同分母解决方案更多的选择,这些解决方案扩展性不好。

错误的解决方案优先级 🔗

明确地说,这并不意味着警报生成器是错误的解决方案——情况可能决定首先从更容易的解决方案开始。有一个完整的安全操作领域致力于检测和响应(将在未来的帖子中讨论)。

获得对漏洞数量的初步可见性是重要的第一步。然而,在应用程序安全中,你应该根据解决方案解决根本原因的能力来优先排序。为此,我喜欢参考Kelly Shortridge的安全解决方案冰淇淋锥层次结构:

来源:Kelly Shortridge, 对CISA信息请求的响应

虽然该框架源自许多行业使用的成熟控制层次结构,但不幸的是在网络安全领域被忽视。简而言之,冰淇淋锥底部的较弱解决方案越来越依赖人类干预才能成功,而接近顶部的解决方案通过设计消除危险。

任何依赖人类干预的解决方案都内置了误差边际——毕竟,我们只是人类——然后需要额外的干预来解决。回想一下John必须做的Jira工作流程、电子邮件提醒和会议,只是为了确保开发者修补了他们的错误。这就是忙碌工作的来源,也是为什么他的解决方案注定会产生越来越多的忙碌工作。更糟的是,这意味着消除可利用漏洞的实际结果永远无法达到100%——总会有另一个未分类或错误驳回的警报。

缺乏机制 🔗

此时,你可能会有一个常见且非常合理的反对意见——不是每个人都有Google的安全团队,有能力构建和推出整个Web框架。

如果Google开源他们的高保证Web框架就好了,但它可能过于优化Google的内部工具链和模式。一些组织可能有高度异构的内部环境,排除了单一框架。

这是选择像警报生成器这样的最低共同分母解决方案的一个重要因素。它在大多数缺乏组织范围机制来影响变化的安全团队的控制范围内。

如何避免忙碌工作生成器? 🔗

既然你理解了导致忙碌工作生成器的原因,让我们探索如何避免这些原因并探索替代方案。关键是从这些第一原则开始:

保持清洁不同于保持清洁 🔗

在DevSecOps领域有一个常见的方法“获取清洁,并保持清洁”。“获取清洁”指的是解决所有未决的发现,而“保持清洁”指的是阻止新的发现发生。这里的关键原则是这些是两种独立的策略。更关键的是,“获取清洁”是一个临时的第一步,而“保持清洁”是长期解决方案。

把它想象成从沉船中舀水。理论上,你可以添加更多的人来舀水,但在某个时候,你需要修补越来越大的洞。一个常见的错误是相信你需要完全清洁才能保持清洁。这是一个不可能的任务;总会有新的问题涌入。

相反,获取足够清洁,在那里你已经解决了真正可利用的高和关键问题,然后转向保持清洁。John的错误是过度专注于获取清洁,没有设定一个时间点开始投资保持清洁。

有多种方法可以设定一个合理的“足够清洁”点——可利用性或可达性、严重性、CVEs的KEV/EPSS。只需设定那个点并承诺它。在OGP,我们利用Dependabot自动分类规则,编写了我们自己的CodeQL规则集,并投入了几个周期来处理(优先排序的)漏洞积压,然后为高严重性漏洞设置警报。这给了我们空间探索“保持清洁”解决方案,同时为重要漏洞保持合理的基线。

一个真正可扩展的代码漏洞解决方案看起来更像Google的高保证Web框架蓝图,其中Google的安全工程团队内置了多个安全控制。他们的分享听起来很像Shortridge在本帖开头的引用:

值得强调:此列表中的所有内容都由安全团队维护。使用我们的高保证Web框架,应用程序所有者不需要意识到任何这些功能——它们都启用并开箱即用。

在OGP,我们构建了一个Starter Kit,其中安全工程团队内置了安全组件(Starter Kitty)来解决一些常见的重复发现。虽然它们在第一次尝试中没有完美工作,但我们随着时间的推移通过跨多个产品的连续渗透测试逐渐强化它们,确保一个产品团队学到的教训被编码并传输给所有其他产品,使我们能够明显消除一类Web漏洞。

良好意图不起作用,机制才起作用 🔗

任何依赖人类干预的解决方案都带有内置的失败率。虽然开发者培训很重要,但它不是万无一失的——或者攻击者可能发现新的攻击向量。理解这一原则帮助你远离警报生成器或NagOps,并看向构建真实机制。

即使你缺乏现有机制,从合规到安全工程的演变部分涉及强大的跨学科工作——特别是与工具、平台、devops和其他拥有高杠杆机制的工程团队合作。安全在孤岛中作为首席阻塞器操作是最容易和最差的操作模式。

在OGP,安全工程团队没有构建Starter Kit——工具团队做了。通过与他们合作将Starter Kitty组件引入Starter Kit,我们还解决了开发者体验和可用性的重要问题,工程师更敏感(观察此线程),改进了我们的最终实现。

回想John最初在推出时如何破坏开发者的CI/CD管道。安全工程有时可能缺乏其他工程团队的上下文,这就是为什么紧密合作对于避免“大红按钮”很重要。授权也是一种机制,但它们肯定不是最有效的。

简而言之,缺乏正确的机制不是使用错误解决方案的借口——构建或找到那些机制。我将在第二篇关于构建有效机制的博客文章中更多讨论这一点。

案例研究:tj-actions供应链攻击 🔗

让我们通过使用tj-actions供应链攻击作为案例研究来 walk through 如何应用这些原则。借助一些ChatGPT协助(有指导避免警报生成器),你可以快速映射五个为什么:

  • 为什么我们的仓库受到tj-actions妥协的影响?因为开发者使用了被妥协的第三方GitHub Action,并通过可变标签(@v35, @v1)自动拉取其最新版本。
  • 为什么我们的工作流程自动更新到恶意版本?因为GitHub Actions默认使用可变标签,我们允许在生产工作流程中使用未固定的版本。没有强制执行防止它。
  • 为什么我们没有阻止未固定的第三方操作运行?因为我们没有将GitHub Actions视为生产依赖。我们的安全态势将应用程序依赖(例如,npm, pip)视为不可变和扫描的——但CI/CD依赖完全信任且未经审计。
  • 为什么GitHub Actions与应用程序依赖不同对待?因为我们缺乏一个框架或工具,让我们像代码依赖一样管理GitHub Actions。没有锁文件,没有审查过程,没有仓库到操作的信任模型——只是即插即用。
  • 为什么我们没有那个框架?因为GitHub不提供开箱即用,并且没有对锁文件、中央批准工作流程或操作依赖代理的第一方支持。我们已经接受该风险为正常而不是解决它。

从这里,你可以生成一些可能的解决方案:

  • 检测和警报非哈希固定的GitHub Actions。
  • 在运行时或静态扫描、检测和警报恶意GitHub Actions。
  • 使用GitHub策略强制执行哈希固定的GitHub Actions。
  • 只允许批准的、白名单的GitHub Actions。

我相信你自己可以想到更多——供应商通常很乐意建议一些他们自己的产品来响应此类事件。但哪些解决方案解决根本原因,它们会落在安全解决方案冰淇淋锥层次结构的哪里?

忙碌工作生成器解决方案 一个负面的忙碌工作生成器结果可能是“检测和警报”解决方案,甚至是白名单;这通过警告系统和管理控制将更多人类放入循环。

根本原因解决方案 然而,在这种情况下,GitHub确实为企业范围的GitHub Actions策略提供了一个清晰的机制。这必然是一个破坏性变化,因为开发者不能再使用不受信任的操作。正如Google关于高保证Web框架的博客文章分享:

其中一些功能确实约束了应用程序所有者编写的代码(例如,它们会阻止编写代码如document.body.innerHTML = "foo"),但这是我们“安全编码”安全方法的有意部分……

“使安全方式变得容易(或唯一)是可能的,但安全团队需要完全拥有推出以减少意外中断并确保可维护性。”

我们做了什么 GitHub策略功能只有一个问题:GitHub只允许将操作限制为内部操作、已验证市场创建者拥有的操作、官方GitHub操作和白名单的操作。然而,这可以解决强制哈希固定依赖的实际根本原因。

幸运的是,我们能够使用白名单中的模式为哈希固定操作创建一个变通方法。虽然不完美,但这足够了,直到GitHub实际在策略中添加哈希固定操作作为选项。我们还决定允许市场验证的创建者和官方GitHub操作,以减少实施策略时失败操作的数量,减少开发者摩擦。

更重要的是,我们与工程师合作,构建了一个自定义GitHub应用程序,识别并通知开发者所有不受信任的操作,这些操作在策略实施后会失败——你可以在这里看到FormSG的示例。这减少了当我们切换开关时的不愉快惊喜。

最后,我们为GitHub Actions中断指标和日志创建了监视器,以警告我们新策略是否导致失败激增,确保我们可以在需要时及时回滚。

根据你组织的上下文,你可能会得出不同的解决方案,但你可以看到这种方法如何满足两个原则:

  • 获取清洁不同于保持清洁:我们投入了一些时间来识别不受信任的操作并提醒开发者,但这为自动执行的GitHub策略铺平了道路,防止更多不受信任的操作。我们没有停滞在检测和警报阶段。
  • 良好意图不起作用,机制才起作用:使用哈希固定或受信任的操作可能很繁琐。当不知情的开发者使用失败的不受信任操作时,它会引入意外元素。合理假设唠叨开发者使用受信任/哈希固定的操作不会起作用。相反,我们使受信任的操作成为唯一路径,但我们通过准备工作和更友好的策略选择使其尽可能容易。

结论 🔗

在这篇博客文章中,我解释了一些网络安全解决方案设计的第一原则,希望说明好解决方案与坏解决方案的区别。你的安全计划可能并不总是有此刻的能力或机制,但培养好品味很重要:

“没有人告诉初学者这个,我希望有人告诉我。所有做创造性工作的人,我们进入它是因为我们有好的品味。但有一个差距。在头几年你制作东西,它只是不那么好。……你的品味是你的工作让你失望的原因。……这需要一段时间。花一段时间是正常的。你只需要战斗通过。” Ira Glass

在我的下一篇文章中,我将讨论我们在OGP学到的关于构建有效机制以支持强大安全解决方案的一些教训。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计