编译:奇安信代码卫士团队

近日,GitHub 发布 Octoverse 安全报告,如下是对该报告的节选编译。

概要

开源是很多信息经济的结缔组织。如今很难找到数据未通过至少一个开源组件传递的场景。我们所依赖的很多服务和技术,如银行、医疗等也依赖于开源软件。开源代码工件是很多全球经济的关键基础设施,因此开源软件在全球起着至关重要的作用。

作为全球最大的开源平台,GitHub 在分析开源软件依赖关系和漏洞对这些依赖关系所起的作用以及警报用户大规模解决漏洞方面,具有得天独厚的优势。GitHub 对该平台上漏洞报告、警报和修复的思考有助于其找到开源安全方面的重要趋势。

本报告全方位说明了开源安全和漏洞生命周期的情况,找到了我们所拥有的关键机遇,以及社区在提升开源安全方面能够做出的努力。同时也找到了软件团队能够集中资源改进的地方。

1、采取行动

1.定期检查依赖关系中的漏洞。

第一步是了解,我们无法修复自己不了解的漏洞。虽然很少有人会为私有仓库启用警报机制,但这样做会让我们面临威胁。启用自动化警报机制,企业和开源项目可跟进安全漏洞、信息和补丁情况。

2.如果你在安全团队,那么参与到社区中来。

开源是关键的基础设施,我们都应当为开源软件的安全贡献力量。其中一种方式是查找所使用开源代码中的漏洞并告知维护人员。另外一种方式是使用 CodeQL 搜索自己的代码中是否存在漏洞,之后将查询结果分享并帮助他人。

3.采用自动化工具修复漏洞,维护安全。

使用自动化警报和修复工具快速加固软件的安全,因为攻击面不断扩展,这样做会使得攻击者难以利用漏洞。自动生成 pull 请求以更新易受攻击依赖关系的仓库修复软件的速度比不使用自动化工具的仓库快1.4倍。通过将安全实践自动化,开发人员和社区共享专业知识,破除安全和工程孤岛并将专业知识规模化。

4.快速修复漏洞并将代码库更新至最新状态。

尽早修复软件并经常通过已知安全修复方案打补丁。延迟修复可面临漏洞遭利用的风险,而且可能会为未来打补丁(依赖于之前更新)带来困难。此外应该及时将代码库更新到最新状态,从安全更新和社区专业知识方面获益。些许的延迟很快将积攒为数年之久,而落后会在补丁的可用性方面(最常用的依赖关系版本可能是最安全的,而不常用的版本使用的人也更少)、维护方面(老版本的开源支持更少,因此你不得不亲自动手维护)甚至是招聘(没人愿意应付不具备现有支持或示例的过时版本)方面带来大不相同的结果。

2、报告数据

本部分数据源自 GitHub 的依赖关系安全功能和所支持的六个软件包生态系统。比对日期是2019年10月1日至2020年9月30日与2018年10月1日至2019年9月30日。

本节分析基于符合如下标准的超过4.5万个仓库:

  • 使用六个受支持软件包生态系统中的一个

  • 均为活跃的仓库,即在2018年10月至2020年9月期间,至少有一次贡献。也就是说,只有在两年时间内活跃的仓库才会涵盖在内,因此排除了新的仓库。

  • 启用了依赖关系图表,即默认启用该内容的公开仓库

  • 非 fork,未被归类为垃圾内容,或非 GitHub 员工所有

本报告中包括的软件包生态系统和它们所表示的语言如下:

在本报告中,我们引用了软件包生态系统。软件包生态系统是以一致方式打包的库集合,目的是使它们易于复用。多数程序语言都有一个单一的软件包生态系统,即使它们拥有一个以上的软件包管理器也不例外。

本报告包括所列软件包生态系统的数据。例如,分析并不包括使用 Gradle 包管理器的 Java 仓库数据,或者使用 Poetry 或 Conda 的 Python 仓库数据,不过我们仍然能够获得关于开源软件安全和最佳实践的有趣且有意义的观点。

开源软件安全

虽然开发人员担心会引入安全缺陷,但只要你编写代码或者添加新的依赖关系,就会存在这种风险。它的反面状况也是一种风险:稳定的代码和过时的依赖关系意味着攻击者可以有时间利用已有的每个漏洞,有条不紊地攻击系统。由于恶意攻击利用代码中的缺陷,因此开发人员正在通过积极的检测和自动化方法阻止或限制漏洞对生产环境造成的影响。要取得成功,我们应当重视代码中的所有漏洞:我们编写的代码和所依赖的开源软件中的漏洞。

3、攻击面

安全漏洞可直接影响软件或者通过依赖关系(被引用和绑定以使软件包工作的任意代码)造成影响。也就是说代码易受攻击或者是因为它本身包含漏洞,或者是因为它依赖的依赖关系中包含漏洞。在当代软件中,80%及以上的多数应用的代码源自依赖关系,因此我们来了解下软件包生态系统及其一般的依赖关系特征。

简单地说,漏洞是可被攻击者利用的任意弱点,可能包括内部控制、安全程序、实现和计算机系统中的缺陷。本报告分析的漏洞是指可通过软件遭利用的漏洞。

首先,我们说明了引用了至少一种开源依赖关系的仓库的比例。我们发现:

最频繁使用开源软件的是 JavaScript (94%)、Ruby (90%) 和 .NET (90%)。我们注意到 Java 所占比例较少是因为我们所分析的数据集中并未包含使用 Gradle 作为包管理器的依赖关系信息。这也是我们所预料到的结果。

在每个仓库中,我们都检查了每种包生态系统中的依赖关系数量。在查看直接依赖关系时,我们发现在所有的仓库中,从所包含的依赖关系中位数角度来看,JavaScript 最多 (10)、其次是Ruby 和 PHP (9)、Java (8) 以及 .NET 和 Python (6)。这表明各种语言中的直接依赖关系的中位数存在一些差异,但差异并不大。

但直接依赖关系并非事实的全部。每种直接的依赖关系本身可以拥有依赖关系,而后者也可能也有依赖关系,等等。我们将所有非“直接的”依赖关系称为“可传递依赖关系”。对于包含以 “.lock” 或 “-lock.json” 格式存在的(lockfile)可传递依赖关系详情的语言以及包含lockfile 的仓库,JavaScript 包含的中位数依赖关系最多,为683个,其次是 PHP (70)、Ruby (68) 和 Python (9)。(注:包含 JavaScript (npm)、Ruby (RubyGems)、PHP(Composer) 和 Python (使用 Pipenv的仓库),但不包含 Java (Maven) 或 .NET (NuGet) 生态系统的数据)。

注意,我们没有 Java 和 .NET 的可传递依赖关系数据,可传递依赖关系图表示的是仅针对包含 lockfile 仓库的中位数。

4、恶意还是错误:bug门和后门

后门是指被故意植入软件中便于利用的软件漏洞;bug 门是指某种特定类型的后门,它们将自己伪装成便于利用但难以发现的 bug,而不是引入直接恶意行为。

后门模棱两可的本质使它们难以具备漏洞的资格,而建立意图也极具挑战性。良好的后门和正常的编程错误很难区分开。因此,我们需要依靠其它指标来判断可疑后门事件的意图。

后门最明显的指标是攻击者获得对软件包源代码仓库的 commit 访问权限,而这种权限通常通过账户劫持实现,如2018年发生的 ESLint 攻击事件:攻击者使用一个受陷软件包窃取用户的 npm 包注册表凭据。抵御这些后门尝试的最后一道防线是在开发管道中尤其是对新 committer 的变更进行仔细的同行评审。很多成熟的项目都具有这种仔细的同行评审机制。攻击者也了解这一点,因此他们通常试图在发行点攻击不在版本控制范围内的软件,或者通过typosquatting 软件包名称的方式,诱骗人们取走恶意代码版本。

对这六个生态系统中的随机样本(521个安全公告)的分析表明,17%的安全公告与直接恶意行为如后门尝试有关。在这17%中,多数源自 npm 生态系统。虽然17%的恶意攻击将引发安全关注,但编程错误引入的漏洞同样具有破坏性,而且更可能对流行的项目造成影响。从 GitHub 向开发人员发出的所有提醒他们注意依赖关系中漏洞警报信息来看,仅有0.2%的漏洞与直接恶意活动有关。也就是说,多数漏洞是由编程错误造成的。

维护开源信任的挑战很大一部分在于向下游客户保证生态系统中代码的完整性和连续性,而在这个生态系统中,自愿commit 是常态。这就要求我们更好地理解项目的贡献图表、一致的同行评审、commit 和发布签名以及通过多因素认证机制执行账户安全性检查。

5、新增安全公告

漏洞是通过安全公告报告的,而这些公告可从公开数据库中找到。它有助于开发人员和开源维护人员集中提供关问题、修复方案、补丁和更新信息,保护软件的安全。

