关于安全软件供应链的最佳做法

开源代码无处不在。 它存在于许多专有代码库和社区项目中。 对于组织和个人来说,现在的问题不是你是否在使用开源代码,而是你在使用什么开源代码,以及使用多少。

如果你不知道软件供应链中的内容,当其中一个依赖项中的上游漏洞可能是致命的,你和你的客户就容易受到潜在的损害。 在本文档中,我们将深入探讨术语“软件供应链”的含义、为什么它很重要,以及如何通过最佳做法来帮助保护项目的供应链。

State of the Octoverse 2020 - 开放源代码

依赖项

术语“软件供应链”用于指进入软件中的所有内容以及它的来源。 它是软件供应链依赖的依赖项的依赖关系和属性。 依赖项是软件运行时需要的内容。 它可以是代码、二进制文件或其他组件,也可以是这些组件的来源,例如存储库或包管理器。

它包括代码作者、贡献时间、审查安全问题的方式、已知漏洞、受支持的版本、许可证信息,以及在整个过程中的任何时候接触到它的任何内容。

供应链还包括套件中除单个应用程序外的其他部分,如生成和打包脚本或运行应用程序所依赖的基础结构的软件。

漏洞

如今,软件的依赖关系非常普遍。 经常见到项目使用数百个开源依赖项来实现某种功能,而你不必亲自编写该功能。 这可能意味着大多数应用程序都包含并非你创作的代码。

State of the Octoverse 2020 - 依赖关系

第三方或开源依赖项中可能存在的漏洞多半是依赖项,因为你不能像自己编写的代码那样严格控制这些依赖项,可能会在你的供应链中产生潜在的安全风险。

如果这些依赖项中有一个存在漏洞,则很可能你也会遇到漏洞。 这可能很可怕,因为你的一个依赖项可能会发生更改,而你甚至都不知道。 即使依赖项中存在的漏洞现在不会被攻击,将来也会被攻击。

能够利用数千名开源开发者和库作者的工作成果意味着数千名陌生人可以有效地直接为你贡献生产代码。 通过产品供应链获得的产品会受到未修补漏洞、无意错误甚至对依赖项的恶意攻击的影响。

供应链漏洞

供应链的传统定义来自于制造业;它是制造和供应某物所需的过程链。 它包括规划、材料供应、制造和零售。 软件供应链类似,但不是供应材料,而是供应代码。 它不是制造,而是开发。 代码不是从地下挖矿石,而是由供应商提供,分为商业来源和开放源,而一般情况下,开源代码来自存储库。 从存储库添加代码意味着产品依赖于该代码。

举个软件供应链攻击的例子:当恶意代码被故意加入依赖项中,使用该依赖项的供应链将代码分发给受害者时,就会发生这种攻击。 供应链攻击是真实存在的。 攻击供应链的方法有很多:从直接以新贡献者的身份插入恶意代码到在别人不注意的情况下接管贡献者的帐户,甚至破解签名密钥来分发不属于正式依赖项的软件。

软件供应链攻击本身很少是最终目标,但提供了一个让攻击者插入恶意软件或为将来的访问提供后门的机会。

State of the Octoverse 2020 - 漏洞生命周期

未修补软件

当今开源代码的使用非常普遍,而且预计不会很快放缓。 假设我们不打算停止使用开源软件,则提供链安全性的威胁是未修补软件。 知道这一点,你如何解决项目的依赖项有漏洞的风险呢?

  • 了解环境中的内容。 这需要发现依赖项和任何过渡性依赖向,以了解这些依赖项的风险,如漏洞或许可限制。
  • 管理依赖项。 发现新的安全漏洞时,必须确定你是否受影响,如果是,则更新到可用的最新版本和安全修补程序。 这对于审查引入新依赖项的更改或定期审计旧的依赖项尤为重要。
  • 监视供应链。 这是通过审核已有的控件来管理依赖项。 这将帮助你为依赖项强制实施更严格的条件。

State of the Octoverse 2020 - 顾问

我们将介绍 NuGet 和 GitHub 提供的各种工具和技术,你现在可以使用它们来解决项目中的潜在风险。

了解环境中的内容

具有已知漏洞的包

📦 包使用者 | 📦🖊 包作者

