谷歌内部开源组件的风险治理框架和工作流程

年后第一篇,有点懒,实际上是翻译人家Google写出来的文章,读完之后确实对现在的工作有些新的理解。

执行摘要:

开源软件的安全性理所当然地引起了业界的关注,但是解决方案需要就执行过程中的挑战和合作达成共识。问题很复杂,涉及的方面很多:供应链,依赖管理,身份和构建管道。当问题得到妥善解决时,解决方案就会变得更快。我们为行业如何考虑开放源代码和具体领域中的漏洞提出了一个框架(“了解,预防,修复”),首先要解决的问题包括:

  • 关于元数据和身份标准的共识:我们需要就基础知识达成共识,以解决作为一个行业的这些复杂问题。关于元数据详细信息和身份的协议将实现自动化,减少软件更新所需的工作并最大程度地减少漏洞的影响。
  • 提高关键软件的透明度和审计:对于对安全至关重要的软件,我们需要就确保充分审计,避免单方面更改并透明地产生定义明确,可验证的正式版本的开发流程达成共识。

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

由于最近发生的事件,软件界对供应链攻击的实际风险有了更深入的了解。开源软件在安全方面应降低风险,因为所有代码和依赖项都是公开的,可用于检查和验证。尽管这通常是正确的,但它假设人们实际上正在看。由于存在如此多的依赖关系,因此监视所有这些依赖关系是不切实际的,而且许多开源软件包的维护也不好。

程序直接或间接依赖于数千个程序包和库是很常见的。例如,Kubernetes现在依赖大约1,000个软件包。与封闭源相比,开放源可能会更多地使用依赖项,并且供应商范围更广。需要信任的不同实体的数量可能非常高。这使得很难理解产品中如何使用开源以及哪些漏洞可能是相关的。也不能保证所构建的内容与源代码匹配。

退后一步,尽管供应链攻击是一种风险,但绝大多数漏洞是世俗的和无意识的-善意的开发人员犯了诚实的错误。此外,不良行为者更有可能利用已知漏洞而不是找到自己的漏洞:这很容易。因此,我们必须专注于进行根本性更改以解决大多数漏洞,因为这样做将使整个行业在解决复杂情况(包括供应链攻击)方面也向前迈进了一步。

很少有组织可以验证他们使用的所有软件包,更不用说对那些软件包的所有更新了。在当前情况下,跟踪这些程序包需要大量的基础架构和大量的人工。在Google,我们拥有这些资源,并竭尽全力来管理我们使用的开源程序包-包括对内部使用的所有开源程序包进行私有回购-跟踪所有更新仍然是一项挑战。庞大的更新流程令人生畏。任何解决方案的核心部分都将是更高的自动化程度,这将是2021年及以后我们开源安全工作的关键主题。

由于这是一个需要行业合作的复杂问题,因此我们的目的是将对话重点放在具体目标上。 Google与他人共同创立了OpenSSF,以此作为合作的重点,但要取得进展,我们需要整个行业的参与,并就问题所在以及如何解决这些问题达成共识。为使讨论开始,我们提出了解决此问题的一种方法,并提出了一系列具体目标,希望这些目标可以加快整个行业的解决方案。

我们建议将挑战划分为三个主要独立的问题领域,每个领域都有具体的目标:

1.了解您软件中的漏洞

2.防止增加新的漏洞,并且

3.修复或消除漏洞。

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

了解您的漏洞

出于多种原因,知道您的漏洞比预期的要难。尽管存在报告漏洞的机制,但很难知道它们是否确实影响您所使用的特定版本的软件。

目标 1:精确的漏洞数据

首先,至关重要的是从所有可用数据源中捕获精确的漏洞元数据。例如,了解哪个版本引入了漏洞可以帮助确定某个软件是否受到了影响,而知道何时对其进行了修复,则可以准确,及时地进行修补(并减少了潜在的利用范围)。理想情况下,此分类工作流程应是自动化的。其次,大多数漏洞都在您的依赖项中,而不是您直接编写或控制的代码中。因此,即使您的代码没有更改,漏洞也可能持续不断地流失:某些漏洞得到修复,而另一些漏洞得到了补充。

目标2:漏洞数据库的标准架构

需要基础结构和行业标准来跟踪和维护开源漏洞,了解其后果并管理其缓解措施。标准漏洞架构将允许通用工具跨多个漏洞数据库工作并简化跟踪任务,尤其是当漏洞涉及多种语言或子系统时。

