如果你最近一周在任意一台开发机或 CI 上跑过 `npm install`,建议先花五分钟把这篇文章看完,再决定要不要顺手做个自查。
这不是又一次"狼来了"。刚过去的 8 月,npm 生态密集爆了几件大事:下载量最大的基础组件之一被投毒、蠕虫式扩散、针对大厂开发环境的定向攻击,逼得 npm 官方连夜改了发布机制。事情本身很多文章都报了,但信息又碎又乱,数字各家对不上,还有几个关键误区流传很广。我把多个安全公司的报告交叉对了一遍,这篇直接给你:到底发生了什么、哪些说法不可信、以及一份能直接照着做的自查清单。

先说结论:你可能从没装过 keyv,但它大概率就在你的依赖树里
8 月 4 日,npm 基础缓存组件 keyv(周下载量 1.27 亿)的维护者 GitHub 账号被攻破。攻击者没有劫持 npm 账号,而是直接向 main 分支推送恶意代码,再由官方 GitHub Actions 流水线自动构建、自动发布——毒版本 keyv@6.0.0 就这么带着合法的发布签名上了架。知乎
随后蠕虫用偷到的凭据横向扩散,同一个维护者名下的 flat-cache、file-entry-cache、cacheable-request 接连中招。安全公司 Aikido 在 8 月 5 日的通报里给出的口径是:至少 444 个包、1381 个恶意版本被确认。知乎
为什么这事和你有关?大多数前端项目不会直接安装 keyv,但它藏在一条极其常见的依赖链里:eslint → file-entry-cache → flat-cache → keyv。安全公司 Wiz 的统计显示,这几个包在云与代码环境中的出现率都在 46% 左右。知乎也就是说,只要你用 ESLint,这条链大概率就在你的 lockfile 里。
这轮不是孤立事件:2026 年的 npm 投毒已经"按月连载"
把时间线拉长看,8 月这波只是最新一集。我按公开报告整理了一份时间线:
3 月 31 日:axios 维护者的 npm 发布身份被滥用,约 39 分钟内连发 1.x 与 0.x 两个带后门的版本,恶意依赖提前 18 小时预埋,安装即触发 postinstall 后门;
5 月中旬:TanStack、Mistral AI 等 160 多个包被投毒,攻击者通过污染 GitHub Actions 缓存,让恶意包带着有效的 provenance 签名发布;
6 月 1 日:Red Hat 官方命名空间 @redhat-cloud-services 下 32 个包因员工账号泄露被投毒;
8 月 4 日起:keyv 系投毒 + 蠕虫扩散(本文主角);
8 月 17 日:解压库 @xhmikosr/decompress(周下载 280 万+,大量 CI 流水线和 Docker 构建在用)曝出路径遍历漏洞,CVSS 9.1;
8 月 18 日起:安全公司 Socket 披露另一起针对阿里巴巴开发环境的定向攻击——攻击者注册了一批与阿里内部私有包名称高度相似的包,把攻击逻辑拆散到多个依赖里,单独看每个模块都"功能正常",组合起来才形成完整攻击链,最终投递远程木马。栗子前端技术周刊
同期还有一份火绒安全实验室的报告值得注意:8 月这轮蠕虫的升级变种已经把 C2 地址藏进了区块链合约,通过 72 个 RPC 节点动态拉取。火绒安全检测难度明显上台阶。

三个流传很广的误区,恰恰是这次最危险的地方
误区一:“npm 官方都下架了,没事了。”
下架只意味着"装不到新的",不代表"你没装过"。恶意版本会留在三个地方:你的 lockfile、CI 的旧构建缓存、以及企业私有镜像源。尤其要注意最后一条:很多内部 npm 镜像不会自动同步官方的删除操作,毒版本会在内网长期驻留。知乎
误区二:“有 provenance 签名的版本可以信任。”
这次的 keyv@6.0.0 就带通过验证的 provenance。知乎因为签名证明的是"由哪条官方流水线构建发布",而这条流水线拿到的源代码已经被污染了。签名证明不了源码本身安全,5 月的 TanStack 事件已经验证过一次,这次是第二次。

