前段时间和朋友聊OSS供应链安全。在谈话中,我提到与我的 GitHub 或 PyPI 账户相比,我更愿意让我的银行账户被盗。
由于 urllib3 和其他基础软件包每天收到的下载量巨大,任何威胁都会广泛传播并具有破坏性。
想想就害怕!😱
我们已经单独和作为一个团队采取了许多预防措施,以确保这种情况不太可能发生。我希望我在此过程中获得的一些知识可以帮助您保护自己的软件包,并激发一些具有对抗性安全意识的思维。
实施本文中描述的安全实践需要时间、精力,有时甚至是金钱!如果您的组织使用开源软件但不关心依赖项和维护者的安全实践,那么您就是在赌组织的安全性。
对维护者的财务支持对于确保您的依赖关系得到维护、保持最新并使用安全最佳实践大有帮助。有许多选项可以支持GitHub Sponsors、Tidelift和Open Collective等维护者。
如果一个项目对您的业务特别重要,您还可以考虑雇用一名或多名维护人员,并分配他们的一些时间来维护该项目。这是我目前对Elastic和 urllib3 的安排。
本文重点介绍保护帐户和包存储库:
我计划在 GitHub 上发布未来的文章和示例项目,其中介绍如何安全地配置项目和部署管道。如果您想在该文章发表时得到通知,您可以通过电子邮件或RSS 订阅。
如果您是包维护者,您的帐户可能非常有特权,包括有权将代码推送到项目、发布包的新版本或管理多个平台上的帐户访问。出于这个原因,帐户安全应该是您的首要考虑。
即使你不是包维护者,很多这些建议也适用于你!更好的安全性对每个人来说都是一件好事。
由于密码重置,您在平台上用于帐户的电子邮件地址很重要,以至于电子邮件地址的所有权几乎与使用该电子邮件地址的帐户的所有权同义。
您的主要电子邮件帐户应该来自一个已经存在很长时间并且非常重视安全性的提供商。我的建议是Gmail或Outlook。
我不建议将个人域用于电子邮件。个人域名不会永远存在,如果您停止向注册商付款,该域名将重新投放市场,这意味着购买该域名的任何人都可以收到与他们关联的帐户的密码重置。我gmail.com
比任何个人域名都更有信心。
例如,如果您有一个个人域,但想将该域用作“虚拟地址”并将电子邮件转发到您的 Gmail 帐户,那很好!但是,不要将您的个人域电子邮件地址用于您的帐户,而是使用直接转发到的电子邮件地址。
双重身份验证(缩写为 2FA)不仅需要您的用户名和密码登录,通常是智能手机或其他物理设备,因此极大地提高了您的帐户安全性。正确完成 2FA 后,对手几乎不可能在没有物理访问您的 2FA 设备的情况下登录您的帐户,即使使用您的密码也是如此!
有很多 2FA 方法可用,我个人使用并推荐 Google Authenticator。
我坚信每个涉及提交代码或发布包的帐户都应该至少启用非 SMS 2FA。这包括您的电子邮件提供商、项目主机和包存储库的帐户。否则就是放弃最近历史上对帐户安全性的最佳改进之一。
推动帐户所需的 2FA 已经在进行中:NPM 最近宣布前 500 名软件包的所有发布者都必须使用强制 2FA。我衷心希望 Python 和其他生态系统能效仿,继续推动 2FA 作为向平台发布代码的要求。
知道你的密码是如此的 2000s!— 使用密码管理器是确保密码唯一且安全的最简单方法。养成为所有帐户使用一个用于开源工作的习惯。
我认识到进行切换可能有点令人生畏,尤其是因为您可能习惯于“知道”您的密码。尝试几个小时,我保证您不会错过在几次登录后输入您的密码。如今,密码管理器更加用户友好,并集成到移动设备和浏览器中。
尝试过多个不同的密码管理器后,我更喜欢BitWarden,它是开源的,支持云同步的免费层。还有其他免费的替代品,例如KeepassXC(它也是开源的)和我过去使用过的LastPass 。
大多数移动 2FA 应用程序使用基于时间的一次性密码(TOTP) 算法,该算法本质上是服务器和 2FA 设备之间的共享机密。还有另一种称为Webauthn的算法,通常通过硬件密钥使用。Webauthn 使用非对称密钥而不是共享的对称密钥进行身份验证。
比较 TOTP 和 Webauthn:
对于被动帐户安全来说,两者都“足够好”,但由于前面的原因,有针对性的网络钓鱼更有可能成功对抗 TOTP 2FA。如果您对 2FA 方法之间的差异更感兴趣,可以阅读为 PyPI 实现 2FA 的开发人员的这篇文章。
硬件密钥的唯一缺点是它们要花钱,所以如果你有一些额外的现金,你可以投资购买硬件密钥。我使用并推荐 Yubikey 作为硬件键,根据您的用例,有很多选项可供选择。我个人使用的是Yubikey 5 NFC,价格约为 50 美元,另一种选择是Google 的硬件密钥,价格为 30 美元。
SMS 2FA 是一种 2FA 方法,您可以在其中接收带有验证码的 SMS 文本。我不推荐使用 SMS 2FA 的原因有两个:
SIM 卡劫持:多次发生恶意行为者使用社会工程将电话号码重新分配给另一张 SIM 卡。这称为“SIM 劫持”,它通过允许攻击者接收包含您的 2FA 代码的文本来绕过 SMS 2FA。
方便:我个人发现使用身份验证应用程序比短信体验要好得多。您不必等待短信到达,然后手动复制代码。打开应用程序、单击和粘贴要容易得多。
请记住,如果您之前在您的帐户上使用过 SMS 2FA,则需要明确禁用 SMS 2FA,因为服务将 2FA 的所有方法视为同等有效。
当您第一次启用 2FA 时,通常会向您提供“恢复代码”,它本质上是一次性令牌,用于在没有 2FA的情况下登录您的帐户,以防您的手机或 2FA 设备丢失、无法访问或损坏。这些代码很重要,应该像对待手机或 2FA 设备一样安全。
在理想情况下,这些代码不应与您的密码存储在同一位置。如果可能,将它们存储在安全的物理位置,例如保险箱,否则在密码管理器中作为安全注释存储也可以。
如果您怀疑您的帐户已被盗用,您应立即重置密码并通知平台的安全团队。
当您重置密码时,平台应从所有设备上注销您的帐户,并要求使用新密码重新进行身份验证。在与平台的安全团队交谈之前,您可能希望暂缓将此密码存储在密码管理器中。
每个平台都有不同的安全团队,您可以联系。对于 Python 生态系统,此过程在PyPI 安全披露页面中进行了概述,该页面建议向 Python 安全响应团队[email protected]
和 PyPI 管理员发送电子邮件至[email protected]
.
本文的其余部分侧重于平台的安全性。我对不同的平台及其角色使用以下术语:
我将使用的示例来自我自己的经验,主要使用 GitHub 和 Python 包索引 (PyPI)。然而,这些建议可能适用于其他项目主机,如 GitLab 或 BitBucket 以及其他包存储库,如 NPM 和 RubyGems。
本文是从为项目宿主和包存储库提供单独平台的角度编写的,但情况并非总是如此!
例如,在 Go 和 PHP+Composer 生态系统中,项目宿主和包存储库通常是同一个平台,并使用 git 标签来表示新版本。如果您的生态系统有这种重叠,您应该查阅更具体的文档以保护您的项目主机、部署管道和包。
成为开源软件包的维护者意味着什么?维护者一词用于各种各样的上下文,从定期提交代码到负责未来的项目方向。
为了避免这种歧义,我将使用术语审阅者(缩写REV
)、发布经理(RMGR
)和所有者(OWNER
)。下面将详细描述这些角色中的每一个。
该图确定了帐户可以具有写入权限的 3 种资源:
源代码和部署管道代码之间的区别在于,部署管道代码有时存储在与源代码的其余部分相同的代码存储库中(例如 GitHub Actions)。如果是这种情况,则应通过GitHub 上的 CODEOWNER 之类的机制保护此代码,以防止审阅者在未经发布经理或所有者批准的情况下直接写入访问权限。
此角色也称为协作者或开发人员。短语commit-bit有时用于描述此角色,因为它们具有对源代码的写入权限。
“审阅者”角色的定义特征是他们可以自己审阅和合并拉取请求。这意味着它们可以在项目中自治并分类拉取请求。让许多成员担任审阅者角色对于项目来说非常有用,因为这增加了项目快速发布功能和修复错误的能力。
“发布经理”角色的定义特征是,他们可以将包直接发布到包存储库,也可以通过运行自动化部署管道间接发布。
在许多项目中,特别是在较小的项目中,没有团队成员担任发布经理角色。相反,项目的所有者履行了发布经理的所有职责。
对于较大的项目,我更倾向于拥有多个发布经理而不是两个以上的所有者。
最后,“所有者”的定义特征是他们有权分配和修改项目宿主和包存储库中其他角色的权限。一个包的 Owner 角色不应该有很多帐户,坚持 3 个或更少,但每个包的最佳 Owners 数量是 2。
对于可以配置的不同角色,每个平台都有不同的术语。当您应用本文中的建议时,请注意选择正确的建议。如果您不确定给定平台的角色,我建议您仔细阅读他们的文档。
对于某些平台,您将没有足够细粒度的角色来完全匹配此处列出的角色。请注意 RubyGems 上只有一个角色,因此 RubyGems 上的所有者和发布经理之间没有区别。请记住这一点。
小心选择谁成为新的发布经理(或所有者)。理想情况下,这些人将是具有为该项目或其他项目做出贡献的历史的公众人物。不幸的是,信任并不是很容易用明确的术语来定义的,所以要谨慎行事。特别要警惕那些不是公众人物的项目新人,他们要求包所有权。
有时人们从开源继续前进,或者从事一个项目不再有趣。那完全没问题!但是,如果是这种情况,并且您是该项目的“所有者”,那么您将面临一些艰难的决定。
如果您计划在某种程度上坚持但不是积极的贡献者,有时没有必要重新分配所有权。这种情况适用于 urllib3,原作者Andrey Petrov作为“元所有者”一直存在,而发布经理(我自己和Quentin Pradet)则决定项目的方向。
如果您计划完全继续,并且您的项目有一个或多个发布经理或审阅者,请考虑将其中一个提升为您的所有者角色。
如果您处于拥有很多用户但没有多少受信任的贡献者的情况下,另一种方法是将项目提供给已知的包维护者组织,例如Jazzband for Python。
如果您不知道在此之后该怎么做,您可以通过问题提醒您的用户或在变更日志中注明该项目有被无人维护的风险。这有时会引起用户对 fork 或继续维护您的项目的兴趣。
如果你已经用尽了所有其他可能性,最后的选择也是最简单的:什么都不做。请记住,创建开源代码并不意味着您欠任何人的回复、支持或时间。如果您的包存储库或项目宿主支持“归档”您的包,您可以考虑这样做以将项目标记为非活动。
唯一应该有权访问包存储库的帐户是活动的 Release Manager 和 Owner 帐户。强调活跃的,在包存储库中保留不活跃的帐户(超过最佳 2 Owners)只会增加攻击面而没有好处。
我很同情想要保留包的原始作者或以前的所有者的感觉,但请记住,最终访问包存储库是一个操作问题。作者身份和历史所有权的庆祝活动可以保留在其他地方,而不会影响安全性。
转让所有权后,您可能很想将自己完全从包中移除。如果新的所有者在未将所有权转让给其他人的情况下意外离开,则包只有一个所有者的情况有时会导致该包成为孤立的。
这对每个人来说都是一个不幸的情况,因此我建议在一个包中包含 2 个所有者,即使其中一个所有者不再积极开发包。
什么都不做是最好的最终选择是有原因的:删除你的包是个坏主意!某些软件包存储库不允许您删除自己的软件包。有两个原因:
如果有人依赖于您的软件包,然后在安装后收到一个具有相同名称的不同恶意软件包,那将是一场噩梦!最好将包保持原样并走开。
一些包存储库支持通过范围 API 令牌或密钥发布包,而不是使用您的用户名和密码。当您配置自动部署时,您应该利用此功能。
您的用户名和密码组合有可能发布到您维护的所有包,而作用域 API 令牌只能发布到令牌范围内的一个包。这意味着如果您的发布凭据以某种方式暴露,那么只有一个包而不是许多包处于危险之中。
支持令牌的另一个原因是在轮换凭证时。理想情况下,您应该能够随时重置密码,而无需考虑它将如何影响您的部署配置。如果您在多个项目中使用您的用户名和密码,则每次都必须更新每个项目。与在每个项目中使用 API 令牌时相比,您只需更新一个项目的凭据。
哇,你做到了这一步!感谢您这么久的停留,现在我们可以谈谈我使用的一些其他做法,但不一定推荐给每个人来实现安全配置。
我不会在这里给出完整的指南,但你应该花时间通过 GPG 设置签名提交。生成 GPG 密钥并配置 git 以使用该密钥只需要几个步骤,并减少了您的提交被欺骗的机会。
因为这只能防止通过拉取请求提出的恶意提交,并且仍然需要批准审查才能造成任何损害,我不认为签名提交对于“足够好”的安全性至关重要,但这是值得考虑的事情。
如果强制所有提交者使用签名提交将有更多的牙齿,但这可能会影响您项目的新贡献者的数量。
当前的操作系统支持自动加密磁盘上的所有数据。您的磁盘可能包含您不希望任何人访问的信息(会话 cookie、终端历史记录、API 令牌、SSH 密钥)。因为启用它相当简单,所以我建议对所有设备都这样做。
旅行时,您通常需要通过与设备分开的检查站。为了避免敏感信息落入坏人之手,许多密码管理器支持暂时从您的设备中删除保险库,直到您越过检查点。有时此过程是手动过程,您可以从手机中删除单个保险库(或整个应用程序),并且一些密码管理器提供“旅行模式”作为一项功能。