【公益译文】了解、预防、修复:开源漏洞讨论框架

一、执行摘要

开源软件的安全性引起了业界关注,这并不奇怪。在实施相关安全方案时,需要对过程中的挑战与合作达成共识。这个问题很复杂,涉及方方面面:供应链、依赖关系管理、身份和构建管道。问题捋清后,解决方案也就呼之欲出。为方便业界讨论开源软件中的漏洞以及首要着眼点,我们提出了一个框架(“了解、预防、修复”),内容包括:

• 元数据和身份标准共识:行业需要就解决这些复杂问题的基本原则达成共识。在元数据细节和身份方面统一看法后,将推动自动化,减少更新软件所需的工作量,并将漏洞的影响降至最低。

• 提高关键软件的透明度,加强对这些软件的审查:对于对安全至关重要的软件,我们需要就开发过程达成一致,确保充分审查,过程透明,避免单方面更改,最终产生语义清晰的可验证官方版本。

以下框架和目标的提出旨在引发业界对开源软件安全的讨论,促进开源软件的安全。

鉴于最近发生的多起事件,软件界切实感受到了供应链攻击的风险实实在在地存在着。开源软件在安全方面的风险应该较小。因为所有代码和依赖项都是开放的,可供检查和验证。总的来说,这种看法没什么问题,但它的前提是确实做了检查。要全面监控这么多的依赖项不太现实,而且许多开源软件包并没有得到妥善维护。

一个程序通常直接或间接地依赖于数千个软件包和库。例如,Kubernetes现在就有约1000个依赖包。开放源代码可能比封闭源代码的依赖关系更多,并且来自更广泛的供应商;需要信任的实体数量巨大。这样,就很难弄清楚开放源代码在产品中的使用情况以及哪些漏洞事关重大,同时也无法保证生成的内容与源代码匹配。

退一步讲,尽管存在供应链被攻击的风险,绝大多数漏洞都是善意的开发人员的无心之失。此外,攻击者更可能利用已知漏洞,而不是寻找新漏洞:显然前者更简单。因此,我们必须集中精力进行根本性的变革,堵住大多数漏洞,进而推动整个行业取得进步,有效解决复杂问题,包括供应链攻击。

很少有组织能够验证自己使用的所有软件包,更不用说这些软件包的所有更新了。在当前情况下,跟踪这些包需要大量的基础设施和大量的手动工作。谷歌拥有这些资源,同时尽自己所能管理所使用的开源软件包(包括对内部使用的所有开源软件包建立私有库),但要跟踪所有更新仍颇具挑战。光是上传下载这些更新就令人望而生畏。无论什么方案,解决这个问题的核心都是提高自动化水平,这将是2021及以后我们开源安全工作的一个重要主题。

由于这个问题很复杂,需要行业合作,本文仅围绕具体目标展开。谷歌联合创建了OpenSSF,聚焦于解决这一问题,但要取得进展,需要整个行业的参与,就问题所在以及如何解决这些问题达成一致。为抛砖引玉,我们提出了一个讨论办法以及一系列具体目标,希望通过这些目标加速全行业的解决方案研发。

我们建议将挑战分为三个基本独立的问题领域,每一领域都有自己的具体目标:

1. 了解软件中存在的漏洞

2. 预防新漏洞的出现

3. 修复或删除漏洞

提高开发过程的安全性是一个相关但独立的问题,对于确保供应链安全至关重要。我们在第四节“关键软件的预防措施”中概述了此问题带来的挑战并提出了目标。

二、了解漏洞

由于各种原因,了解漏洞比想象的要困难。虽然有漏洞报告机制,但很难判断漏洞是否确实会影响所使用的特定软件版本。

目标:精确的漏洞数据

首先,从所有可用数据源捕获精确的漏洞元数据至关重要。例如,知道哪个版本引入了漏洞就能确定所使用的软件是否受到影响,知道漏洞何时被修复就可以准确及时地进行修补(并减少被利用的可能性)。理想情况下,这种分类工作应自动完成。

其次,大多数漏洞都存在于依赖项中,而不是自己所编写或控制的代码中。因此,即使自己的代码并未改动,漏洞也会不断发生变化:有被修复的,也有新引入的。

目标:标准的漏洞库格式

跟踪和维护开源漏洞、了解其后果并管理缓解措施需要基础设施和行业标准。采用标准漏洞格式后,通用工具就能跨漏洞库工作,简化跟踪任务,尤其是当漏洞涉及多种语言或子系统时。