目标3:准确跟踪依赖关系

需要更好的工具来快速了解哪些软件受到新发现的漏洞的影响,该问题因大型依赖树的规模和动态性质而变得更加棘手。由于只有通过安装程序才能提供用于版本解析的软件,因此当前的做法通常也很难准确地预测使用哪些版本而不进行实际安装。在这里解释一下防止新漏洞:理想的情况是防止漏洞的产生,尽管测试和分析工具可以提供帮助,但预防始终是一个难题。 在这里,我们关注两个特定方面:决定新的依赖关系时要了解风险和改善关键软件的开发流程

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

第一类实质上是在您决定使用软件包时了解漏洞。承担新的依赖关系具有固有的风险,因此需要做出明智的决定。一旦有了依赖项,通常随着时间的推移就变得很难删除。了解漏洞是一个不错的开始,但是我们可以做更多的事情。许多漏洞是由于缺乏软件开发流程中的安全性最佳实践而引起的。所有贡献者都使用两因素身份验证(2FA)吗?项目是否具有持续集成设置并正在运行测试?模糊集成了吗?这些类型的安全检查可帮助消费者了解新依赖关系带来的风险。 “分数”低的软件包需要进行仔细的审查,并制定补救计划。OpenSSF最近宣布的“安全计分卡”项目试图以完全自动化的方式生成这些数据点。使用记分卡还可以帮助抵御流行的抢注攻击(名称与流行软件包相似的恶意软件包),因为它们的得分要低得多,并且无法通过许多安全检查。改进关键软件的开发流程与漏洞预防有关,但值得在我们的帖子中进行进一步的讨论。

修复或删除漏洞

解决漏洞的一般问题超出了我们的范围,但是对于管理软件依赖项中的漏洞这一特定问题,我们可以做很多事情。今天,在这方面几乎没有帮助,但是随着我们提高精度,有必要对新的流程和工具进行投资。当然,一种选择是直接修复漏洞。如果您可以通过向后兼容的方式执行此操作,则该修补程序适用于所有人。但是一个挑战是,您不太可能在问题上没有专业知识,也不太可能具有直接进行更改的能力。修复漏洞还假定软件维护者知道此问题,并拥有披露漏洞的知识和资源。相反,如果仅删除包含该漏洞的依赖项,那么它对您和导入或使用您的软件的依赖项是固定的,但不适用于其他任何人。这是您直接控制的更改。这些方案代表了软件与漏洞之间的依赖关系链的两端,但是实际上,可以有许多中间包。人们普遍希望依赖关系链中的某个人能够解决它。不幸的是,仅修复链接是不够的:您和漏洞之间的依赖关系链的每个链接都需要更新,然后才能修复软件。每个链接都必须在其下面包含事物的固定版本,以清除漏洞。因此,除非需要完全消除依赖关系,否则更新需要自下而上进行,因为依赖关系可能需要类似的英雄技巧,而且几乎不可能实现,但这是最好的解决方案。

目标5:了解消除漏洞的选择

今天,我们在这个过程中缺乏明确性:其他人已经取得了什么进展,应该在什么级别上应用什么升级?流程卡在哪里?谁负责修复漏洞本身?谁负责传播此修补程序?

目标6:通知以加快维修速度

最终,您的依赖关系将得到修复,您可以在本地升级到新版本。了解何时发生是一个重要的目标,因为它可以加快减少漏洞暴露的风险。我们还需要一个通知系统来实际发现漏洞;通常,新漏洞表示即使实际代码未更改,新发现的潜在问题(例如Unix实用程序sudo中已有10年历史的漏洞)。对于大型项目,大多数此类问题将出现在间接依赖关系中。今天,我们缺乏做好通知所需的精度,但是随着我们提高漏洞的准确性和元数据(如上所述)的同时,我们也应该推动通知。

到目前为止,我们仅描述了一种简单的情况:一系列向后兼容的升级,这意味着除了没有漏洞之外,其行为是相同的。实际上,升级通常不向后兼容,或者被限制性版本要求所阻止。这些问题意味着更新依赖关系树深处的程序包必须在其上面的内容中造成一些混乱,或至少导致需求更新。这种情况是在对最新版本(例如1.3)进行修复后出现的,但是您的软件或中间软件包要求1.2。我们经常看到这种情况,但这仍然是一个很大的挑战,因为很难使所有者更新中间包,这使挑战变得更加艰巨。而且,如果您在数千个地方使用软件包,这对于大型企业来说并不疯狂,那么您可能需要经历一千次更新过程.

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

