跳至内容

事件报告:GitHub 个人访问令牌泄露

2024 年 6 月 28 日,security@pypi.org 和我 (Ee Durbin) 收到通知,我的 GitHub 用户帐户 ewdurbin 的 GitHub 个人访问令牌被泄露。该令牌已立即被撤销,并对我的 GitHub 帐户和活动进行了审查。未发现任何恶意活动的迹象。

事件时间线

  • 2023-MM-DD1:为 ewdurbin 创建了一个 GitHub 个人访问令牌。该令牌对我的用户的所有组织和存储库2(包括 pypipythonpsfpypa)具有推送、拉取和管理员访问权限。
  • 2023-03-033cabotage/cabotage-app:v3.0.0b35 推送到 hub.docker.com,其中包含 ewdurbin 的 GitHub 个人访问令牌,位于一个 .pyc 文件中。
  • 2023-07-203cabotage/cabotage-app:v3.0.0b110 推送到 hub.docker.com,其中包含 ewdurbin 的 GitHub 个人访问令牌,位于一个 .pyc 文件中。
  • 2024-06-21:cabotage/cabotage-app:v3.0.0b35cabotage/cabotage-app:v3.0.0b110 从 hub.docker.com 中删除,原因与本报告无关4
  • 2024-06-28 7:09 AM 东部时间:JFrog 的 Brian Moussalli 向 security@pypi.org 和 Ee 的个人电子邮件地址报告了他们发现 ewdurbin 的 GitHub 个人访问令牌。
  • 2024-06-28 7:26 AM 东部时间:ewdurbin 的 GitHub 个人访问令牌被销毁。

事件是如何发生的

在本地开发 cabotage-app5 时,我正在处理代码库的构建部分,一直遇到 GitHub API 速率限制。这些速率限制适用于匿名访问。虽然在生产中,系统被配置为 GitHub 应用程序,但我修改了我的本地文件,以便在懒惰的情况下将自己的访问令牌包含在内,而不是配置一个 localhost GitHub 应用程序。这些更改从未打算远程推送。

diff --git a/cabotage/celery/tasks/build.py b/cabotage/celery/tasks/build.py
index 0f58158..3b88b5d 100644
--- a/cabotage/celery/tasks/build.py
+++ b/cabotage/celery/tasks/build.py
@@ -395,7 +395,10 @@ def build_release_buildkit(release):


 def _fetch_github_file(
-    github_repository="owner/repo", ref="main", access_token=None, filename="Dockerfile"
+    github_repository="owner/repo",
+    ref="main",
+    access_token="0d6a9bb5af126f73350a2afc058492765446aaad",
+    filename="Dockerfile",
 ):
     g = Github(access_token)
     try:
@@ -407,7 +410,13 @@ def _fetch_github_file(
         return None

虽然我很清楚 .py 文件中泄漏令牌的风险,但我没有考虑到包含编译字节码的 .pyc 文件。

当时,使用以下脚本执行暂存部署,该脚本尝试但未能删除临时应用的更改,包括硬编码的密钥。

#!/bin/bash

generation=$(cat generation)
git stash
docker buildx build --platform linux/amd64,linux/arm64 -t cabotage/cabotage-app:v3.0.0b${generation} --push .
kubectl -n cabotage set image deployment/cabotage-app cabotage-app=cabotage/cabotage-app:v3.0.0b${generation} cabotage-app-worker=cabotage/cabotage-app:v3.0.0b${generation} cabotage-app-beat=cabotage/cabotage-app:v3.0.0b${generation}
git stash pop
echo $((generation + 1)) > generation

由于应用程序一直在 Docker 中的共享卷上本地运行,因此仍然存在包含本地和未提交更改的 .pyc 文件。当时,一个最小的 .dockerignore 文件 并没有从构建中排除 __pycache__*.pyc 文件,导致密钥泄露。

响应

除了撤销令牌并审查我可获得的几乎所有 GitHub 审计日志和帐户活动,以查找可能对令牌的恶意使用之外,还做了一些更改来降低此类泄露的未来风险。

Cabotage 现在完全自托管,这意味着 cabotage-app 的构建不再使用公共注册表6,部署构建从源代码的干净签出开始。这缓解了本地编辑进入开发环境之外的镜像构建的场景,也消除了发布到公共注册表的必要性。

我已经撤销了与我的帐户相关的唯一一个 GitHub 访问令牌,并且在未来将避免创建令牌,除非绝对必要,并且会确保令牌有内置的过期时间。除了这个奇怪的使用案例,我不记得我的用户曾经使用过长时间的令牌,并且拥有一个令牌以便我在懒惰时使用,似乎风险大于收益。

收获

这是一个很好的提醒,要为 API 令牌设置激进的过期日期(如果需要的话),将 .pyc 文件视为源代码,并仅从干净的源代码中执行自动化系统的构建7

感谢

首先,感谢 JFrog 的团队发现并报告了这次泄漏。您可以在他们的博客上阅读有关此发现的博客文章 他们的博客

我们感谢整个安全研究人员社区对 Docker Hub 和 PyPI 本身等公共存储库进行的扫描。PyPI 依赖于这个社区的努力来检测 上传到 PyPI 的恶意软件,并与 GitHub集成,以自动处理提交中泄露的 PyPI 凭据,以及 公共问题中的凭据。所有各方之间的合作有助于提高开源的安全性,我们谁也无法单独做到这一点。


Ee Durbin 是 Python 软件基金会的基础设施总监。自 2013 年以来,他一直在为保持 PyPI 在线、可用和安全而贡献力量。


  1. 确切日期未知,因为 GitHub 帐户安全日志 无法获取超过 90 天的日志。 

  2. 此令牌的具体权限未知,因为它们没有保留在 GitHub 帐户安全日志中,并且在令牌被销毁之前没有被记录。我们已经询问 JFrog 他们的发现是否包含令牌级别权限,如果他们提供,我们将更新此帖子。

    编辑:JFrog 于 2024 年 7 月 9 日从他们的发现中提供了这些日期。 

  3. 发布时间未知,因为 hub.docker.com 不会保留任何已删除镜像的历史记录。我们已经询问 JFrog 他们的发现是否包含发布时间,如果他们提供,我们将更新此帖子。

    编辑:JFrog 于 2024 年 7 月 9 日从他们的发现中提供了这些日期。 

  4. cabotage-app 已经将构建迁移到自动化系统。hub.docker.com 上的镜像不再需要,并已主动删除。 

  5. cabotage 是部署 warehouse 代码库 以及构成 PyPI 的相关服务的代码库。您可以在我们博客中发布的关于 代码库的安全审计 的博客文章中了解有关 cabotage 的更多信息。 

  6. 更重要的是,用于 cabotage 构建的内部私有注册表使用 细粒度的 Docker 身份验证,只允许指定的 Kubernetes 服务帐户访问镜像。 

  7. 例如,PyPI 支持 受信任的发布者。