SlugFest:npm 可信发布机制缺陷,1500 多个包面临劫持风险

GitHub 已收紧 npm 可信发布(Trusted Publishing)机制:新建但尚未完成首次成功发布的信任配置,将在 48 小时后自动过期。这项改动源自安全公司 Bloom Security 发现的缺陷 SlugFest —— 攻击者只要抢注一个已失效的仓库 slug,就能向不属于自己的 npm 包推送恶意版本,无需窃取任何凭据。研究显示,存在风险的包超过 1500 个,累计下载量超过 1000 万次。

事件概述

Bloom Security 于 2026 年 6 月 6 日向 GitHub 报告该问题,GitHub 在 10 月 2 日的变更日志中发布缓解措施,The Hacker News 在 10 月 13 日报道了这一事件。除 48 小时过期规则外,npm 还开始拒绝由 GitHub Actions issue_comment 事件签发的可信发布令牌,此前已对 pull_request_target 做过同样限制。

技术细节

可信发布于 2025 年 7 月在 npm 上线,用 CI/CD 平台签发的短期 OIDC 令牌替代长期发布密钥,理论上消除了密钥泄露风险。问题出在「如何识别仓库」这一步:OIDC 令牌里既带了可跨改名与转移存续的仓库数字 ID,也带了人类可读的 slug(形如 owner/repository)。npm 在包首次成功发布之前,只记录 slug,完全忽略那个不可变 ID。

于是攻击路径变得很短:受害者把仓库改名、转移或删除后,原 slug 被释放;攻击者在同一 owner/repository 路径下新建仓库,GitHub 会签发一个全新的数字 ID;随后触发发布工作流,拿到携带「旧 slug + 新 ID」的 OIDC 令牌;把它提交给 npm 的交换端点时,npm 会认为仓库声明与存档的 slug 吻合,于是发出发布凭据——攻击者就此获得了对一个自己并不拥有的包的限时发布权,推送恶意版本。

影响评估

这条链不需要内部人员配合、不需要窃取凭据、也不依赖竞态条件,唯一前提是受害者绑定的 slug 可被抢注、且该绑定从未成功发布过版本。得手后,恶意版本直接继承原包的名称、信誉、下载量与依赖关系网,是典型的级联式供应链攻击起点。风险还会被 AI 编码代理放大:据 Bloom 的观测数据,编码代理平均每月在每个端点上自动安装约 70 个包(开发者机器约 350 个),几乎不检查包的来源,「取最新版本、直接运行」成为默认行为。

修复建议

# 自查可信发布绑定(npm 侧)
npm trust list           # 列出信任关系
npm trust revoke --id    # 撤销旧绑定
# 逐项确认:绑定的 repository slug
# 是否仍解析到你自己控制的仓库
# 仓库改名 / 转移 / 删除后:
#   删除旧绑定 -> 用当前 slug 重建 -> 完成一次发布以通过校验

维护者应立即审查所有信任绑定,重点排查「仓库已改名或转移但绑定未更新」的情况;开启发布事件监控,对非预期的版本发布告警。使用方则可在 CI 中固定依赖版本、校验 lockfile 与来源证明(provenance),避免自动拉取新发布的版本。

参考来源