.NET 8 和 Visual Studio 17.8 添加了 NuGetAudit,它将在还原期间针对包含已知漏洞的直接包发出警告。 .NET 9 和 Visual Studio 17.12 也更改了默认值,针对传递包发出警告。

NuGetAudit 要求源提供已知的漏洞数据库,因此,如果不将 nuget.org 用作包源,则应将其添加为审核源

在 NuGet 发出警告之前,公众就已知道该漏洞。 攻击者可以使用此公开披露针对尚未修补其应用程序的目标开发攻击。 因此,当收到项目正在使用的包有已知漏洞的警告时,应赶快采取措施。

NuGet 依赖项关系图

📦 包使用者

可以通过直接查看相应的项目文件来查看项目中的 NuGet 依赖项。

一般在以下两个位置中可以找到:

根据你用来管理 NuGet 依赖项的方法,还可以使用 Visual Studio 在解决方案资源管理器NuGet 包管理器中直接查看依赖项。

在 CLI 环境中,可以使用 dotnet list package 命令列出项目或解决方案的依赖项。 还可以使用 dotnet nuget why 命令来了解项目包图中包含传递包(项目未直接引用的包)的原因。

有关管理 NuGet 依赖项的详细信息,请参阅以下文档

GitHub 依赖项关系图

📦 包使用者 | 📦🖊 包作者

可以使用 GitHub 的依赖项关系图来查看项目依赖的包以及依赖于该项目的存储库。 这可以帮助你查看其依赖项中检测到的任何漏洞。

有关 GitHub 存储库依赖项的详细信息,请参阅以下文档

依赖项版本

📦 包使用者 | 📦🖊 包作者

为了确保依赖项供应链的安全,你需要确保所有依赖项和工具定期更新到最新稳定版本,因为这些版本通常会包含最新功能和已知漏洞的安全修补程序。 你的依赖项可以包括依赖的代码、使用的二进制文件、使用的工具以及其他组件。 这可能包括:

管理依赖项

NuGet 已弃用且有漏洞的依赖项

📦 包使用者 | 📦🖊 包作者

可以使用 dotnet CLI 列出项目或解决方案中的任何已知的已弃用或有漏洞的依赖项。 可以使用命令 dotnet list package --deprecateddotnet list package --vulnerable 获得任何已知的弃用功能或漏洞的列表。 NuGetAudit 可以针对已知易受攻击的依赖项发出警告,并在源提供漏洞数据库时默认启用。

GitHub 有漏洞的依赖项

📦 包使用者 | 📦🖊 包作者

如果项目托管在 GitHub 上,则可以利用 GitHub 安全性在项目中查找安全漏洞和错误,Dependabot 将通过针对你的代码库打开拉取请求来修复它们。

在引入有漏洞的依赖项之前将其捕获是“左移”的一个目标。 能够获得有关依赖项的信息(例如许可证、过渡性依赖项和依赖项的存在时间)可帮助你做到这一点。

有关 Dependabot 警报和安全更新的详细信息,请参阅以下文档

NuGet 源

📦 包使用者

使用信任的包源。 当使用多个公共和专用 NuGet 数据源的源时,可以从任何源下载包。 若要确保你的生成是可预测和安全的,且不受已知攻击(如依赖项混淆)的影响,最佳做法是了解包来自哪些特定的源。 可以使用具有上游追溯功能的单个源或专用源来提供保护。

若要详细了解如何保护包源,请参阅使用专用包源时降低风险的 3 种方法

使用私有源时,请参阅管理凭证的安全最佳做法

客户端信任策略

📦 包使用者

你可以选择采用某些策略,以要求签署你所使用的包。 这样,只要包具有作者签名,你就可以信任包作者,或者如果包由 NuGet.org 进行存储库签名的特定用户或帐户拥有,就可以信任该包。

若要配置客户端信任策略,请参阅以下文档

锁定文件

📦 包使用者

锁定文件存储包内容的哈希。 如果要安装的包的内容哈希与锁定文件匹配,则将确保包的可重复性。

若要启用锁定文件,请参阅以下文档

包源映射

📦 包使用者

通过包源映射,可以集中声明解决方案中每个包应从哪个源还原到 nuget.config 文件中。

若要启用包源映射,请参阅以下文档

安全计算机

目录权限

📦 包使用者

