你昨天的 npm install,可能已经偷走了你的所有密钥
你昨天的 npm install,可能已经偷走了你的所有密钥
2026 年 8 月 4 日下午,有人往 keyv 的 GitHub 仓库推了两个恶意文件,顺手打了个版本标签。npm 自动发布,GitHub Actions 签好名。一切看起来正常。
只有一点不对:那次 push 不是维护者干的。账号被人拿下了。
那天下午跑过 npm install 的人——可能包括你——机器上悄悄执行了一段 728KB 的混淆脚本。它干的事很直接:把能翻到的密钥全打包,加密,传到一个公开的 GitHub 仓库。
那个仓库的简介只有一句话:"Shai-Hulud: Here We Go Again"。
中招的不是一个包,是一整条链
keyv 只是入口。同一个维护者手里还有 flat-cache(月下载 5.8 亿)、file-entry-cache(5.7 亿)、cacheable-request(1.4 亿)、cacheable——一整套缓存工具链。账号一丢,11 个包全被注入。
真正难受的是后面那步:蠕虫。Math_Symbol.js 偷完密钥不收工,它还要翻一遍你的硬盘,看看你是不是某个 npm 包的维护者。是的话,偷走你的 npm token,用你的名义发一版带毒的包出去。
截止写作时,434 个包、1381 个版本被污染,合计月安装量超过 20 亿次。@deliveroo/reevent、@picsart/ai-sdk、@qlik/embed-runtime、picasso.js 都在名单里——大公司的包,现在也在替它分发。
它到底偷了什么
翻一眼 Math_Symbol.js 的提取器列表,后背发凉:
- npm token:读
~/.npmrc,再扫全盘找散落的.npmrc,把每个_authToken抠出来,当场打registry.npmjs.org/-/whoami验有效性 - GitHub token:classic PAT、OAuth token、GitHub App token、JWT OIDC token。来源从
~/.config/gh/hosts.yml到环境变量,再到 CI runner 的进程内存,一个不落 - AWS 凭证:
~/.aws/credentials里所有 profile、环境变量、EC2 metadata(IMDSv2 走不通就退 v1)、ECS container metadata,最后直接调secretsmanager:ListSecrets跨区域列一遍 - Kubernetes secret:读 service account token,调 K8s API 把当前 namespace 的 secret 全拉走,顺手捎上 kubeconfig
- HashiCorp Vault token:六个来源挨个试——环境变量、
~/.vault-token、CI runner 路径、K8s JWT 登录、AWS IAM 登录。拿到就遍历所有 KV store - Stripe & Slack token:全盘扫描,匹配
sk_和xox开头的字符串 - 通用文件扫描:约 200 个 glob,覆盖
.env、.pem、.key、.p12、SSH key、Terraform state、浏览器 cookie 和密码库。基本上就是"你所有不想被人看见的东西"
触发这一切,只用了 package.json 里的一行 preinstall。node_modules 还没装完,密钥已经飞出去了。
签名和 2FA 为什么没拦住
很多人第一反应是:npm 不是有 provenance 签名吗?不是强制 2FA 了吗?
这次攻击把一个残酷的事实摆到台面上:攻击者一旦控制了维护者的 GitHub 账号,他发的包和维护者本人发的,没有任何区别。
GitHub Actions 的 OIDC token 是发给 runner 的,不是发给人的。攻击者把代码推进 main,切个 tag,剩下的构建、签名、发布全自动完成。每一步都合法——因为在 GitHub 眼里,就是"维护者"本人在操作。
2FA 也一样。只要攻击者手上有一枚有效的 session cookie 或 OAuth token,session 没过期,2FA 就压根不会被触发。不需要密码,不需要验证码,一个 cookie 就够。这就是 session hijacking。
依赖的风险,不只有恶意代码这一种
8 月 4 日那天,关于"依赖"的新闻其实有两条。另一条是 Bending Spoons 花 13 亿美元收购了 Airtable。
看着八竿子打不着,底层是同一件事:你依赖的东西,不归你管。
Keyv 是"有人蓄意搞你",Airtable 是"东家换人了,定价大概率要动"。对开发者来说,落到身上的结果一样——你的地基被别人捏在手里。
Bending Spoons 的打法很稳定:买下来、裁员、涨价、砍免费层。所以接下来大概率会看到 Airtable 免费 API 额度缩水、套餐重组,某些功能直接下架。如果你有自动化流程跑在上面——不少人拿它当无代码后端——现在就该起草迁移方案。不是说它明天就没了,而是免费的日子多半到交割那天为止。
这不是 FUD,是最基本的风控。
今天能做的五件事
别慌,但也别关掉页面就当没看见。按优先级来:
1. 检查 lockfile(5 分钟)
grep -E "keyv|flat-cache|file-entry-cache|cacheable|cache-manager" package-lock.json yarn.lock 2>/dev/null
有命中就核版本号。已知受影响的包括 keyv 6.0.0、flat-cache 6.1.24、file-entry-cache 11.1.6 等。升到干净版本,然后把那台机器碰过的 token 全部轮换。
2. 关掉 install 脚本(10 分钟)
npm config set ignore-scripts true
preinstall 和 postinstall 从此不自动跑。代价是部分 native 模块要手动 npm rebuild——这点麻烦我认为值得。不想一刀切的,至少带 --ignore-scripts 跑一次全量安装,看看依赖树里有没有异常。
3. 给 CI 加依赖审查(30 分钟)
GitHub Actions 加一步:
- name: Audit dependencies
run: |
npm audit --audit-level=high
npx socket scan
不想引新工具,那至少确认两件事:lockfile 提交进仓库了,CI 跑的是 npm ci 而不是 npm install。
4. 轮换密钥——如果你昨天真跑了 install(紧急)
8 月 4 日跑过 npm install,而且 lockfile 里有受影响的包,现在就换:
- npm token:npmjs.com 重新生成,旧的全部 revoke
- GitHub PAT:Settings → Developer settings → Personal access tokens
- AWS access key:IAM → Users → Security credentials
- 那台机器
~/.env里存过的每一个 API key
不是过度反应。攻击脚本扫的就是这几样。
5. 列一张"单点依赖"清单(本周)
打开一个空白笔记,回答三个问题:
- 哪些第三方服务一挂,我的产品直接不可用?(数据库、CDN、认证、支付跳过,这些你早有意识了,找那些你"从没当回事"的)
- 哪些 npm 包突然被删或被污染,我要花多久才能替掉?
- 哪些 SaaS 的价格翻一倍,我的成本结构会崩?
写下来就行,不用马上出方案。"心里有数"和"完全没想过",差的是一整个反应速度。
供应链攻击不会停。AI 写的代码越来越多,依赖树越来越深,攻击面只会更大。
所以别看完就走。现在开个终端,把第 1 条那行 grep 跑一遍,两分钟的事。真命中了,你会庆幸自己没拖到明天。
✨ 本文由 DeepSeek 生成初稿,Claude 审核润色。
参考来源: