网络安全反模式:忙碌工作生成器及其解决方案

本文探讨了网络安全中常见的反模式——忙碌工作生成器,分析了其成因如浅层因果分析和错误解决方案优先级,并提出了避免策略如区分清理与保持清洁、依赖机制而非善意,通过实际案例展示了如何应用这些原则。

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

Apr 19, 2025 · 3025 words · 15 minute read

本文最初发表于 Open Government Products 的 Substack。提醒:我正在与 No Starch Press 合作出版新书!我为希望进入漏洞研究领域的新手撰写了《From Day Zero to Zero Day》。

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

这是关于在 Open Government Products 构建网络安全解决方案的第一原则系列博客文章的第一篇。来自攻击性安全背景,我发现最弹性的网络安全解决方案直接解决根本原因,从而产生高度可扩展的解决方案,减少组织的整体工作。 然而,许多安全计划陷入一个常见陷阱:有前景的安全解决方案最终创造越来越多的工作,最终消耗大部分资源和注意力。最终,这分散了解决更重要安全问题的精力,因为团队忙于发送提醒、填写(数字)文书和处理豁免请求。 让我们看一个非虚构的例子:

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

兴奋的 John 开始推出该解决方案。尽管有一些小问题(如破坏一些 CI/CD 工作流程),他完成了推出。然而,他很快意识到很少有开发者响应漏洞发现。他们太忙了无法进行分类,或者不知道该解决方案。 John 尝试了一些生活质量改进,如调整检测规则和向开发者发送自动提醒。这些改进略微提高了参与度,但效果是短暂的。无论如何,不可能达到 100% 的真阳性率。发现的数量不断堆积,修复时间指标亮起红灯。John 感到沮丧:开发者不知道漏洞很重要吗? 没有想法了,John 接近管理层按下大红按钮:强制对这些发现进行分类和修复时间表。被 John 清晰解释如果真阳性被利用可能出什么问题的说服,管理层同意了。为了强制执行授权,John 获得了几个人头来跟踪漏洞发现。 随着未解决的漏洞警报数量达到数千,John 意识到他需要一个适当的工作流程来管理它们。他的团队创建了一个 Jira 工作流程来自动 ping 和提醒开发者(如果他们没有及时响应,还会提醒他们的经理)。 然而,并非一切都能 neatly 解决——有争议的发现、豁免请求和其他查询开始涌入,需要团队手动响应。有时,特别好斗的团队将问题升级到管理层,需要更多会议来解决冲突。团队为常见问题如豁免请求创建了更多 Jira 工作流程,但最终,仍然需要人工批准它们。 John 为授权辩护,但越来越失望。他的倡议早期采摘的低 hanging fruit 大多已解决,因此误报的比例也在增加。此时,投入该倡议的大部分工时涉及分类误报或处理官僚内斗。他想花更多时间改进解决方案或构建自己的内部集成,但总有另一个会议……*

这是许多安全计划中常见的场景。虽然最初的意图是好的,结果一开始看起来有前景,但实际的投资回报随着时间的推移大大减少,特别是考虑到维护合规机构的 opportunity cost。 这是第一个网络安全反模式:忙碌工作生成器。

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

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

浅层因果分析 🔗

在其核心,John 的倡议旨在解决可利用漏洞。这很关键但容易忽视。考虑到这一点,任何花在非可利用漏洞上的时间 effectively 是浪费时间和资源,分散了这一目标。James Berthoty 在一篇博客文章和 accompanying 图形中简洁地解决了这一点:

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

来源:James Berthoty,Latio Pulse 许多“shift-left”忙碌工作生成器的麻烦在于,很多成本转移给了开发者,这在评估计划有效性时没有准确捕获。通常,运营的总成本要高得多。 部分动机是偏向于责怪人为错误——在这种情况下,“可利用漏洞发生是因为人类将它们引入代码库。”因此,负责的人类应该分类和修复识别的漏洞。表面上,这确实是真的。但表面级评估对组织不利。重要的是保持好奇并抓住机会实践五个为什么:

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

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

错误解决方案优先级 🔗

明确地说,这并不意味着警报生成器是错误的解决方案——情况可能 dictate 首先从更容易的解决方案开始。有一个完整的安全运营领域 dedicated 检测和响应(将在未来的帖子中讨论)。 获得对漏洞数量的初步可见性是重要的第一步。然而,在应用程序安全中,你应该根据它们解决根本原因的能力来优先排序你的解决方案。为此,我喜欢参考 Kelly Shortridge 的安全解决方案冰淇淋锥层次结构:

来源:Kelly Shortridge,对 CISA 信息请求的响应 虽然该框架源自许多行业使用的 well-established 控制层次结构,但不幸的是在网络安全领域被忽视。简而言之,冰淇淋锥底部的较弱解决方案越来越依赖人类干预成功,而靠近顶部的解决方案通过设计消除危险。 任何依赖人类干预的解决方案都 baked in 误差 margin——我们毕竟只是人类——然后需要额外干预来解决。回想 John 必须做的 Jira 工作流程、电子邮件提醒和会议,只是为了确保开发者修补他们的错误。这就是忙碌工作的来源,以及为什么他的解决方案注定生成越来越多。更糟的是,这意味着消除可利用漏洞的实际结果永远无法达到 100%——总会有另一个未分类或错误驳回的警报。

