一周内三次。 三次。到了这个地步,我开始认为NPM代表”永不停止的被黑机器”(Never-ending Pwning Machine),因为天哪,又一个供应链攻击,而且这次简直绝妙到令人发指。
认识一下Shai-Hulud(是的,这是真实的代号——以《沙丘》中的巨型沙虫命名,因为黑客们显然现在迷上了科幻引用)。这不是你常见的”糟糕,我下载了一个恶意软件包”的情况。不不不。这是一场自我复制的数字瘟疫,从一个仓库传播到另一个仓库,就好像它在争夺”最具创意的毁掉所有人一天的方式”诺贝尔奖。
让安全工程师哭泣的剧情转折
还记得恶意软件简单的美好时光吗?下载坏软件包,恶意软件窃取你的令牌,游戏结束。Shai-Hulud说”拿好我的啤酒”,决定以最糟糕的方式进行创新。
这个美人不仅仅窃取你的凭证就收工。它使用你被盗的令牌来感染其他仓库。就像一个窃贼不仅抢了你的房子,还用你的钥匙去抢你所有邻居,然后他们的钥匙被盗去抢他们的邻居,依此类推,直到你整个城市变成一个巨大的犯罪现场。
在Shai-Hulud攻击中被入侵的主要软件包包括@ctrl/tinycolor、@crowdstrike/falcon-shoelace、ngx-bootstrap等180多个。攻击针对多个维护者的流行库,从UI组件到安全工具,展示了这次供应链入侵的巨大范围。来源:OX Security
攻击始于一个名为rxnt-authentication的卑微、已弃用的软件包,这个软件包多年来都没有更新。然后突然——剧情转折!——0.0.5和0.0.6版本像数字幽灵一样出现,将其恶意负载带入了野外。
数字达尔文主义的解剖
以下是技术上既迷人又可怕的地方:
第1步:钓鱼钩 某个可怜的开发者可能收到了一封令人信服的电子邮件:“嘿,出于安全原因,你需要更新你的npm令牌!点击这里!“*点击。*游戏结束。
第2步:特洛伊木马 攻击者使用补丁更新推送恶意版本(因为显然我们仍然没有学会不要自动升级补丁版本——真的,各位,锁定你的版本)。
第3步:数字寄生虫 一旦安装,软件包运行一个安装后脚本,部署一个叫做”Truffle Hog”的东西——顺便说一句,这既是一个绝妙的名字,也是一个扫描泄露凭证的合法安全工具。只是现在它被用于邪恶而不是善良。就像看着超人变成银行抢劫犯。
第4步:病毒传播 这是杰作:Shai-Hulud不是只把你的令牌外泄到某个远程服务器,而是立即开始在你的GitHub仓库上工作。它:
- 更新你的CI/CD管道
- 修改你的package.json文件
- 发布你的软件包的新”补丁”版本
- 在新版本中包含它自己
冲洗、重复、指数增长。就像一个传销骗局,但是为了恶意软件。
数字游戏(剧透:我们正在输)
攻击始于40个被入侵的软件包。几小时内,这个数字跳到了180+。按照这个增长率,到下周我们将拥有比可观测宇宙中的原子更多的被感染软件包。(好吧,这是夸张,但指数增长确实可怕。)