我们可看到,npm 和 Maven 的安全公告数量在 GitHub 安全公告数据库中所占的比例最大,分别是 26% 和 23.8%,而 NuGet 所占比例最小,仅为1.2%。但从漏洞的严重性角度来看,并非所有的安全公告都生而平等。

6、其它数据:安全公告

到了这个分析阶段,我们还包含了额外的数据来源:GitHub 安全公告数据库 (Advisory Database)。该数据库包含精选的安全漏洞列表,已被映射到由 GitHub 依赖关系图追踪的软件包。

本报告中的安全公告有两个来源:外部生态系统(占所分析安全公告的54%)和由维护人员报告的 GitHub 安全公告(发布于2019年5月,占比46%)。外部生态系统包括美国国家漏洞数据库 (NVD)、RubySec、FriendsOfPHP 和其它偶然使用的来源。GitHub 仔细验证了第三方发布的安全公告以及GitHub 安全公告数据库中包括的由维护人员发布的安全公告。我们评估了漏洞的严重性,证实了受影响版本的范围并查看了修复建议。

GitHub 安全公告可使维护人员直接在 GitHub 上描述、修复并发布代码中的漏洞情况。GitHub 审计所有已发布的安全公告,并在时机合适时,通过CVE编号发布机构的身份为这些漏洞分配 CVE 编号。这样就能够使其发布到NVD 中并对全球软件社区开放。

从中可看到,在严重和高危等级的安全公告中,npm 占比(分别为23和66个)最大,Maven 在中危级别占比最高(86)。RubyGems 并未出现在严重级别的安全公告中,而从整体来看,和 NuGet 相关的安全公告数量最少。

7、如何对漏洞进行评分

除了相对较小比例的漏洞已知已遭在野利用外,“严重性”是一个有点主观的概念。通常来讲,严重性评分是由查看了可用信息并做出判断安全专家分配的。有助于标准化的常用工具是 CVSS 评分。CVSS 3.1 中定义了四种严重等级:低危、中危、高危和严重。漏洞级别取决于多种因素如利用漏洞的难度以及成功利用后的影响。

如果漏洞位于广泛使用的应用中且起作用的 PoC 已存在,那么对漏洞进行准确评分相对容易。但如果不存在 PoC 或者该漏洞位于库中(说明漏洞的可利用性取决于库的使用方法),那么评分系统就变得更具主观性了。和所有的 CVE 编号发布机构一样,GitHub 致力于按照 CVSS 评分客观评估漏洞的严重程度。同时,我们还主动和 NVD 在 CVMAP 进程上进行协作。该进程通过美国政府安全研究员客观地评估提交给 NVD 的所有漏洞的安全评分。

8、安全警报

漏洞修复进程的重要部分是了解并追踪安全库存,匹配安全公告,并在出现相关漏洞时发布警报信息。这就涉及识别易受攻击的组件以及相应漏洞,以便团队可采取正确的措施,确保代码的安全性。

在当前分析阶段,我们新增了其它数据源:Dependabot。Dependabot 默认会发出警报,提醒开发人员关于公开仓库中易受攻击的依赖关系,而开发人员可能会取消勾选这一默认选项。与之相反,私有库会选择勾选这一选项,而开发人员必须在个人或组织机构级别启用 Dependabot 警报选项。鉴于此,并非所有仓库都会获得警报信息。在本分析中,我们抓取了发送给开发人员的警报信息。

总体而言,具有受支持包生态系统的活跃仓库收到安全警报的概率是59%。按照包生态系统来划分,最有可能收到警报信息的仓库使用的是 RubyGems (81%) 和 npm (73%)。

从按照严重程度划分的安全公告和按照严重程度划分的自动化警报来看,它们和之前从GitHub 安全公告数据库中看到的安全公告比例之间存在一些不同之处。最大的不同集中于低危(24%,高于安全公告数据库)和高危(2%,低于安全公告数据库)等级。这说明用户收到了不成比例的低危漏洞警报信息,尽管这可能说明的是,由于没有充分区分,罕见的更严重的警报淹没在噪音中。它也说明最严重的漏洞并不见于广泛使用的组件中,因此从一开始影响的用户就更少。

为了查看安全警报在各软件包生态系统中的分布情况,以及它们和安全公告数据库中的安全公告之间的区别,我们展示了这两种分布情况。

9、每种软件包生态系统中的警报

去年,我们划分了每种软件包生态系统中的警报数量,并发现依赖关系中出现的漏洞的严重程度与对该依赖关系的使用情况之间并非特别相关。