目标:精确跟踪依赖项

要快速了解新漏洞对软件的影响需要更好的工具,而大型依赖树的规模和动态特性使这个问题变得愈加棘手。就目前的普遍做法来看,在没有实际安装的情况下很难准确预测使用的版本,因为版本解析软件只能通过安装程序获得版本信息。

三、预防新漏洞

理想的做法是防止漏洞产生。尽管有测试和分析工具可用,预防始终是一个难题。在这里,我们关注两个具体方面:

• 在决定引入新的依赖项时提前了解风险;

• 改进关键软件的开发过程。

目标:了解新依赖项的风险

第一个方面实际上是指在决定使用某软件包时提前了解其中存在的漏洞。引入新的依赖项具有内在风险,不能盲目决策。依赖关系一旦引入,会随着时间的推移越来越难消除。

首要的是要了解漏洞,但这还远远不够。

许多漏洞之所以存在是因为软件开发过程中未遵守安全最佳实践。是否所有参与者都使用了双因素身份认证(2FA)?项目是否有持续集成设置和运行测试?是否集成了模糊测试?这些都是安全检查类型,可以帮助消费者了解新引入的依赖项中所面临的风险。得分较低的软件包需要进一步审查,并制定补救计划。

OpenSSF最近宣布的安全记分卡项目旨在以全自动方式生成这些数据点。使用记分卡还有助于抵御猖獗的Typosquatting(名称与常用软件包相似的恶意软件包)攻击,因为这种软件包的得分要低得多,无法通过多数安全检查。

改进关键软件的开发过程与漏洞预防相关,但需要进一步讨论,所以将在后文详述。

四、修复或删除漏洞

漏洞修复的一般问题不在本文讨论之列,我们关注的是管理软件依赖项漏洞这一具体问题。在这方面,目前几乎没有现成工具可用,但随着漏洞相关数据准确性的提高,可望通过投资新工艺和工具解决这个问题。

当然,最直接的方法是修复漏洞。若采用的是向后兼容方法,那么这一修复会惠及所有人。但问题是,你可能并不具有相关专业知识,也无法直接做出改变。修复漏洞的前提是软件维护人员知道漏洞的存在并拥有漏洞披露所需的知识和资源。

相反,如果你干脆删除了包含漏洞的依赖项,这种修复便对自己的软件以及导入或使用这个软件的其他人有效,但不会惠及所有人。这一更改由软件所有者直接控制。

上述场景代表了自己的软件和漏洞之间依赖关系链的两端,但实际上中间可能存在多个软件包。人们都指望依赖关系链上的某个人能够修复漏洞。然而,只修复单个环节是不够的:要更新自己和漏洞之间依赖关系链的所有环节,才能修复自己的软件。每一环节都必须修复之前环节的版本漏洞,这样才能彻底清除漏洞。因此,更新需要从下到上进行,除非能完全消除依赖项,这个办法需要勇气,也几乎不可能,但若可能的话,则是最佳解决方案。

目标:了解漏洞删除方案

当前,我们对这一过程缺乏清晰的认识:其他人取得了什么进展,应该在什么级别进行哪些升级?这一过程在哪里受阻?谁负责修复漏洞?谁负责推行修复方案?

目标:利用通知加速修复

最终,依赖项会被修复,之后,就可以在本地升级到新版本。了解修复时间是一个重要目标,因为这可以大大降低漏洞带来的风险。我们还需要一个通知系统来通告发现的漏洞;通常,新漏洞指新发现的潜在问题,即使实际代码并没有更改(例如UNIX实用程序sudo中有一个存在10年之久的漏洞)。对于大型项目,大多数此类问题会出现在间接依赖项中。目前,由于准确性还达不到要求,无法做好通知,但随着漏洞准确性和元数据(如上所述)的提高,我们还应推动通知。

到目前为止,我们只描述了一种简单情况:升级都是向后兼容的,这意味着除了没有漏洞之外,行为相同。

实际上,升级通常不向后兼容,或者受限于限制性版本要求。这些问题意味着更新依赖树深处的软件包必然会导致上面的内容发生变化,或者至少要更新要求。常见例子是,修复了最新版本(比如1.3版本),但自己的软件或中间软件包要求使用1.2版本。这种情况并不鲜见,如今仍是一大挑战,尤其是很难让软件所有者更新中间软件包。此外,如果一个软件包用在一千个不同的地方(这对于大企业来说很正常),那么可能需要经历一千次更新过程。

