← 随机比特 / 所有内容

axios 被投毒这件事最可怕的,不是有人往源码里塞了恶意代码,而是他们几乎什么都没改,只多加了一条依赖,就让一次普通的 `npm install` 变成了中招入口。

2026-04-01 · 随机比特

你以为在升级 axios,结果先把远控装进了电脑

你只是想装个依赖。axios 又是天天见的老朋友。可这次出事的方式,偏偏不写在源码里。

StepSecurity 在三月底披露:npm 上曾短暂出现过两个恶意版本 [email protected][email protected]。它们和正常版相比,只多了一条依赖,却能在你跑 npm install 时拉起跨平台远控投放器。文中称 axios 周下载量在亿级规模,所以波及面很广。若你当时装过上述版本,官方与安全方都建议按已失陷处理并做完整排查。

恶意代码不在 axios 里,在谁身上

按同一篇文章的拆解,axios 本体里没有被塞进恶意逻辑。攻击者做的是:在 package.json 里多加一个运行时依赖 plain-crypto-js,版本指向 ^4.2.1。这个包在 axios 源码里从未被 import 或 require,存在的目的就是让你安装树里多出它,从而触发它的安装脚本。对你而言,体感仍是「装 axios」,真正被拉起的是下游那个小包的生命周期脚本。

[email protected] 里加了 postinstall: node setup.js。也就是说,危险发生在安装阶段,不是你业务代码去调了某个函数。StepSecurity 还提到,安装后约两秒内就可能出现对攻击者 C2 的回连,时机早于很多人直觉里的「依赖装完再说」。

伪装链:先干净,再动手

文章里的时间线写得很细,这几步和普通「随手发个毒包」不一样。

攻击者先用 [email protected] 在 npm 上发了 [email protected]:里面是完整、干净的 crypto-js 同源文件,没有 postinstall。StepSecurity 认为,这一步是为了攒发布历史,让后面那个包不像「零历史新号」,从而降低自动化扫描的警觉。

大约 18 小时后,同一账号再发 [email protected],才加上 postinstall 和混淆过的 setup.js

接着,被劫持的 axios 维护者账号在约 39 分钟内,先后往 1.x0.x 两条线各推了一版带这条依赖的 axios。npm 随后下架了这两个 axios 版本,并对 plain-crypto-js 做了安全占位替换。更细的节点以原文表格为准,这里不逐条复述。

事后查 npm list 也可能被误导

更阴的一步在反取证。StepSecurity 写道:setup.js 跑完后会自删,并用事先藏好的干净 package.json 替身(文中称来自 package.md)覆盖当前目录里的清单。替身里写的版本号是 4.2.0,不是 4.2.1

因此,感染之后再执行 npm list plain-crypto-js有可能显示成 4.2.0。你若只对照「有没有装过 4.2.1」这一条,可能得出错误结论。原文强调更稳的做法是看 node_modules/plain-crypto-js 目录是否出现过——合法 axios 根本不该依赖这个包名。

和你习惯的「安全习惯」错在哪

很多团队的习惯仍是:看主仓库 diff、读 CHANGELOG、扫一眼大版本说明。这次攻击在 axios 侧几乎是 只改 package.json 一条依赖,其余大量文件可与前一干净版逐字节一致(原文对 1.14.0 vs 1.14.1 有文件级对比结论)。

所以单靠「axios 源码看起来没事」不够。更值得盯的是:有没有幽灵依赖(声明在 manifest 里、代码里永远不用)、postinstall / preinstall、以及 发布元数据是否异常——例如原文指出,正规 1.x 发布多走 GitHub Actions 的 npm OIDC Trusted Publisher,而这次恶意 1.14.1 在 registry 里体现为人工 token 发布、且 GitHub 上找不到对应 tag。这些是「供应链」里比源码更靠外的一环。

锁文件不是万能,但有锁比没锁强:至少能回答「当时解析到的到底是哪几个版本」。若团队长期忽略 lock 或随便删锁重装,事后想对齐时间线会更痛苦。pnpm、yarn、npm 各家锁格式不同,道理一样:把可复现安装当成安全基线,而不是纯运维细节。

<figure><img src=“images/01-compare.png” alt=“01-compare”></figure>

时间线一图流(便于对齐事件顺序)

<figure><img src=“images/02-timeline.png” alt=“02-timeline”></figure>

你现在可以做的几件事(不夸大、可执行)

第一,查清项目锁文件里是否曾解析到 [email protected][email protected]。有则按安全事件响应流程处理,不要赌运气。

第二,对新增依赖lifecycle scripts 提高敏感度。尤其是名字像知名库变体、描述却指向正经仓库的包,要对照发布者与 registry 元数据。

第三,CI 里能记录出站连接的监控(原文举了 Harden-Runner 在开源仓库里抓到异常 C2 的例子)对这类「装完就跑」的行为有帮助。这不是说只有某一种工具才行,而是安装阶段的网络行为本身就是信号。

Hacker News 上同题讨论里,不少人在补「本地怎么自查」「企业里谁该背锅」——情绪之外,共识大致是:别再把 npm 安装当成纯透明管道。你信任的既是包名,也是发布链路与生命周期脚本。

讨论

你平时装依赖时,最容易跳过的是哪一步?是锁版本、看 postinstall,还是核对发布身份?


参考:StepSecurity 官方博客《axios Compromised on npm》技术分析;开发者社区亦有大量讨论可作对照。