对抗知识焦虑,从看懂这条开始
App 下载对抗知识焦虑,从看懂这条开始
App 下载
tanstack系列|凭证窃取|供应链攻击|GitHub Actions缓存|npm恶意包|网络安全|前沿科技
2026年5月11日19点20分到26分,6分钟里,前端开发圈常用的42个@tanstack/*系列npm包,被悄悄发布了84个恶意版本。这些包带着合法的官方签名,用正常的发布流程推送到npm仓库,却能在开发者执行npm install时,偷偷窃取AWS、GCP、GitHub等几乎所有能接触到的凭证。更可怕的是,攻击者没偷任何npm令牌,也没攻破发布流程——他们只是利用了GitHub Actions里一个被忽略的缓存机制。没人想到,用来加速构建的缓存,会变成供应链攻击的隐形通道。
你可以把GitHub Actions的缓存理解成办公室的共享文件柜——大家把常用的工具文件存进去,下次要用直接拿,不用再重新下载。但如果有人偷偷把里面的工具换成了带窃听器的仿制品,后面所有人用的时候都会中招。

攻击者的第一步,是给目标仓库发了一个看似无害的PR,标题是“简化历史构建”。这个PR触发了仓库里的bundle-size.yml工作流——这个工作流用了pull_request_target触发器,它的特殊之处在于:会以主仓库的高权限运行,而且默认不需要审核就能执行。
第二步,攻击者在PR里藏了一个恶意脚本vite_setup.mjs。当工作流执行时,这个脚本会把恶意代码写入pnpm的缓存目录,而且特意用了和官方发布流程完全一样的缓存键——就像在共享文件柜里,把“正版工具”的标签贴在了恶意文件上。

第三步,攻击者立刻把PR改回了空提交,删掉了分支,看起来什么都没发生。但被污染的缓存已经躺在了GitHub的服务器上,静静等待下一次被调用。
当官方合并了一个正常的PR,触发release.yml发布流程时,意外发生了。发布流程按照惯例去缓存里取pnpm依赖,精准命中了那个被污染的缓存键——就像工作人员毫无防备地拿起了共享文件柜里的“正版工具”。
恶意代码启动后,做了一件关键的事:它直接读取了GitHub Actions Runner进程的内存,从中提取出了OIDC令牌。这个令牌是GitHub用来做无密码认证的短期凭证,原本是给发布流程用的,现在却被攻击者抢来,直接向npm注册表发布恶意包。

整个过程里,发布流程的测试步骤其实失败了,官方设置的Publish Packages步骤根本没执行。但攻击者绕开了所有环节,用偷来的令牌直接完成了发布。等外部研究员发现异常时,84个恶意版本已经在npm上挂了20分钟,而这个时间足够成千上万的开发者执行npm install。
这次攻击能成功,本质上踩中了三个普遍存在的安全误区。
第一个误区是对pull_request_target的信任。很多开发者用这个触发器来处理PR,却忘了它是在主仓库的权限上下文里运行的——相当于给陌生人一把办公室的钥匙,还让他随便用里面的设备。
第二个误区是对缓存的过度信任。缓存被当成了“安全的加速工具”,没人会去验证缓存里的内容是不是被篡改过。就像共享文件柜里的东西,默认就是安全的,没人会每次用都拆开检查。
第三个误区是对OIDC令牌的忽视。短期凭证被认为比长期令牌安全,但攻击者通过读取内存直接获取,绕过了所有针对令牌泄露的防护。这就像你把钥匙藏在口袋里,却被人直接伸手掏走了。
更值得警惕的是,攻击者用的所有技术都是公开的:2024年就有研究报告过GitHub Actions缓存投毒,2025年的tj-actions事件里就出现过内存提取令牌的手法。攻击者只是把这些零散的技术拼成了一条完整的攻击链。
事件发生后,TanStack团队紧急废弃了所有恶意版本,npm也下架了相关包,官方建议所有安装过的用户立刻轮换所有凭证。但这次攻击留下的阴影远没散去——当CI/CD流水线从“效率工具”变成“攻击入口”,当缓存从“加速手段”变成“投毒通道”,开发者不得不重新思考:我们到底该信任什么?
安全的本质,从来不是依赖工具的“默认安全”,而是对每一个权限、每一个环节都保持警惕。流水线的每一步,都是信任的选择题。 而在供应链攻击面前,任何一个被忽略的细节,都可能成为下一个隐形的炸弹。