安全审计修复:仓库
这篇文章深入探讨了仓库的安全审计结果的修复工作 - 这是PyPI.org 的主要代码库。
审计报告可以在这里找到。我强烈建议首先阅读该报告,以便充分了解上下文。
发现
审计报告针对仓库确定了 18 项发现,以及一些代码质量建议。本文将重点介绍这些发现及其修复。部分代码质量建议已实施,另一些则被推迟。
以下是与仓库相关的项目及其状态的表格
ID | 标题 | 严重性 | 难度 | 状态 |
---|---|---|---|---|
TOB-PYPI-1 | "合并 PR" 工作流中的不安全输入处理 | 信息性 | 高 | 已修复 |
TOB-PYPI-2 | AWS SNS 验证中使用弱签名 | 中等 | 未确定 | 已修复 |
TOB-PYPI-4 | 发送电子邮件的端点缺乏速率限制 | 低 | 高 | 已接受 |
TOB-PYPI-5 | 冻结和禁用帐户的帐户状态信息泄露 | 中等 | 低 | 已修复 |
TOB-PYPI-6 | 搜索锁定中潜在的竞争条件 | 低 | 高 | 已修复 |
TOB-PYPI-7 | 使用多个不同的 URL 解析器 | 信息性 | 未确定 | 已修复 |
TOB-PYPI-8 | XML 视图上的过于宽松的 CSP 标头 | 信息性 | 高 | 已修复 |
TOB-PYPI-9 | 缺少 Permissions-Policy | 中等 | 高 | 已修复 |
TOB-PYPI-10 | 文件摘要中的域分离 | 低 | 低 | 已修复 |
TOB-PYPI-11 | 由于临时文件,对象存储易受 TOC/TOU 攻击 | 信息性 | 高 | 已接受 |
TOB-PYPI-12 | 如果令牌不匹配,HTTP 标头将被静默信任 | 信息性 | 高 | 已修复 |
TOB-PYPI-13 | Bleach 库已弃用 | 信息性 | 未确定 | 已修复 |
TOB-PYPI-14 | 存储后端中的弱散列 | 中等 | 高 | 已接受 |
TOB-PYPI-15 | 使用精心制作的 README 时的未捕获异常 | 信息性 | 中等 | 已接受 |
TOB-PYPI-16 | 通过 zxcvbn-python 依赖项进行 ReDoS | 信息性 | 高 | 已接受 |
TOB-PYPI-23 | XMLRPC 服务器中的不安全 XML 处理 | 低 | 低 | 已修复 |
TOB-PYPI-27 | tar.gz 上传的拒绝服务风险 | 信息性 | 中等 | 已接受 |
TOB-PYPI-29 | LIKE SQL 查询中的未转义值 | 信息性 | 低 | 已接受 |
ID 不连续,因为审计报告还包括了针对 cabotage 的发现。
对于部分已修复的条目以及所有已接受的条目,我将在下面详细介绍。
详情
现在您已经有机会阅读原始审计报告,并且可以看到我们已经修复了大部分发现,我想花点时间深入研究一些特定发现的细节。
TOB-PYPI-2:AWS SNS 验证中使用弱签名
PyPI 使用 AWS SES 向用户发送电子邮件。SES 配置设置为使用消息传递状态 主题,该主题会向 AWS SNS 主题发送通知,然后该主题会向我们的应用程序发送通知。
这对“已接受/已送达”等操作很有用,但更重要的是“退回”和“投诉”通知,这些通知会更改用户帐户的状态。我们不想向已知的错误地址发送更多电子邮件,也不想向将我们标记为垃圾邮件的用户发送电子邮件。
由于 PyPI 从 AWS SNS 接收 webhook,因此它需要验证消息的签名。
验证传入的 SNS 消息通常由用户自己处理。AWS SNS 文档 清楚地说明了这一点。
我们之前为版本 1 实施了签名验证,该版本使用 SHA1 哈希算法,因为这是我们实施时的可用算法。
随着时间的推移,AWS SNS 添加了对 SHA256 的支持,但升级路径仍然留给用户处理。SNS 仍然默认使用 SHA1 (SignatureVersion: '1'
),并且没有 Python SDK 函数可以调用来为您验证签名。
2022 年 9 月,AWS SNS 添加了对 SHA256 签名的支持,并在这篇博文中分享了详细信息。他们还在一些客户端 SDK 中添加了对验证的支持,但遗憾的是 Python 尚未包含在内。
虽然我们已经验证了 SignatureVersion 1,但我们利用此次机会添加了对 SignatureVersion 2 的支持,更新了我们的设置,现在只接受 SHA256 签名。
作为AWS 英雄,我联系了 AWS 现代计算社区负责人 Farrah Campbell,她迅速将我与 AWS SNS 服务团队联系起来进行交流。我们讨论了一些挑战,以及一些前进方向的想法。
我希望将来我们会看到两件大事
boto3
中的两个签名版本的邮件验证 这将使我们能够删除我们添加到仓库中的MessageVerifier
,并从验证过程的任何未来增强中受益。- 将 AWS SNS 更新为默认使用
SignatureVersion: 2
(SHA256)。这对于尚未更新其设置的用户来说可能是一个重大更改,但对于安全来说将是向前迈出的一大步。这可能需要一些时间,因为新的签名版本是在一年前才添加的。我会把它留给 SNS 服务团队处理。
TOB-PYPI-4:发送电子邮件的端点缺乏速率限制
我们接受了这一发现,因为我们需要向未经验证的用户发送电子邮件,作为帐户创建过程的一部分,我们不想阻止这种情况。
该发现详细说明了 PyPI 没有应用全局速率限制,这是正确的。向未经验证的地址发送电子邮件的端点通过速率限制得到保护。
PyPI 已实施了补偿性控制来防止滥用,例如防止向单个用户发送太多密码重置电子邮件。
由于这里的风险是针对电子邮件服务的成本和声誉,因此我们决定接受这一发现。在将来,我们可能会重新审视发送电子邮件的速率限制策略。
TOB-PYPI-6:搜索锁定中潜在的竞争条件
这又是“我们实施了一些当时有效的措施,但随着时间的推移,出现了更好的解决方案”的例子。
我们编写了一个上下文管理器来处理在执行更新时锁定搜索索引,以防止多个进程尝试同时更新搜索索引。该实现没有绑定到底层 Redis 锁定到期时间,因此可能会导致 Redis 锁定到期,但 Python 认为它仍然处于锁定状态。
在这里,我们更新了我们的实现,使用 redis-py
现在提供的上下文管理器,而不是自己编写。
一个很好的提醒,时不时地检查一下您的库和服务,看看是否有新的功能可以帮助您。
TOB-PYPI-11:由于临时文件,对象存储易受 TOC/TOU 攻击
这是一种复杂的定时攻击,需要对系统进行一定级别的访问,这将允许进行更直接的攻击。发现本身详细说明了,如果攻击者可以执行此操作,他们更有可能造成其他类型的破坏。
在我们的各种存储后端/客户端 API 之间进行导航的复杂性似乎不值得进行深度防御,因为需要进行一定级别的访问才能利用漏洞。
我们有一个草案 PR,其中包含实现的开始部分,如果我们决定继续执行此操作。
TOB-PYPI-14:存储后端中的弱散列
这专门针对 Backblaze B2 存储后端,它是 PyPI 目前的对象存储提供商之一,目前不支持 SHA-256 校验和。他们支持 SHA-1,这对于检测传输过程中的数据损坏很有用,但不足以用于非碰撞校验和 - 我们必须为此使用 MD5。
在审计期间,我们联系了 Backblaze 团队进行讨论,并确定这已列入他们的路线图,当他们实施后,我们将相应地更新我们的使用情况。
TOB-PYPI-15:使用精心制作的 README 时的未捕获异常
这一发现发现了一个 docutils
中的错误,PyPI 通过 readme_renderer
库使用它来将项目描述从 reStructuredText 和 Markdown 渲染为 HTML。
该错误在 此处跟踪,并且维护者尚未做出回应。
它只适用于使用 reStructuredText 作为 README 时的客户端行为,因此我们接受了这一发现。此外,任何在上传之前执行 twine check
的用户都会发现此问题。
一旦该错误被修复,我们就会更新!
TOB-PYPI-16:通过 zxcvbn-python
依赖项进行 ReDoS
直接来自审计
这一发现纯粹是信息性的。我们相信它几乎没有影响,就像许多 ReDoS 漏洞一样,这是由于仓库的部署架构造成的。
仅此而已。
TOB-PYPI-23:XMLRPC 服务器中的不安全 XML 处理
审计开始时,仓库部署在 Debian 11 bullseye
上,作为正常维护的一部分,我们在审计进行过程中升级到了Debian 12 bookworm
。
Python XMLRPC 使用expat
用于 XML 解析。Debian bullseye
中的 expat
版本为 2.2.10
,该版本容易受到审计报告中详细介绍的特定攻击。
对于 bookworm
,expat
的版本为 2.5.0
,不受影响。(通常被认为从 2.4.1 开始修复。)
这是一个很难追踪的问题,因为一旦报告出来,我就无法在本地重现问题,因为我已经升级了。
使用一些 git bisect
魔法,我能够追踪到修复该问题的精确提交(bookworm
升级),然后就是确定哪个库发生了改变。
在弄清楚之后,我与审计人员合作,更新了他们的建议以反映升级。到目前为止,一般建议是采用 defusedxml
,这可能更难,因为我们将大部分 XML 解析委托给 pyramid-rpc
,它使用标准库中的 xmlrpc.client
。
如果您想检查自己的安装,可以运行以下命令
python -c "import pyexpat; print(pyexpat.EXPAT_VERSION)"
在 bookworm
上,我们获得了 expat_2.5.0
,不受漏洞影响。
这个问题已通过底层操作系统更新至 bookworm
版本修复。Debian 发行版在同一发行版版本期间会固定使用特定版本的库。
TOB-PYPI-27:tar.gz 上传的拒绝服务风险
这是一个棘手的问题,因为它需要在可用性和安全性之间权衡。
审计报告详细说明了一种具体的攻击方法,攻击者可以上传一个包含高度压缩文件的 tar 包,导致服务器花费大量时间解压缩它。
由于我们接受来自公众的上传,因此我们必须尽可能采取预防措施以防止滥用。对于 ZIP 文件(所有 .whl
或“wheel”文件都是),我们已经有一套机制来检测解压缩炸弹并将其拒绝。
然而,由于 .tar.gz
文件没有将文件大小作为元数据进行宣传,为了检测解压缩炸弹,我们必须无论如何解压缩整个文件。
正如报告中所述,我们的部署架构对这种行为进行了补偿,我们为此配备了一个专门的 worker 池来处理上传。
我们将来可能会在系统级别实施额外的限制,但目前我们已接受这一发现。
TOB-PYPI-29:LIKE SQL
查询中未转义的值
这里的风险是,查询可能会“遍历表”,而不会利用任何索引,从而导致更高的资源使用率。
我们使用未转义 LIKE
查询的大多数地方都在 PyPI 的管理员专用界面,我们希望允许管理员搜索用户、软件包等。
对于我们允许公开访问 LIKE
查询的一个地方,已经设置了速率限制以防止滥用。该表的大小也小于 100 万行,因此遍历未索引的列不会造成显著的资源使用,并且只需要额外的几毫秒。
潜在的更高资源使用仅限于恶意的内部人员,如果我们无法信任彼此,那么我们还有更大的问题需要解决。
我们已接受这一发现,并将继续监控所有相关资源。
总结
与 Trail of Bits 的团队合作非常愉快,我感谢他们的细致和专业精神。
虽然审计资金来自 Open Technology Fund,但我对修复工作的及时完成要感谢 Amazon Web Services 的资助,让我能够担任 PyPI 安全与安全工程师。我感谢这两个组织对让 PyPI 成为所有 Python 用户更安全的地方的持续支持。
Mike Fiedler 是首任 PyPI 安全与安全工程师。