在过去的12个月中,从最多使用的 npm 软件包中找到的漏洞为中低危级别。我们很可能就此得出严重性和流行度之间存在负相关关系的结论,很可能会得出更多人检查会让漏洞更容易发现的结论。然而,快速浏览 Composer 警报的严重性分布情况就能发现这些都是误解;它最大的软件包中含有严重和高危漏洞。真相似乎是,严重漏洞逃过代码评审的容易程度和低危漏洞一样。

10、开源漏洞的生命周期

安全漏洞是软件开发和交付的一个重要组成部分,应用安全专业人员帮助团队和组织机构守护代码和系统的安全。本章节,我们将查看漏洞的生命周期并展示最佳实践如何有助于更快地修复漏洞,从而得到更安全可靠的软件。

开源漏洞修复的四个步骤如下:

1.找到并报告漏洞。

2.维护人员修复漏洞并发布新版本。

3.安全工具向用户发出安全更新警报。

4.开发人员更新至最新版本。

开源社区中的任何人(虽然一般是安全研究员)都可查找漏洞(第一步)。之后,维护人员创建修复方案并发布安全更新(第二步)。维护人员或安全研究员请求获得 CVE 编号而安全工具将其增加到数据库后,依赖关系的终端用户收到通知(第三步)。之后,终端用户更新代码,使用新发布的修复版本(第四步)。

11、漏洞的完整生命周期

在发现漏洞前,人们一般需要218周(比四年稍长)才能检测到漏洞。之后社区需要花费4.4周才能找到该漏洞并发布修复方案,之后需要10周才会发布关于安全更新可用的警报信息。我们发现,对于应用该修复方案的仓库而言,一般需要一周的时间解决漏洞。而将精力集中于检测所需时间,我们就有机会缩短漏洞的生命周期。这说明了两点:将精力集中于检测的重要性以及当前的开源软件中可能存在大量未发现的漏洞。如果开发阶段不断引入漏洞,那么发现漏洞的速度就会大大落后于引入的速度。

漏洞的情况因软件包生态系统、安全公告数据库和安全团队发布警报和修复所使用方法的不同而不同。我们将详细调查每个步骤,分析将集中于数据可用的 RubyGems 和 npm。

12、2020年最糟糕的漏洞

截止到2020年11月最糟糕的漏洞是什么?这取决于你如何定义“糟糕”。一些明显的候选者是 CVE-2020-0601(即 Curveball)、CVE-2020-0796(即 SMBGhost) 和 CVE-202-1472(即 Zerologon)。这些漏洞从所影响的开发人员数量及其对易受攻击网络和端点产生的潜在影响来看,是严重的。它们可能是最糟糕的,因为它们是严重的漏洞,需要系统管理员紧急注意。

但糟糕的另一种定义是,对项目维护人员产生的影响最大。按照这个定义来看,今年影响力最大的漏洞的强劲候选者是 CVE-2020-8203(Iodash 中的原型污染漏洞)。该漏洞直接造成超过500万次的 Dependabot 警报。这是因为lodash 是使用最广泛的 npm 包之一。此外,原型污染是一个潜在严重漏洞,在最糟糕的情况下可导致 zipObjectDeep 方法所使用的地方出现远程代码执行后果。强烈建议开发人员更新至 lodash 的最新版本。

13、找到并修复漏洞

漏洞被引入生态系统与研究人员和维护人员找到修复方案之间的时间一般要经过七年(RubyGems)和五年 (npm)。这是因为软件漏洞通常无法被注意到和检测到。此外,很多团队可能会缺乏专业知识或者没有时间从代码中找出漏洞,而是集中于开发核心功能。

从严重程度来看,严重漏洞披露的速度更快。虽然它明显是个好消息,时间线更快的原因尚无法确定,需要进行进一步的研究。

14、关于安全更新可用的警报

对于任何安全专业人士及更宽泛的 DevOps 团队而言,发布警报的时间是重要组成部分。和自己找到漏洞不同,收到关于易受攻击依赖关系的警报是响应的第一次机会。一旦发现修复方案,团队就可修复任何易受攻击的代码并升级受影响系统。

分析所使用的警报机制和数据源自 GitHub 安全公告数据库和 Dependabot 警报。

