引子:一位开发者的七年回声
2018 年 7 月 12 日,Jamie Kyle 敲下了这样一段文字——
“如果有人真的想做,他们完全可以写一个 npm 蠕虫:能够凭借维护者账户权限,把恶意版本推送到下游成千上万个项目中。我们唯一的防线是什么?人性。人们没做,只是因为他们是好人。”
他甚至用一个失败的样本 Fluke 做了验证——只因代码里有 bug,否则它可能会成为首个自我传播的 npm 攻击。
npm 官方的回应是“按预期工作,无意修复”。这一句话,为七年后 JavaScript 史上最大规模的供应链攻防战埋下了伏笔。
第一幕:AI 初入场的黑色礼花 —— S1ngularity 攻击
时间:2025 年 8 月 26 日
目标:Nx 构建系统生态
影响:几小时内泄露 1,000+ GitHub Token、数十个云凭证、约 2 万个敏感文件
作案过程:
- 巧妙撬锁——攻击者利用 GitHub Actions 工作流注入漏洞,将恶意代码混入 CI 流程。
- “偷天换日”——窃取到长期有效的 npm 发布令牌(
NPM_TOKEN)。 - AI 武器化——调用本地 CLI 版 Claude / Gemini / Amazon Q,生成“定制化”扫描策略,秒懂文件结构与命名模式,找到最值钱的密钥文件。
- 无声搬运——双重 Base64 编码,上传至以
s1ngularity-repository为前缀的公共 GitHub 仓库。
🚨 关键突破:这是历史上第一次,AI 工具被编织进供应链恶意代码执行链,成为攻击者的中枢神经系统,使扫描与窃取更隐蔽、更精准。
第二幕:热门依赖的精准狙击 —— 钓鱼劫持核心包
时间:2025 年 9 月 8 日
目标:chalk、debug、ansi-styles、strip-ansi 等 18 个核心依赖
影响:累计每周下载量超过 26 亿,堪称“毒源入血”
攻击向量:
- 邮件发件人:
support@npmjs.help - 主题:紧急:需要在 XX 日前更新双因素认证
在一个疲倦的早晨,核心维护者 Josh Junon 点击了钓鱼链接——
一次 AiTM 劫持,即时夺走了他的密码与 2FA 短信 Token。
结果:这批支撑无数 CLI 工具、构建框架、终端渲染的基础库,瞬间被注入后门。
⚠️ 依赖链杀伤效应:当基础组件被毒化,相当于在操作系统的
libc下毒,几乎所有上层应用都会入口感染。
第三幕:沙丘巨虫出击 —— Shai‑Hulud 蠕虫
时间:2025 年 9 月 14 日至 15 日
攻击模型:首次实现 npm 生态自我复制传播
蠕虫传播算法(简化伪码)
function propagate() {
const token = findNpmToken();
if (!token) return;
const pkgs = getAccessiblePackages(token)
.sort(by('downloads'))
.slice(0, 20);
pkgs.forEach(pkg => {
injectPayload(pkg);
publishNewVersion(pkg, token);
});
}
“技能树”全开:
- 自动横向移动:发现新
NPM_TOKEN→ 全面接管可访问包。 - 持久化:植入恶意 GitHub Actions 到受害者仓库。
- 外泄链路:
- 创建 “Shai-Hulud” 公共仓库,双 Base64 存放窃取的密钥
- Webhook 实时推送
- 数据层突破:扫描
.ssh/、.aws/credentials、.env,以及云服务元数据接口(AWS IMDS、GCP/Azure Metadata)。
深度溯源:为什么 npm 抵挡不住?
- 长期有效 Token + 无发布审批 = 单点全死
一旦获取 Token,你就是这个账户下所有包的“上帝”。 - postinstall 的权限裸奔
没有容器隔离,没有权限沙箱,读写系统和连外网如入无人之境。 - CI/CD 权限配置松散
PR 与主干构建共用一套高权限 Secrets,等于把钥匙挂在大门外面。 - 平台级失职
GitHub/NPM(微软)掌握全球 JS 代码与包分发入口,却将核心安全特性(包签名、强制 2FA、脚本审查)设为可选。
实战级缓解方案
开发者(包使用者)
- 锁版本:提交
package-lock.json/yarn.lock并启用npm ci - 切换 pnpm 或安装时使用
--ignore-scripts - CI 集成依赖扫描:Socket.dev / Snyk
维护者(包发布者)
- 启用硬件 2FA
- 迁移至 OIDC Trusted Publishing:
permissions: contents: read id-token: write - 粒度化 Token:限制某个 Token 只能发布一个包
- 审核工作流权限:Secrets 按分支控制,仅必要范围开放
平台方(日光下的“裁判”)
- 将 OIDC 发布模式列为强制
- 默认禁用
postinstall - 对“高影响包”引入多签发布与人工审批
未来三大变数
- AI 驱动的“智能蠕虫”
可实时自适应目标环境与防御策略。 - 零信任供应链管理
不信任任何依赖,全部验签 + 沙箱执行。 - 法规硬约束
欧盟 CRA、美 NIST SSDF 等将要求开源供应链安全达到合规标准。
尾声:信任是砂,不是钢
Fluke 没成,Shai‑Hulud 成了。
七年时间,技术债、信任债一起复利滚成了沙丘巨虫。
这场攻击提醒我们:
- 供应链安全不是产品功能,而是系统生命线。
- 信任不能只寄托于人的善意。
- 安全不是锦上添花,而应是上线门槛。
代码世界的沙海很美,但在巨兽觉醒前,我们最好先把海底的地雷排干净。