TanStack 遭 NPM 供应链攻击:42个包84个版本被注入恶意代码,三重漏洞链详解

事件概述

2026年5月11日,流行的 JavaScript 生态系统 TanStack(React Query、React Table、TanStack Router 等库的维护者)遭遇了一次精心策划的 NPM 供应链攻击。攻击者利用 GitHub Actions 缓存投毒、pull_request_target 工作流漏洞和 OIDC 令牌内存提取的三重漏洞链,成功向 NPM 发布了 42 个包的 84 个恶意版本

这次攻击之所以引起广泛关注,不仅因为 TanStack 是前端生态的核心依赖,更因为它展示了现代 CI/CD 供应链攻击的复杂程度——三个漏洞单独都不足以完成攻击,但被巧妙地串联在一起。

攻击链详解:三步串联

第一步:pull_request_target 工作流漏洞

攻击者在 TanStack/router 仓库创建了一个看似正常的 PR(标题为”WIP: simplify history build”),利用 bundle-size.yml 工作流中使用的 pull_request_target 触发器。这个触发器会在 fork PR 上运行,但具有主仓库的权限上下文。

关键问题在于:actions/cache@v5 的 post-job 缓存保存操作不受 permissions: 配置限制,它使用 runner 内部令牌而非 workflow GITHUB_TOKEN。即使设置了 permissions: contents: read,缓存修改仍然可以进行。

第二步:GitHub Actions 缓存投毒

攻击者提交了一个恶意的 vite_setup.mjs 文件(约 30,000 行混淆 JS),在构建过程中将恶意数据写入 pnpm-store 目录,并使用与 release.yml 发布工作流完全匹配的缓存键。

当维护者合并其他正常 PR 触发 release.yml 时,GitHub Actions 自动恢复了被投毒的缓存——这是缓存机制的正常行为,却成了攻击的入口。

第三步:OIDC 令牌内存提取

release.yml 声明了 id-token: write 权限用于 NPM OIDC 可信发布。被投毒的缓存中包含攻击者控制的二进制文件,它通过 /proc/*/cmdline 定位 GitHub Actions Runner.Worker 进程,读取 /proc/<pid>/maps/proc/<pid>/mem 从内存中提取 OIDC 令牌,然后直接向 registry.npmjs.org 发送发布请求。

受影响范围

42 个 TanStack 包被发布恶意版本,每个包两个版本(间隔约 6 分钟)。恶意软件安装后会:

  • 窃取凭证:AWS IMDS/Secrets Manager、GCP 元数据、Kubernetes 服务账户令牌、Vault 令牌、~/.npmrc、GitHub 令牌、SSH 私钥
  • 外传数据:通过 Session/Oxen 加密信使网络(filev2.getsession.org)传输,端到端加密使得 IP/域名封锁成为唯一的网络层防御
  • 自我传播:枚举受害者维护的其他包,以相同方式注入并重新发布

已确认未受影响的包族包括:@tanstack/query*@tanstack/table*@tanstack/form*@tanstack/virtual*@tanstack/store@tanstack/start(元包)。

时间线

5月10日 17:16 UTC:攻击者创建 fork 仓库(重命名为 configuration 以规避 fork 列表搜索)

5月10日 23:29 UTC:提交恶意代码,使用伪造身份 claude <claude@users.noreply.github.com>

5月11日 10:49 UTC:PR 被提交,pull_request_target 自动运行

5月11日 11:29 UTC:1.1GB 投毒缓存被保存

5月11日 19:15 UTC:维护者合并正常 PR,触发 release.yml,恶意包被发布

5月11日 ~19:50 UTC:外部研究员 ashishkurmi(StepSecurity)发现并报告——距恶意发布仅约 20 分钟

5月11日 21:00 UTC:确认影响范围,所有恶意版本被标记为 deprecated

修复与加固措施

TanStack 团队在事后采取了以下措施:

  • 禁用发布流水线中的 pnpm 缓存
  • 清除所有 GitHub Actions 缓存条目
  • 将所有第三方 action 固定到 commit SHA(不再使用浮动标签)
  • 移除所有 pull_request_target,替换为 workflow_run + 沙箱化 pull_request 工件
  • 强制非短信 2FA(npm 和 GitHub)
  • 升级到 pnpm 11
  • 引入 zizmor(GitHub Actions 工作流静态分析器)作为必需的 PR 检查
  • .github 文件夹添加 CODEOWNERS
  • 考虑关闭外部贡献者 PR,改为”欢迎 Issue 和 Discussion,PR 仅限邀请”

对站长和开发者的启示

1. 审计你的 GitHub Actions 工作流:检查是否使用了 pull_request_target,这是已知的危险模式。

2. 固定依赖版本和 Action SHA:不要使用 @main@v6 等浮动引用。

3. 关注依赖安全告警:如果你的项目依赖 TanStack,务必检查是否使用了受影响版本。

4. 使用 npm audit 定期检查:供应链攻击越来越复杂,自动化安全检查必不可少。

5. 了解你的 CI/CD 安全边界:缓存投毒、OIDC 令牌提取等高级攻击手法正在变得越来越普遍。

本文参考来源:

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容