修复较旧版本中的漏洞也很重要,尤其是那些经常使用的漏洞。对于具有长期支持的软件子集,这种修复是常见的做法,但是理想情况下,应该修复所有广泛使用的版本,尤其是对于安全风险。自动化可能会有所帮助:给定一个版本的修复程序,也许我们可以为其他版本生成良好的候选程序修复程序。今天有时有时会手动完成此过程,但是如果我们可以大大简化此过程,则实际上会打补丁更多的版本,并且在链中进行更高工作的工作将更少。总而言之,我们需要更容易,更及时地解决漏洞,尤其是依赖项中的漏洞。我们需要增加针对广泛使用的版本(不仅限于最新版本)进行修复的机会,由于其包含的其他更改,通常很难采用该修复程序。最后,“修复”方面还有许多其他选择,包括各种缓解措施,例如避免某些方法,或通过沙盒或访问控制来限制风险。这些是重要的实用选项,需要更多讨论和支持。

预防关键软件

上面的框架广泛地适用于漏洞,无论它们是由于不良行为所致还是仅仅是无辜的错误。尽管建议的目标涵盖了大多数漏洞,但不足以防止恶意行为。为了对预防不良行为者(包括供应链攻击)产生有意义的影响,我们需要改进用于开发的流程。

这是一项艰巨的任务,目前对于大多数开放源代码来说都是不现实的。开源之所以具有美感,部分原因在于它对流程的缺乏约束,这鼓励了广泛的贡献者。但是,这种灵活性可能会妨碍安全性考虑。我们需要贡献者,但是我们不能期望每个人都同样关注安全性。相反,我们必须确定关键软件包并加以保护。这样的关键软件包必须遵守一系列更高的开发标准,即使这可能会增加开发人员的摩擦。

目标8:确定符合更高标准的“关键”开源项目的标准

重要的是要确定我们都依赖的“关键”软件包,这些软件包的危害将危及关键基础架构或用户隐私。这些软件包必须保持较高的标准,我们在下面概述了其中的一些标准。如何定义“关键”还不是很明显,该定义可能会随着时间的流逝而扩展。除了诸如OpenSSL或密钥加密库之类的明显软件之外,还有广泛使用的软件包,其绝对功能使它们值得保护。我们启动了Criticality Score项目,以与社区集体讨论该问题,并与哈佛大学就开源普查工作进行合作。

目标9:关键软件无单方面更改

我们在Google上遵循的一个原则是,更改不应是单方面的,也就是说,每个更改都至少涉及作者,审阅者或批准者。目标是限制对手自己可以做的事情-我们需要确保有人实际上正在观察变化。对于开放源代码来说,要做到这一点实际上比在一家公司中要困难得多,后者可以拥有强大的身份验证并执行代码审查和其他检查。

  • 避免单方面更改可以分为两个子目标:

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

除了是改进代码的好方法之外,审阅还确保除了作者之外,至少还有其他人正在查看每项更改。代码审查是Google内部所有更改的标准做法。

目标9-2:关键软件的更改需要两个独立的缔约方批准

为了真正实现“有人在看”的目标,我们需要审稿人独立于撰稿人。对于重大更改,我们可能需要多个独立审查。当然,我们需要梳理什么才算是“独立”审查,但是独立性的思想对于大多数行业的审查至关重要。

目标10:对关键软件参与者的身份验证

任何独立性概念也都意味着您知道参与者-匿名参与者不能被认为是独立的或值得信赖的。今天,我们基本上有了化名:同一个人反复使用身份,因此可以享有声誉,但是我们并不总是知道个人的信任度。这导致了一系列子目标:

目标10-1:对于关键软件,所有者和维护者不能匿名

攻击者喜欢匿名。在过去的供应链攻击中,攻击者利用匿名性并通过程序包社区努力成为维护者,而没有人意识到这个“新维护者”有恶意(将破坏性源代码最终注入上游)。为了减轻这种风险,我们认为关键软件的所有者和维护者一定不能匿名。可以想象,与所有者和维护者不同,贡献者可以是匿名的,但前提是其代码已经通过了受信方的多次审核。还可以想象,我们可以拥有“已验证”身份,在该身份中,受信任实体知道真实身份,但出于隐私原因,公众却不知道。这将使有关独立性的决定以及对非法行为的起诉成为可能。