目标:修复广泛使用的版本

修复旧版本中的漏洞也很重要,尤其是那些使用频繁的版本。这种修复对于具有长期支持的软件来说很常见,但理想情况下,所有广泛使用的版本都应该修复,尤其是具有安全风险者。

自动化能促进这项工作:得到某一修复版本后,也许我们可以为其他版本生成有效的修复方案。目前,这个过程有时通过手工完成,但如果我们能大大简化这一过程,那么实际上能够修复更多版本,这样,在依赖关系链上端需要做的工作就会减少。

总而言之,我们需要使用多种方法来简化并加快漏洞修复,尤其是依赖项中的漏洞。对于广泛使用的版本,需要尽可能进行修复,而不仅仅是修复最新版本,因为最新版本可能包含一些其他程序无法兼容的变更,导致无法采用。

最后,在“修复”方面还有许多其他方案,包括各种缓解措施,如避免某些方法,或通过沙盒或访问控制抑制风险。这些方案也很重要且可行,需要进一步讨论,得到更多支持。

五、关键软件的预防措施

上述框架广泛适用于各种漏洞,无论这些漏洞是由恶意主体造成的,还是仅为无心之失。虽然建议的目标涵盖了大多数漏洞,但它们不足以防止恶意行为。为了加强对恶意行为(包括供应链攻击)的预防,我们需要改进开发过程。

这是一项艰巨的任务,尤其是对于现今大多数的开源软件而言,很难实现。开源的其中一个优点是对过程没有限制,这鼓励了各类人群的广泛参与。然而,这种灵活性可能会影响安全。我们需要贡献者,但我们不能指望所有人都对安全给与同样的关注。我们要做的是识别关键软件包并加以保护。这类关键软件包必须遵守更严格的开发标准,即使这会让开发人员心存抵触。

目标:定义需要遵循更严格标准的“关键”开源项目

识别大家普遍依赖的关键软件包很重要,因为这些软件被入侵后将危及关键基础设施或用户隐私。关键软件包需要遵循更严格的标准,如下所述。

如何定义“关键”并非易事,而且定义还可能会随着时间的推移而扩展。除了OpenSSL或密钥加密库等众所周知的软件外,还有一些广泛使用的软件包,它们的覆盖范围之广决定了它们必须得到保护。我们启动了关键性评分项目,与社区集体讨论这个问题,并与哈佛大学合作开展开源普查工作。

目标:避免单方面更改关键软件

谷歌全司遵循的一个原则是,不进行单方更改——也就是说,每个更改都至少涉及一名作者和一名评审者。目标是限制攻击者独立行动的能力,保证有人确实查看了变更之处。要在开放源代码中做到这一点,实际上比在公司内部要困难得多,因为公司能使用强身份认证,强制执行代码审查和其他检查。

“避免单方面更改”可拆分为两个子目标:

目标:要求对关键软件进行代码审查

审查过程不仅能大大促进代码改进,还确保除了作者之外,至少有一个人在查看更改。代码审查是谷歌内部所有变更的标准做法。

目标:关键软件的更改需要独立两方的批准

为了真正实现“有人在看”的目标,审查者需要独立于贡献者。对于关键变更,我们可能需要不止一次独立审查。当然,我们需要理清“独立”审查的含义。实际上,独立审查的理念对于大多数行业来说都很重要。

目标:关键软件参与者认证

独立意味着参与者身份已知,匿名者则被视为非独立或可信。现今,基本每个人都有化名:同一个人反复使用同一身份,以此身份为人所知,但我们并不能确定这人一定可信。所以,还需要一系列子目标:

目标:关键软件的所有者和维护者不能匿名

攻击者喜欢匿名。在之前发生的供应链攻击中,攻击者利用匿名性,进入软件包社区成为维护者,但没人发现这个“新维护者”有恶意意图(最终向上游注入了入侵源代码)。我们认为,为了减轻这种风险,关键软件的所有者和维护者不得匿名。

与所有者和维护者不同,贡献者可以匿名,这可以理解,但前提是他们的代码通过了可信方的多次审查。

同样,我们可以拥有“经验证”的身份,其中一个可信实体知道我们的真实身份,但出于隐私原因,不透露给公众。这有助于做出关于独立性的决定,对非法行为进行起诉。