在 Windows 和 Mac 以及有些 Linux 分发版上,用户帐户主目录默认为专用。 但是,有些 Linux 分发版默认情况下允许同一计算机上的其他帐户读取用户目录。 此外,还有多个配置选项可将 NuGet 的全局包文件夹和 HTTP 缓存重定向到非默认位置。 解决方案、项目和存储库也可在用户的主目录之外创建。

当使用任何不在 nuget.org 上的包时,如果计算机上的任何其他帐户都可以读取 NuGet 的全局包或 HTTP 缓存目录,或项目的生成输出目录,则这些包可能会泄露给不应访问这些包的人员。

在 Linux 上,dotnet nuget update source 将更改 nuget.config 文件权限,使其仅可由文件所有者读取。 但是,如果以任何其他方式编辑 nuget.config 文件,且该文件位于其他帐户可以读取该文件的位置,则可能存在有关包源 URL 或包源凭据的信息泄漏。 应当确保同一计算机的其他用户无法读取任何 nuget.config 文件。

下载目录内的解决方案

📦 包使用者

如果处理下载目录内的解决方案或项目,应格外小心。 NuGet 将从多个配置文件累积设置,MSBuild 通常会从任何父目录直到文件系统根目录中导入 Directory.Build.propsDirectory.NuGet.propsDirectory.Build.targets 和其他文件。

下载文件夹有额外的风险,因为它通常是 Web 浏览器从 Internet 下载文件的默认位置

生成代理

📦 包使用者

在每次生成后未重置为初始状态的生成代理(CI 代理)有多种必须考虑的风险。

要了解管理凭据的安全方法,请参阅有关从经过身份验证的源使用包的文档

要了解如何修改 NuGet 存储数据的目录,请参阅有关管理全局包、缓存和临时文件夹的文档。 这些目录应配置为 CI 代理在每次生成后清理的目录。

请注意,项目使用的任何包都可能留在项目的生成输出目录中。 如果项目使用来自经过身份验证的源的包,则同一 CI 代理的其他用户可能会获得对该包程序集的未经授权的访问权限。 因此,即使生成失败或取消,在生成结束时也应当清理存储库。

监视供应链

GitHub 机密扫描

📦🖊 包作者

GitHub 会扫描 NuGet API 密钥的存储库,以防止欺诈性地使用意外提交的机密。

若要详细了解密钥扫描,请参阅关于密钥扫描

作者包签名

📦🖊 包作者

作者签名允许包作者在包上加盖标识,方便使用者认出这是你创作的包。 这可以保护内容不被篡改,并充当确定包来源和包真伪的唯一真实源。 与客户端信任策略结合使用时,可以验证包是否来自特定的作者。

要创作包签名,请参阅对包进行签名

可重现生成

📦🖊 包作者

可重现生成在你每次生成时都创建字节对字节完全相同的二进制文件,并且包含源代码链接和编译器元数据,包使用者可使用这些内容直接重新创建二进制文件并验证生成环境是否未被侵害。

若要详细了解可重现生成,请参阅使用源链接生成包可重现生成验证规范。

双因素身份验证 (2FA)

📦🖊 包作者

nuget.org 上的每个帐户都已启用 2FA。 在登录到 GitHub 帐户NuGet.org 帐户时,此操作增加了额外的安全层。

包 ID 前缀预留

📦🖊 包作者

为了保护包的标识,可以使用各自的命名空间保留与匹配的所有者关联的包 ID 前缀,前提是包 ID 前缀符合指定标准

若要了解有关保留 ID 前缀的信息,请参阅包 ID 前缀保留

弃用和下架有漏洞的包

📦🖊 包作者

为了保护 .NET 包生态系统,当你发现自己创作的包中存在漏洞时,请务必将该包弃用并下架,以防用户搜到该包。 如果你正在使用已弃用且下架的包,应避免使用该包。

若要了解如何将包弃用和下架,请参阅以下有关将包弃用下架的文档。

另请考虑向 GitHub 顾问数据库报告已知信息。

总结

软件供应链是进入或影响代码的任何内容。 即使供应链漏洞真实存在且发展迅速,它们仍非常罕见;因此,你可以做的最重要的事情就是通过了解依赖项、管理依赖项和监视供应链来保护供应链

你已经了解 NuGet 和 GitHub 提供的用于更高效查看、管理和监视供应链的各种方法。

有关保护全球软件的详细信息,请参阅 State of the Octoverse 2020 安全报告