目标10-2:对关键软件提供者的强身份验证

恶意行为者正在寻找容易的攻击媒介,因此网络钓鱼攻击和与凭证相关的其他形式的盗窃是常见的。一种明显的改进是需要使用两因素身份验证,尤其是对所有者和维护者而言。

目标10-3:身份的联合模型

要继续保持开源的包容性,我们需要能够信任各种各样的身份,但仍需经过验证的完整性。这意味着身份的联合模型,可能类似于我们今天支持联合SSL证书的方式-多个组可以生成有效证书,但是具有强大的审核和相互监督的能力。

OpenSSF的数字身份认证工作组已开始对此主题进行讨论。

目标10-4:风险变更通知

我们应该扩大通知范围,以涵盖风险的变化。最明显的是所有权更改,这可能是新攻击(例如最近的NPM事件流危害)的序幕。其他示例包括发现凭证被盗,串通或其他不良行为者行为。

目标10-5:工件的透明度

通常使用安全的哈希值来检测工件是否已完好无损,并使用数字签名来证明其真实性。添加“透明度”意味着这些证明是公开记录的,因此可以证明其意图。反过来,即使用户不知道,外部各方也可以监视日志中是否存在伪造版本。更进一步,当凭据被盗时,我们可以知道使用这些凭据对哪些工件进行了签名并进行了删除。这种透明性,包括持久的公共日志和第三方监视,已被成功用于SSL证书,我们为包管理器提出了一种实现此目的的方法。知道您拥有正确的软件包或二进制文件类似于您正在访问网站的实际版本。

目标10-6:信任构建过程

肯·汤普森(Ken Thompson)的图灵奖(Turing Award)演讲在1984年著名地证明,仅靠真实的源代码还不够,最近的事件表明,这种攻击是一种真正的威胁。您如何信任自己的构建系统?必须通过建立信任的连续过程来信任和验证它的所有组件。

可复制的构建帮助-构建具有确定性的结果,因此我们可以验证我们做对了-但由于临时数据(例如时间戳记)最终发布在发布工件中,因此难以实现。安全的可复制构建需要验证工具,而验证工具又必须可验证且可复制地构建,依此类推。我们必须构建一个受信任的工具网络并构建产品。

对工件和工具的信任都可以通过“委托”,通过上述透明过程的一种变体(称为二进制授权)来建立。在内部,Google构建系统会对所有工件进行签名,并生成将其与源代码相关联的清单。对于开源,一个或多个受信任的代理可以将构建作为服务运行,对工件进行签名以证明他们对构建的完整性负责。这种生态系统应该存在,并且主要需要意识和关于证明格式的一些协议,以便我们可以安全地自动化流程。

一般而言,本部分中的操作非常适合于软件,目前在Google内部已广泛使用,但与开源相比,它们的工作量要大得多。我们的希望是,通过专注于关键的软件子集,我们至少可以针对该集合实现这些目标。随着工具和自动化的不断完善,这些目标将变得更容易被更广泛地采用。

总结一下:开源的本质要求我们通过共识和协作来解决问题。 对于诸如漏洞之类的复杂主题,这意味着需要围绕关键问题进行集中讨论。 我们提出了一种构架此讨论的方法,并定义了一系列目标,希望这些目标可以加快整个行业的讨论范围和最终解决方案。 第一组目标广泛应用于漏洞,实际上是关于实现自动化以及降低风险和工作量的目标。

但是,这些目标在存在对手或阻止“供应链”攻击时还不够。 因此,我们为关键软件提出了第二套目标。 第二组更加繁重,因此会遇到一些阻力,但是我们认为额外的限制对安全至关重要。 目的是共同定义“关键”软件包的集合,并将这些更高的标准仅应用于该软件包。

尽管我们对如何实现这两个目标有不同的看法,但在共识和可持续解决方案至关重要的领域,我们只是一个声音。 我们期待着这一讨论,推广最好的想法,并最终寻求能够增强和简化我们大家都依赖的开源安全性的解决方案。

原文:https://security.googleblog.com