目标:对关键软件贡献者进行强认证

攻击者寻找容易利用的攻击向量,因此钓鱼攻击和与凭据相关的其他形式的盗窃成为常事。要求使用双因素身份认证,尤其是对于所有者和维护者,这会显著提升安全。

目标:联合身份模型

为了继续保持开放源代码的包容性,我们需要信任各种身份,但仍然要对完整性进行验证。这意味着需要采用联合身份模型,该模型类似于联合SSL证书支持方式,多个群组可生成有效证书,但具有强大的审核和相互监督机制。

OpenSSF的数字身份认证工作组已开始就此展开讨论。

目标:通告风险变化

我们应该扩大通知范围,对风险变化也进行通知,尤其是所有权变更,这可能是新攻击的前奏(例如最近发生的NPM事件流入侵)。若发现了被盗凭证、合谋等恶意行为,也说明风险发生了变化。

目标:工件透明度

通常使用安全哈希来检测所收到的工件是否完好无损,使用数字签名来证明其真实性。引入“透明度”后,会公开记录上述验证及其目的。继而,外部各方可监控日志,发现连用户都未觉察的伪造版本。接下来,当凭据被盗时,我们就能筛选出使用这些凭据签名的工件,将其删除。这种透明度(包括长期保存的公共日志和第三方监控)已经在SSL证书方面取得了巨大成功。针对软件包管理器,我们提出了对应的方法。用户要确认软件包或二进制文件正确无误,就好比在访问网站时要确认网站真实可靠。

目标:信任构建过程

1984年,肯·汤普森(Ken Thompson)在图灵奖(Turing Award)获奖感言中做了一个著名的演示,说明仅保证源代码真实远远不够。近期发生的事件表明,对工件的攻击已成为了实实在在的威胁。如何保证所构建的系统真实可信?工件的所有组件都必须纳入持续的构建信任过程进行验证,保证可信。

构建若可复制,便能产生确定结果,有助于确认可信性。然而,由于发布工件中存在临时数据(如时间戳),可复制又很难实现。安全的可复制构建需要验证工具,而这些工具又必须以可验证和可复制的方式构建,这样循环往复。我们必须构造一个可信工具和构建产品网。

工件和工具信任都可以通过“委托”来建立,而委托通过上述透明过程的一个变体实现,称为二进制授权。谷歌内部的构建系统对所有工件进行签名,并生成清单,将工件与源代码关联起来。对于开放源代码,一个或多个可信代理可以将构建作为服务运行,对工件进行签名,证明工件的完整性。这种生态系统应成为现实,在大多数情况下,还需要培养意识,就证明格式达成一致,以便安全地对过程进行自动化。

总体上,本节中的操作对于软件安全极为有益,如今在谷歌内部广泛采用。而对于开源来说,这些操作更为重要。我们希望,至少可以针对关键软件实现这些目标。随着工具和自动化水平的提高,这些目标将会更容易实现,可望普遍推行。

六、总结

开源的本质要求我们通过共识和协作来解决问题。对于漏洞等复杂主题,需要围绕关键问题进行重点讨论。我们为此提出了一种方法,并定义了一组目标,希望通过这些目标促进全行业讨论,快速找到最终的解决方案。第一组目标适用于所有漏洞,目的是为了实现自动化,减少风险和工作量。

漏洞管理的一般目标

 
然而,这些目标尚不足以对抗攻击者或防止供应链攻击。因此,我们为关键软件提出了第二组目标。这组目标更为繁琐,因此会遇到一些阻力,但我们认为额外的约束是安全的基础。目的是定义“关键”软件包,将更严格的标准仅应用于这类软件。

针对关键开源软件的目标

尽管对如何实现这两组目标看法不一,但在共识和可持续解决方案最为重要的领域,我们意见一致。我们期待大家对此展开讨论,互通有无,分享好点子,最终找到解决方案,加强我们所依赖的开放源代码的安全性,优化防护。

原文作者:Eric Brewer,Rob Pike,Abhishek Arya,Anne Bertucio,Kim Lewandowski

免责声明:该文章原文版权归原作者所有。文章内容仅代表原作者个人观点。本译文仅以分享先进网络安全理念为目的,为业内人士提供参考,促进思考与交流,不作任何商用。如有侵权事宜沟通,请联系littlebee@nsfocus.com邮箱。

Spread the word. Share this post!

Meet The Author