但等等——还有更多!在受害者中,我们有讽刺的王冠上的宝石:CrowdStrike!
是的,你没看错。同一家销售安全解决方案并在今年早些时候给我们带来伟大的蓝屏死机事件的CrowdStrike,现在——鼓声请——正在通过其npm软件包意外分发蠕虫攻击。具体来说,攻击入侵了:
@crowdstrike/commitlint(版本8.1.1、8.1.2)@crowdstrike/falcon-shoelace(版本0.4.1、0.4.2)@crowdstrike/foundry-js(版本0.19.1、0.19.2)@crowdstrike/glide-core(版本0.34.2、0.34.3)@crowdstrike/logscale-dashboard(版本1.205.1、1.205.2)@crowdstrike/tailwind-toucan-base(版本5.0.1、5.0.2)
你真的编不出这种故事。宇宙有幽默感,而那种幽默感显然非常、非常黑暗。
“Tiny Color”悲剧
另一个值得一提的受害者是流行的tiny-color软件包,通过其维护者被入侵的凭证被攻击。版本4.11和4.12被感染了,但在一个美丽的反抗幽默行为中,安全版本被发布为…4.20。
厨师之吻给任何做出那个版本号决定的人。在混乱中,有人仍然有时间开玩笑。
我们创造的依赖地狱
这次攻击突显了我们一直在故意忽视的事情:我们的依赖树是疯狂的。我们为了使用单个函数而导入整个库。需要填充字符串?导入left-pad。需要检查某物是否为数字?导入is-number。需要将HSV转换为RGB?导入另一个有47个传递依赖的软件包。
在AI时代,你可以直接问ChatGPT”给我写一个left-pad函数”并在3秒内获得一个可工作的实现,为什么我们还在玩依赖俄罗斯轮盘赌?
自动更新成瘾
我们能谈谈自动更新补丁版本吗?**停止它。**就…停止。这是今年第三个依赖自动补丁版本更新的重大攻击。现在是2025年,我们仍然一遍又一遍地犯同样的错误。
锁定你的版本。是的,这是更多的工作。是的,你会错过一些bug修复。但你知道你不会错过什么吗?意外安装自我复制的恶意软件。
问题在于:即使你是一个好公民并锁定了你的版本,你仍然容易受到攻击,因为包管理器是递归的噩梦。你依赖软件包A,它依赖软件包B,软件包B自动更新以包含恶意软件,然后无论如何都会感染你的项目。
依赖一直往下走,每一层都是潜在的攻击向量。
现实世界的影响
这不仅仅是理论上的。真实的公司、真实的项目、真实的生产环境被攻击了。攻击针对:
- 暴露了GitHub令牌的开发环境
- 有发布凭证的CI/CD管道
- 有缓存认证的本地开发者机器
如果你有npm令牌存放在任何地方——在你的环境变量中、你的CI配置中、你浏览器保存的密码中——Shai-Hulud正在瞄准它们。
防御策略(或:如何不被黑)
1. 锁定一切
{
"dependencies": {
"some-package": "1.2.3", // 精确版本,没有波浪号,没有插入符
"another-package": "4.5.6"
}
}
2. 像你的生命取决于它一样审计
虔诚地运行npm audit。设置自动依赖扫描。把你的package.json当作包含核发射代码一样对待。
3. 最小化依赖 问问自己:“我真的需要一个软件包来检查一个数字是否是奇数吗?“答案可能是不需要。
4. 轮换你的令牌 如果你最近没有轮换你的npm令牌,现在就做。如果你有2019年的令牌仍在飘荡,昨天就删除它们。
5. 监控你的软件包 如果你维护软件包,为意外发布设置警报。如果你没有发布的3.1.4版本突然出现,那是一个德克萨斯大小的红旗。
更大的图景
Shai-Hulud代表了供应链攻击的进化。它不仅仅是恶意软件——它是自我传播的恶意软件。这是当攻击者研究网络效应并将其应用于网络犯罪时会发生的事情。
这次攻击成功是因为它利用了几个系统性问题:
- 过度依赖自动依赖更新
- 整个生态系统中糟糕的令牌卫生
- 现代包管理的互联特性
- 开发者隐式信任补丁更新的倾向
这对未来意味着什么
我们正处于一个拐点。要么我们集体认真对待供应链安全,要么我们将看到更多这样的攻击。Shai-Hulud证明了npm的生态系统可以被武器化为一个分布式攻击平台。
下一次攻击可能更糟糕。它可能针对Docker镜像、Python软件包或Rust crates。它可能持续更长时间,传播更快,或造成更多损害。
一线希望
有一些好消息:这次攻击被相对快速地发现了,受影响的软件包正在被清理,安全社区正在迅速响应,提供更好的检测工具和实践。
而且,无论我们是否想要,我们都在接受供应链安全的速成课程。没有什么比看着你的整个生态系统被自我复制的软件包病毒攻陷更能教会防御性编程了。
惨痛教训
- 自动更新方便直到它们是灾难性的
- 每个依赖都是潜在的攻击向量
- 令牌轮换不是可选的——它是必要的
- 写自己的实用函数的成本现在低于导入它们的风险
- 安全不是别人的问题——它是每个人的问题
最后的想法
在我写这篇文章的时候,Shai-Hulud的清理工作仍在进行中。软件包正在被修补,令牌正在被轮换,世界各地的开发者正在进行关于他们依赖管理实践的不舒服对话。
这次攻击的简单性是辉煌的,其有效性是可怕的。它把npm最大的优势——其互联的生态系统——变成了最大的弱点。
但问题在于:我们可以从中学习。我们可以构建更好的工具,实施更好的实践,创建更有弹性的系统。或者我们可以忽视教训,等待Shai-Hulud 2.0。
选择在我们手中。让我们做出正确的选择。
保持警惕,保持你的依赖锁定,记住:在包管理的世界里,偏执狂不是bug——它是特性。
资源:
负责任地更新你的软件包,朋友们。生态系统取决于它。