误区三:“发现中招,第一时间换密钥。”
这是处置顺序里最容易踩的坑。根据 Socket 的披露,这轮恶意程序会每 60 秒检查被盗的 GitHub token 是否仍然有效,一旦发现被撤销,可能执行远程下发的处理命令。知乎没有事件响应经验的团队,更稳妥的顺序是:先断网隔离主机 → 排查确认影响范围 → 由安全人员清除持久化项 → 最后才轮换凭据。不要在生产环境里"试着触发它看看"。

自查清单:按你的角色,挑对应档位做
① 5 分钟档:确认你的项目有没有解析到毒版本
先看 lockfile(注意:不是看 package.json,间接依赖不在那里):
```bash
grep -E “keyv|flat-cache|file-entry-cache|cacheable-request” package-lock.json
```
目前已公开的恶意版本号:`keyv@6.0.0`、`flat-cache@6.1.24`、`file-entry-cache@11.1.6`、`cacheable-request@13.0.20`。名单仍在更新,以安全厂商的持续通报为准。pnpm / yarn 用户对应检查 `pnpm-lock.yaml` / `yarn.lock`。
② 30 分钟档:确认"装过"是否等于"执行过"
版本解析到了,不等于载荷执行了。关键分界线是安装时有没有跑生命周期脚本:
如果安装过恶意版本、且没有使用 `–ignore-scripts`:按"主机/CI Runner 已失陷"处理,不要只删 node_modules、改密码了事。攻击者拿到的可能是能发布你的包、访问生产环境、横向移动的完整机器身份;
排查要点:开发机和 CI 上的 `setup.mjs`、`Math_Symbol.js` 等异常文件,以及开机启动项、计划任务;
盘点凭据暴露面:这轮载荷的搜集范围包括 npm token、GitHub 凭据、AWS/GCP/Azure 云密钥、Kubernetes 配置,甚至 Claude、Cursor、Codex 等 AI 编码工具的本地配置——用 AI 写代码的同学,这一项别漏。知乎

③ 团队档:把这次的教训固化成流程
CI 里的安装命令改用 `npm ci --ignore-scripts`,对确需构建脚本的包(esbuild、sharp 这类)单独白名单放行;
确认私有镜像源是否同步清理了恶意版本,这一步不做,前面都白查;
npm 账号和 CI token 全部开启 2FA(有条件上硬件密钥),token 改最小权限 + 有效期;
给关键依赖配"异常发版提醒"——这次蠕虫的套路就是给受害者的包偷偷 +patch 版本再发布,非预期的小版本跳动值得报警。
npm 官方动手了,但别指望一劳永逸
这波攻击也逼出了 npm 的两个新机制:发布时恶意软件扫描(新版本发布前自动扫描,可疑版本可能进入人工审核)和分阶段发布(维护者先上传到暂存区,检查 tarball、2FA 确认后才正式公开)。知乎
方向是对的——从"上架后删"变成"发布前拦"。但要清楚两点:人工审核只针对可疑版本,不是逐个审;确认动作由包维护者完成,而这次所有事件的起点恰恰是维护者账号被攻破。门槛提高了,攻击不会停。
接下来值得盯的三个信号
恶意包名单还在更新。目前各源口径不一(444 / 868 / 1300+,统计时间和口径不同),建议跟 Aikido、Socket 的持续通报,别只认一个数字;
你的私有镜像清干净没有。这是很多企业团队未来几周真正的风险残留点;
decompress 那个 9.1 分的漏洞。如果你的 CI 或构建脚本里有解压逻辑,值得单独排一下。知乎
另外顺带一提:这个月前端工程化圈子还有一个容易被忽略的坑——Vite 开发服务器被 `–host 0.0.0.0` 暴露到公网,红队已经在拿 5173 端口当常规突破口了。知乎自查依赖之余,也看一眼你本机有没有对公网开着的 dev server。
这次事件浓缩成一句话:在 npm 生态里,`npm install` 早就不是一次单纯的下载,安装脚本、本地凭据、维护者账号、CI 流水线,全都是供应链的一部分。清单不长,建议收藏,然后在你的团队里真的跑一遍。