发出警报所需的时间因安全公告来源的不同而不同:是从外部来源导入还是直接提交给 GitHub 安全公告数据库?差别源自用于提交安全公告的流程。由于 GitHub 安全公告数据库直接接收所提交的报告,因此维护人员甚至能在修复方案准备好之前就起草好安全公告,并从 GitHub 直接获得 CVE 编号。发布后,警报就可更快地发出,通常会在一周内发出。相比之下,导入的安全公告在最终进入中心仓库前必须经历其它渠道,从而可能造成延迟。这些其它渠道可能要求经历多个中间步骤,依次是获得 CVE 编号、发布修复方案、提交给 NVD。这种警报的发发布明仓库安全公告的警报发布更快,而导入的安全公告一般会在20周后发布,长尾效应更明显。

15、安全更新修复

如果软件是一部童话,那么团队收到警报、安装补丁之后,系统就会安全。但软件是复杂的,而且知道补丁的存在只是修复刚刚开始的第一步。提早并经常修复是最佳安全战略,但并非总是一种可选项。团队可能需要等待,以确保不会干扰运营;或许一次有太多的补丁需要融合;或者必须先应付尚不支持补丁的功能或遗留平台。

了解到修复安全漏洞并非一路通畅,我们调查了修复所需时间。该分析包含了确实解决了警报的任意仓库的警报解决时间。

在所有仓库中,一天内RubyGems 和 npm 警报的解决率接近20%。随着时间的流逝,该解决率不断提升,在自警报发布的一个月内达到约30%。

开发人员对更严重的问题响应速度更快,但差距并不巨大:所有严重等级漏洞的警报一天内的解决率接近20%。随着时间的流逝,该解决率不断提升,在自警报发布的一个月内达到约30%。

自动化让软件更安全

软件变得更安全了吗?我们在警报处理方面变得更好了吗?团队在解决已有漏洞方面变得更好了吗?这些问题很难回答,其中一方面的原因是软件数量不断增长且软件不断发生变化。

随着我们构建新功能和维护基础设施,软件和系统也在变化,也就是说我们的攻击面也在不断变化。这就使得攻击者保持积极主动并随时做好准备,因为它们之前利用的面可能会随时被新功能所替代或修复。与此同时,团队和项目加入新人,并且正在学习如何保护软件和系统的安全。

自动化,如通过Dependabot 自动更新依赖关系版本,为开发人员提供了保护代码安全的另一个机会。通过将安全实践自动化,开发人员和社区共享专业知识,破除安全和工程孤岛并将专业知识规模化。它使得开发人员能够在提供关键信息安全持续性的同时探索更多机遇。团队可利用更广的信息安全社区的力量找到并修复代码库中的安全漏洞。

很多开发人员使用开源软件更快地创建并构建项目。DORA发布的研究报告指出,精英绩效者要比低绩效者更可能使用开源软件,可能性是后者的1.75倍。

虽然有些人担心开源代码可能会存在看不到的依赖关系和漏洞,但安全性是和软件打交道时总要遇到的问题。分析表明,发现的潜在漏洞数量随着所编写的代码行数增多而增多。开源的力量和承诺掌握在社区手中。携手数百万开发人员不仅构建软件包,而且还更快速地构建软件并修复漏洞,我们就可以更快更安全地构建软件。关键在于利用自动化警报机制和打补丁工具来快速保护软件安全。

我们现在在代码中引入的漏洞数量要比过去少吗?对开源仓库 commit 的分析表明,并非如此。

对某项目的历史 commit 进行静态分析后,我们可以看到新的潜在漏洞是何时被引入的。我们对五年间数千个开源项目的每个 commit 运行了GitHub 的静态分析安全工具 CodeQL,查看漏洞被引入的速度是否有所改变。结果如图所示,它表明2020年编写的一行代码引入漏洞的可能性和2016年并无不同。

16、漏洞修复自动化:左移

DevSecOps 专业人员赞美 “左移“ 是一种超能力,指出将安全构建到开发进程放大了信息安全专业人员的专业知识。但这些团队如何左移并在安全中构建?

DORA 发布的研究报告指出,自动化使团队能够将安全融入开发进程中,是高绩效的预测器。我们自己所开展的分析发现,自动生成 pull 请求以更新已修复版本的仓库在33天内修复软件,要比未使用自动化的团队快13天,或快1.4倍。使用自动化是一种重要的最佳实践:对 pull 请求进行自动化并对安全补丁进行广泛的可持续集成检查的团队表示它们对于快速更新至关重要。

Sonatype 公司也发现,绩效好的软件开发团队更可能(4.9倍)成功地更新依赖关系并修复漏洞且不发生崩溃。

软件安全有赖于每个人。而付出会有回报:具有良好的自动化和打补丁实践使得我们更容易并更安全地将修复方案集成到开发工作中。

原文链接

https://octoverse.github.com/

声明:本文来自代码卫士,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。