缺乏机制 🔗

在这一点上,你可能有一个常见且非常合理的反对——不是每个人都有 Google 的安全团队 capability 构建和推出整个 Web 框架。 如果 Google 开源他们的高保证 Web 框架就好了,但它可能 overly-optimised 用于 Google 的内部工具链和模式。一些组织可能有高度异构的内部环境, preclude 单一框架。 这是选择最低 common denominator 解决方案如警报生成器的一个大 contributing factor。它在大多数缺乏组织范围机制来 effect change 的安全团队的控制区内。

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

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

清理不等于保持清洁 🔗

在 DevSecOps 空间有一个常见方法“清理,并保持清洁”。“清理”指的是解决所有未决发现,而“保持清洁”指的是阻止新发现发生。这里的关键原则是这些是两种 separate 战术。更关键的是,“清理”是一个临时第一步,而“保持清洁”是长期解决方案。 把它想象成从沉船中舀水。你理论上可以添加更多人舀水,但在某个点你需要修补变大的洞。一个常见错误是相信你需要完全清理 before 保持清洁。这是一个不可能的任务;总会有新问题涌入。 相反,清理到足以解决真正可利用的高和关键问题,然后转向保持清洁。John 的错误是 hyper-focus 清理,没有设定一个时间点开始投资保持清洁。 有多种方式设定合理的“足够清洁”点——可利用性或可达性、严重性、CVEs 的 KEV/EPSS。只需设定那个点并 commit to it。在 OGP,我们 leveraged Dependabot 自动分类规则,编写了我们自己的 CodeQL 规则集,并投入了几个周期处理(优先排序的)漏洞积压,before 为高严重性漏洞设置警报。这给了我们空间探索“保持清洁”解决方案,同时维护重要漏洞的合理基线。 一个真正可扩展的代码漏洞解决方案看起来更像 Google 的高保证 Web 框架蓝图,其中 Google 的安全工程团队 built-in 多个安全控制。他们的分享听起来很像 Shortridge 在这篇文章开头的引用:

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

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

善意不起作用,机制才起作用 🔗

任何依赖人类干预的解决方案都带有内置失败率。虽然开发者培训很重要,但它不是 foolproof——或者攻击者可能发现新颖攻击向量。理解这一原则帮助你远离警报生成器或 NagOps,并看向构建真实机制。 即使你缺乏现有机制,从合规到安全工程演变的一部分涉及强跨学科工作——特别是,与拥有高杠杆机制的工具、平台、devops 和其他工程团队合作。安全在孤岛中作为首席阻塞器操作是最简单和最差的操作模式。 在 OGP,安全工程团队没有构建 Starter Kit——工具团队做了。通过与他们合作将 Starter Kitty 组件引入 Starter Kit,我们还解决了开发者体验和可用性的显著问题,工程师更 attuned to(观察此线程),改进了我们的最终实现。 回想 John 最初在他的推出中如何破坏开发者的 CI/CD 管道。安全工程有时可能缺乏其他工程团队的上下文,这就是为什么密切合作重要以避免“大红按钮”。授权也是一种机制,但它们肯定不是最有效的。 简而言之,缺乏正确机制不是使用错误解决方案的借口——构建或找到那些机制。我将在第二篇关于构建有效机制的博客文章中更多讨论这一点。

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

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

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

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

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

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

忙碌工作生成器解决方案

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

根本原因解决方案

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

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

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

我们做了什么

GitHub 策略功能只有一个陷阱:GitHub 只允许将操作限制为内部操作、那些由验证市场创建者拥有的、官方 GitHub 操作和白名单的。然而,这可以解决强制哈希固定依赖的实际根本原因。 幸运的是,我们能够使用白名单中的模式为哈希固定操作创建变通方案。虽然不完美,这足够了直到 GitHub 实际添加哈希固定操作作为策略中的一个选项。我们还决定允许市场验证创建者和官方 GitHub 操作,以减少我们实施策略时失败操作的数量,减少开发者 friction。 更重要的是,我们与工程师合作构建了一个自定义 GitHub 应用程序,识别并通知开发者所有不受信任的操作,这些操作在策略实施时会失败——你可以在这里看到 FormSG 的示例。这减少了当我们翻转开关时不愉快惊喜的数量。

最后,我们为 GitHub Actions 中断指标和日志创建了监视器,以警报我们如果新策略导致失败峰值,确保我们可以在需要时及时回滚。 你可能根据你的组织上下文得出不同的解决方案,但你可以看到这种方法如何满足两个原则:

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

结论 🔗

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

“没有人告诉初学者这个,我希望有人告诉我。所有做创造性工作的人,我们进入它是因为我们有 good taste。但有这个差距。头几年你制作东西,它 just not that good。……你的品味是你的工作让你失望的原因。……需要一段时间。花一段时间是正常的。你只需要 fight your way through。” ——Ira Glass

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

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