site logo

Marico's space

供应链攻击不仅仅是“大库”问题——以下是你今天可以采取的措施

前端技术 2026-05-19 17:34:43 21

最近供应链安全又出事了。2026年5月,一个叫Shai-Hulud的蠕虫病毒搞定了42个TanStack包,包括@tanstack/react-router——一个装在数百万个JavaScript项目里的库。从上线到被发现大概3个小时,够长了。如果你那天正好装了依赖,可能已经中招了。

这篇不是写给库维护者看的。是写给咱们这些普通开发者的——谁还没npm install过呢。

"冷知识"1
只存活了约3小时。@tanstack/react-router每周下载量是1270万次。也就是说在那3小时窗口内,仅这一个包就有约22.5万次下载。这只是被攻击的42个包里的一个。

以前供应链攻击感觉是别人的事——大库被黑,维护者修复,一切如常。Shai-Hulud改写了剧本:这玩意儿会自动扩散到受害者维护的所有包,把普通开发者变成无意识的病毒分发者。下面说说到底发生了什么,以及你现在能做点什么。

发生了什么?

  1. CI工作流用了pull_request_target,这玩意儿用仓库的受信任权限运行——包括访问密钥和跟真实发布流水线共享的构建缓存。

  2. 但它同时检出了fork的代码并执行,用于性能测试。这是个危险组合:陌生人的代码跑在你自己仓库的信任环境里。

  3. 攻击者没有立刻偷东西。只是污染了共享缓存然后等着。几个小时后,合法发布流水线运行了,在不知情的情况下捡起了被篡改的缓存,用TanStack自己的有效凭证发布了恶意包。

关键洞察:这个配置错误并不明显。性能测试的意图是合理的;问题在于没意识到pull_request_target + "运行PR的代码"无论你想做什么,都是危险组合。

"冷知识"2
这个蠕虫有个死亡开关。它植入了一个后台服务,每60秒用窃取的GitHub令牌轮询api.github.com/user。如果令牌被吊销——也就是GitHub返回4xx响应——服务就会触发rm -rf ~/,擦掉用户的整个主目录。所以你得先禁用并移除监控服务再吊销任何凭证。

好消息

pull_request_target Pwn Request专门针对公开仓库——也就是陌生人可以提PR的那种。

坏消息

话虽如此,攻击的其他部分(workflow之间的缓存污染、OIDC令牌过度授权)如果你的GitHub Actions有类似配置错误,私有仓库也逃不掉。

到这儿听起来像是库维护者的问题?还真不是。你不需要维护一个下载量几百万的库才能中招。你只需要在错误的时间运行npm install。一旦被黑的包进了你的node_modules,你也成了链条的一部分。

怎么防止类似事件?

min-release-age 和 ignore-scripts

NPM — .npmrc

min-release-age=7
ignore-scripts=true
Enter fullscreen mode Exit fullscreen mode
  • min-release-age=7 阻止安装发布不足7天的包
  • ignore-scripts=true 阻止生命周期脚本如preinstall/prepare在安装时运行——这正是恶意optionalDependency利用的入口
  • ⚠️ 需要npm CLI v11。升级到Node 24,或者手动安装npm v11。

pnpm (10.16) — pnpm-workspace.yaml

minimumReleaseAge: 10080 # 分钟 — 7天
Enter fullscreen mode Exit fullscreen mode
  • minimumReleaseAge默认开启,默认值1天
  • 需要10.16版本避免忽略此配置的bug
  • ignore-scripts:pnpm v10默认不再运行preinstall/postinstall脚本

Yarn Classic

  • min-release-age不支持。v1没有等效配置。
  • yarn install --ignore-scripts存在,但只是CLI参数

Yarn Berry (v2+) — .yarnrc.yml

npmMinimalAgeGate: 10080 # 分钟 — 7天
enableScripts: false # ignore-scripts等效配置
Enter fullscreen mode Exit fullscreen mode
  • ⚠️ 需要Yarn 4.10,且全局生效,无法限制在特定范围

Dockerfile

ENV npm_config_min_release_age=3
Enter fullscreen mode Exit fullscreen mode
  • npm ci会读取.npmrc设置和环境变量——npm_config_*约定对installci都生效。
  • ⚠️ 仍然需要Node v24或npm v11,否则ENV npm_config_min_release_age=3会被静默忽略

方案A — 仅在Docker中升级到Node 24

FROM node:24-alpine
# npm v11已包含,min-release-age可用
ENV npm_config_min_release_age=3
Enter fullscreen mode Exit fullscreen mode

方案B — 保持当前Node版本,手动安装npm v11

FROM node:22-alpine
RUN npm install -g npm@11
ENV npm_config_min_release_age=3
Enter fullscreen mode Exit fullscreen mode

参考资料

  • Snyk安全分析报告
  • TanStack官方事后分析
  • TanStack提交历史
  • npm min-release-age官方文档
  • npm与Node版本对应关系
  • pnpm minimumReleaseAge配置
  • pnpm v11发布说明
  • Yarn Berry npmMinimalAgeGate配置
  • Yarn Berry enableScripts配置

原文链接:https://dev.to/...