醒醒吧,没有什么安全的软件供应链
本文最初发布于 John McBride 的个人博客。
多年前,企业家和创新者就预言“软件将吞噬世界”。毫不奇怪,年复一年,世界变得越来越依赖软件解决方案。通常情况下,这里说的软件是(或间接依赖于)一些开源软件,由一组人维护,这些人之间的唯一联系可能就是参与该开源项目社区。
但我们有麻烦了。开源软件的安全性正在受到威胁,这几乎耗光了维护这些项目的人员的精力。随着技术栈越来越深,依赖项的关联关系愈加复杂,人们对软件供应链安全做出了可怕的妥协。对于开源世界中正在发生的事情,如果要找一个典型的例子,我们只需看下当下非常流行的 Gorilla Go 工具箱。
Gorilla 是一个提供强大 Web 框架技术的项目,如 mux 和会话。在其漫长的生命周期里,它实际上是一个用于 Web 服务器、路由请求、处理 HTTP 流量及使用 WebSockets 的 Go 框架。有成千上万的软件包使用了它。对于 Go 社区中的大多数人来说,这个项目将不复存在是一个非常让人震惊的消息:不再维护,不再发布新版本,也不会再提供社区支持。但是,如果你对其社区足够关注,应该早就会看到一些动荡的迹象:公开招募维护人员没人响应,几乎没有积极的外部贡献者,并且维护人员的负担非常沉重。
Gorilla 框架只是那些“重要的依赖项”中的一个。它处于一个非常重要的位置,既要能提供便利,又要能安全地处理重要的有效载荷。开发人员可以围绕 Gorilla 提供的 API 构建他们自己的逻辑,也可以将它作为整个代码库的基础。整个社区都很信任 Gorilla,你最不想在服务器上看到的就是一个充满 Bug 和 CVE 的 Web 框架。
在安全的软件供应链中,就像 Nginx 和 OpenSSL 一样,它是许多其他供应链和依赖项的基石。如果 Gorilla 框架出了问题,就有可能影响数百万服务器、服务和其他项目。
软件供应链安全是大型科技公司、安全公司和新闻媒体都喜欢谈论的抽象概念之一。这是一种“理念”,即你在整个技术栈中作为依赖项使用的软件正是你所期望的软件。换句话说,可以保证黑客没有向你使用的库或构建工具中注入后门,从而危及你的整个产品、软件库甚至公司。供应链攻击是恶作剧式的,因为它们几乎从不针对实际的目标。相反,它们会通过攻击一些依赖项来寻找预期的目标。
直到今天,太阳风攻击仍是一个很典型的例子:一些俄罗斯支持的匿名黑客组织能够破坏内部 Solar Winds 构建系统,后续任何使用该系统构建的软件都会被注入后门和漏洞。这次攻击的后果很严重,包括美国国务院在内的许多政府机构都证实发生了大规模数据泄露。据估计,这次攻击造成的损失还在继续上升,预计将达数十亿美元。
在过去几年里,试图解决这些问题的产品一个接一个地涌现出来:软件签名解决方案、自动安全扫描工具、及时更新的 CVE 数据库、自动机器人、人工智能辅助编码工具等。甚至一整个白宫的顾问都在讨论这个问题。联邦政府知道,这是我们国家软件基础设施最重要(也是最脆弱)的载体,他们已经采取了直接行动来打击这类攻击。
但是,安全的软件供应链也是那些很快就会瓦解的东西之一,没有细致的处理和严密的保护,事情很快就会变糟。几个月以来,Gorilla 工具包一直在公开招募维护人员,寻找更多的人来保证代码库的更新、安全以及得到良好的维护。但最终,Gorilla 的维护人员找不到足够的人来维持这个项目。
评论里许多人自告奋勇,但之后就再也没有出现过。当然,维护人员的门槛也很高:
把一个每周有超过 1.3k 不同克隆的软件包(mux)交给别人管理,这是我所不能接受的。这种做法以往在其他项目中引发过糟糕的事情。
2018 年,GitHub 用户 FallingSnow 在流行但并不是很知名的 NPM JavaScript 包事件流中打开了 “我不知道该说什么”这个问题。他在库最近提交的文件中发现了一些非常奇怪的东西。一名社区中从未见过的新的维护人员,似乎还是一个全新的 GitHub 帐户,直接向主分支提交了一段奇怪的代码。这个身份未知的新维护者还把一个新包添加到 NPM 注册中心,并将这一变化强加给追踪项目最新软件包的人。
更改类似这样:在一个新文件中添加了一个很长的内联加密字符串。该字符串会使用一些未知的环境变量进行解码,然后,未加密的字符串将作为 JavaScript 模块注入到包中,进而执行隐藏在加密字符串后面的任何代码。简而言之,未知的代码会在运行时被破译、注入并执行。
GitHub 上的这个问题迅速传播开来。通过纯粹的蛮力、一点点运气和数百名评论者,社区破解了字符串,揭露了注入代码的目的:加密货币“钱包小偷”。如果代码在系统上检测到一个特定的钱包,它就会使用一个已知的漏洞窃取存储在该钱包中的所有加密货币。
这个代码漏洞在事件流 NPM 模块中存在了几个月。安全扫描工具、消费者和项目所有者都没有发现。直到社区中有好奇心比较强的人仔细看了一下时,这种明显的代码注入攻击才变得明朗。
但是,使这次攻击特别糟糕的是,使用事件流模块的模块有很多(而这些模块又被其他模块使用,以此类推)。理论上,这可能会影响到数千个软件包和数百万终端用户。有些开发人员不知道他们的 JavaScript 依赖栈深处使用了事件流,现在,突然之间,他们不得不快速修补自己的代码。这怎么可能呢?是谁批准和允许这一切发生的?
GitHub 存储库的所有者和代码的原作者表示:
他给我发邮件,说他想维护这个模块,所以我把它给了他。我没有从维护这个模块中得到任何东西,我甚至不再用它了,而且已经很多年没用了。
注意:我不再有这个模块在 npm 上的发布权限。
就像这样,仅仅通过询问,一些恶意行为者就能够破坏成千上万的软件包,披着“维护人员”的面纱而不被发现。
过去,我将其称为“单一维护者依赖风险”:独自维护广泛分发的软件包所带来的那种无法抗拒的、通常让人感到孤独的、有时甚至是危险的体验。就像事件流的所有者一样,大多数单独的维护人员都会逐渐消失,淡出人们的视野,任由他们的软件陷入混乱。
Gorilla 的情况就是如此:
原作者和维护者 moraes 很久以前就离开了。kisielk 和 garyburd 参与时间最长,各自维护了 HTTP 库和 gorilla/websocket。我(elithrar)大约是在 2014 年的某个时候参与进来的,当时我注意到,kisielk 做了很多繁重的工作,我在一些个人项目中使用了这个库,我希望提供帮助并回馈项目。大约从 2018 年左右开始,我(基本上)是除了 websocket 之外所有东西的唯一维护人员,这也是 garyburd 发布公告招募新维护人员(实际上并不成功)的时间。
安全的软件供应链永远不会真正的强大和安全,只要一个单独的维护人员就能够破坏整个包的生态系统,而他所要做的只是将包提供给一些恶意行为者。事实上,不存在安全的软件供应链:我们的强大程度取决于中间最弱的环节,而且在很多情况下,供应链中的薄弱环节已经被破坏并变得越来越糟糕,或被那些怀有邪恶目的的人所利用。
每当我提起这个话题,总会有人问起钱的问题。啊,金钱,生活带给人的最真实的满足!是的,对一些人来说,金钱是一个强大的动力。但对于安全的软件供应链所真正需要的东西——真正的可靠性,这是一个可悲的借口。
软件行业可以在重要开源项目的维护者身上砸下所有的钱,Valve 已经开始这么做了:
Griffais 表示,该公司还直接雇佣了 100 多名开源开发人员来开发 Proton 兼容层、Mesa 图形驱动程序和 Vulkan,并完成 Steam for Linux 和 Chromebook 等其他任务。
但在某些时候,只靠少数几个人来保证公司整个产品栈的完整性、安全性和可行性是不合理的。如果它如此重要,为什么不雇佣一群这样的人,成立一个专门的维护人员团队,创建贡献流程,并让开发人员可以留出时间参与开源项目的开发呢?
我经常听到有人说,要通过砸钱来解决开源问题,但在某种程度上,扩大软件交付规模的问题不是给几个人付多少钱就能解决的。比如你在建造一所房子,让一两个人打地基是合理的。但如果你正在规划和建造整个城市街区,我当然希望你能投入整个团队来规划、建造和维护这些地基。钱再多,少数几个人也无法打下一个强大、安全的地基。但我们要求一些开源项目维护者做的就是为全世界规划、构建和调整这样的地基。
Gorilla 的维护人员也意识到了这一点:
不。我觉得我们都不是为了钱。回顾下最活跃的维护者,Gorilla Toolkit 是一个充满激情的项目。我们不想让它成为一份工作。
对他们来说,这不是钱的问题,所以在这个项目上砸多少钱都无济于事。那关于软件质量、可维护性以及它所提供的内在满足感。
那么,我们如何激励开源维护人员以一种可扩展而又现实的方式维护他们的软件呢?有些人的动机是为社区提供利他价值。有些人的动机是名誉、权力和认可。而其他人只是想玩得开心,做一些酷酷的事情。我们不可能理解开源社区中不同人错综复杂的动机来源。
相反,最好的解决方案是显而易见的:如果你所在的团队依赖于某种开源软件,那么就分配真正的工程时间来做贡献,成为社区的一部分,并帮助维护该软件。最终,你就会对项目的运作方式和主要参与者的动机有一个很好的了解。更好的是,你可以帮助减轻独自维护者的沉重负担。
有时,我喜欢把软件想象成一个木制的独木舟,它的许多依赖就是船的木条。刚建好的时候,它看起来结实、稳固,能够承受最恶劣的条件。它的第一层油面新鲜而美丽,它的木纹光滑而平直。但随着岁月的流逝,最终,它的的饰面逐渐褪色,它的木条需要更换,如果它进了水,可能还需要时间和新的材料来修补。久而久之,它的木头会从内部发霉腐烂,船遭到了破坏,不再完整。
就像船一样,软件也需要时间、精力、维护和“全体动员(hands-on-deck)”,以确保它在安全的软件供应链的诸多环节中都是可靠的。否则,时间白蚁和恶意行为者的破坏会削弱链条中的各个环节,危及整个链条的稳定性。
最后,Gorilla 框架的维护者做了正确的事情:他们终止了一个广泛使用的项目,这个项目有从内到外腐烂的风险。这可以避免它陷入混乱或落入坏人之手,它就这样消失了。它故意切断了自己在软件链上所处的环节,迫使所有使用它的人选择一个更好、希望也是更安全的选项。
我相信,开源软件是有生命周期的——开始、中间、结束——没有一个项目需要永远存在。可能会有人因此不开心,但生活就是这样。
原文链接:
https://johncodes.com/posts/there-is-no-secure-software-supply-chain/
文章版权归极客邦科技 InfoQ 所有,未经许可不得转载。
你也「在看」吗? 👇
微信扫码关注该文公众号作者