背景:什么是 Axios?
Axios 是 JavaScript 生态系统中最受欢迎的 HTTP 客户端库之一。无论是 React 前端应用、CI/CD 工具链还是服务器端 API,几乎所有需要发起 HTTP 请求的 Node.js 或浏览器项目都可能用到它。其每周下载量超过3亿次,这意味着即使是一个小版本被植入恶意代码,其影响范围也会极其广泛。通常,开发者在执行 npm install 或 npm update 时,并不会对官方维护者发布的软件包产生怀疑。
对于当下依赖 AI 辅助编程的开发者而言,此类事件的实际影响可能更为严重,因为 AI 工具会自动引入并信任这些依赖。
受影响版本
恶意版本具体为:
axios@1.14.1
axios@0.30.4
事件概述
2026年3月31日,安全公司 StepSecurity 识别出两个被上传到 npm 的恶意 Axios 版本。攻击者攻陷了 Axios 项目一位核心维护者的 npm 账户凭证,绕过了项目正常的 GitHub Actions CI/CD 流程,手动发布了这两个带毒的软件包。攻击者将账户绑定的邮箱改为了一个匿名的 ProtonMail 邮箱。
恶意版本注入了一个名为 plain-crypto-js@4.2.1 的新依赖,该依赖从未在 Axios 的源代码中被导入。它的唯一目的是在其 package.json 中定义一个 postinstall 脚本,执行一个作为跨平台远程访问木马(RAT)的投放器,目标系统涵盖 macOS、Windows 和 Linux。该投放器会连接活着的命令控制服务器,下载并执行针对特定平台的第二阶段恶意负载。执行完毕后,恶意软件会进行自我清除,并用一个干净的版本替换掉自身的 package.json 以规避取证检测。
这两个恶意的 Axios 包内部并不包含恶意代码,其攻击完全通过引入的恶意依赖 plain-crypto-js@4.2.1 实现。如果你安装了 axios@1.14.1 或 axios@0.30.4,应假设你的系统可能已被入侵。
攻击时间线
整个攻击经过了约18小时的预谋策划。攻击者提前在 npm 上发布了恶意依赖,以避免安全扫描器发出“全新软件包”的警报:
- 2026-03-30 05:57 UTC:
plain-crypto-js@4.2.0 发布。这是一个干净的“诱饵”包,包含合法 crypto-js 库的完整副本,没有安装后钩子。其唯一目的是为该 npm 账号建立发布历史,使后续的恶意包看起来不那么可疑。
- 2026-03-30 23:59 UTC:
plain-crypto-js@4.2.1 发布。此版本添加了恶意负载,引入了 ”postinstall”: “node setup.js” 钩子和经过混淆的投放器。
- 2026-03-31 00:21 UTC:
axios@1.14.1 由被攻陷的账户发布。它注入了 plain-crypto-js@^4.2.1 作为运行时依赖,目标是使用现代 1.x 分支的用户。
- 2026-03-31 01:00 UTC:
axios@0.30.4 由同一被攻陷账户发布。此举旨在感染仍在使用遗留 0.x 分支的项目,以最大化攻击覆盖范围。
如何排查
检查项目中是否有恶意版本
你可以通过以下命令检查项目是否引入了恶意版本:
# 检查项目中是否有恶意版本
npm list axios 2>/dev/null | grep -E "1\.14\.1|0\.30\.4"
# 或者检查 package-lock.json 文件
grep -A1 '"axios"' package-lock.json | grep -E "1\.14\.1|0\.30\.4"
检查 node_modules 中的恶意依赖
即使 Axios 版本号正确,也应检查是否引入了恶意依赖包:
#检查 node_modules 中的 plain-crypto-js
ls node_modules/plain-crypto-js 2>/dev/null && echo “可能受影响”
阻断恶意网络连接
如果怀疑系统已受影响,可以在防火墙或 hosts 文件中阻断已知的恶意命令控制服务器地址:
# 使用 iptables 阻断出站连接 (Linux)
iptables -A OUTPUT -d 142.11.206.73 -j DROP
# 或通过 hosts 文件屏蔽域名
echo “0.0.0.0 sfrclak.com” >> /etc/hosts
你也可以借助一些具备安全分析能力的 AI 编程助手来辅助进行依赖安全检查。
攻击机制详解
第一步:维护者账户劫持
攻击者成功攻陷了 Axios 主要维护者 jasonsaayman 的 npm 账户,并将其注册邮箱改为攻击者控制的 ifstap@proton.me。利用此权限,攻击者同时在 1.x 和 0.x 版本分支上发布了恶意构建。
从 npm 注册表的元数据中可以发现关键区别。所有合法的 Axios 1.x 版本都通过 GitHub Actions 配合 npm 的 OIDC 可信发布者机制发布。而恶意版本 axios@1.14.1 完全打破了这一模式——它是使用被盗的长期 npm 访问令牌手动发布的,没有 OIDC 绑定,且在 GitHub 仓库中没有对应的 gitHead、提交或标签。
合法版本与恶意版本的 _npmUser 字段对比:
// axios@1.14.0 — 合法的发布记录
“_npmUser”: {
“name”: “GitHub Actions”,
“email”: “npm-oidc-no-reply@github.com”,
“trustedPublisher”: {
“id”: “github”,
“oidcConfigId”: “oidc:9061ef30-3132-49f4-b28c-9338d192a1a9”
}
}
// axios@1.14.1 — 恶意的发布记录
“_npmUser”: {
“name”: “jasonsaayman”,
“email”: “ifstap@proton.me”
// 没有 trustedPublisher 字段,没有 gitHead,在 GitHub 上没有对应的 commit 或 tag
}
第二步:恶意依赖的预先部署
在发布后门 Axios 版本之前,攻击者使用一个独立的一次性账户 (nrwise, nrwise@proton.me) 预先在 npm 上部署了恶意包 plain-crypto-js@4.2.1。
这个包被精心伪装:
- 它伪装成流行的
crypto-js 库,拥有相同的描述和作者信息。
- 它包含
”postinstall”: “node setup.js” 钩子,使得在 npm install 时会自动执行恶意脚本。
- 它预置了“证据销毁”机制,包含一个名为
package.md 的文件,其中是一个干净的 package.json 存根(版本 4.2.0,无 postinstall 钩子),用于在攻击完成后覆盖真实的清单文件。
第三步:将恶意依赖注入 Axios
攻击者发布的 axios@1.14.1 和 axios@0.30.4 都在其 package.json 的 dependencies 中添加了 ”plain-crypto-js”: “^4.2.1″。这是与之前干净版本唯一的外科手术式差异。
依赖对比:
axios@1.14.0 — follow-redirects, form-data, proxy-from-env [干净]
axios@1.14.1 — follow-redirects, form-data, proxy-from-env, plain-crypto-js@^4.2.1 [恶意]
axios@0.30.3 — follow-redirects, form-data, proxy-from-env [干净]
axios@0.30.4 — follow-redirects,form-data,proxy-from-env,plain-crypto-js@^4.2.1 [恶意]
当开发者运行 npm install axios@1.14.1 时,npm 会解析并自动安装 plain-crypto-js@4.2.1,随后触发其 postinstall 脚本,启动攻击链。
关键特征——幻影依赖:通过对 axios@1.14.1 所有 86 个源文件进行验证,确认其中从未导入或引用 (require) 过 plain-crypto-js。这个依赖仅仅是为了触发 postinstall 钩子而存在。在 package.json 中出现但在代码库中零使用的依赖,是判断软件包是否被攻破的高置信度指标。