本文基于2026年4月5日axios供应链投毒事件,深入分析攻击手法与防护策略。
一、事件回顾:axios投毒攻击时间线
2026年3月底,全球每周下载量超过1亿次的axios库遭遇供应链投毒攻击。这起攻击的精密程度和隐蔽性,堪称npm生态有记录以来最严重的供应链安全事件之一。
攻击时间线
| 时间点 | 事件 |
|---|---|
| T-18小时 | 攻击者发布 plain-crypto-js@4.2.0(干净版本) |
| T-30分钟 | 发布 plain-crypto-js@4.2.1(注入恶意postinstall脚本) |
| T=0 | 用被盗账号发布 axios@1.14.1 |
| T+39分钟 | 发布 axios@0.30.4(覆盖0.x版本线) |
| T+3小时 | npm下架恶意版本,攻击窗口关闭 |
关键数据:
- 攻击窗口仅3小时
- 18小时预埋养号
- 39分钟覆盖两条版本线
- 恶意载荷在npm install开始后2秒内就开始回传信息
二、攻击手法深度剖析
1. 绕过GitHub的发布链攻击
传统的供应链攻击通常通过篡改源码实现,但这次攻击完全绕过了GitHub:
攻击路径:
盗取npm token → 直接发布到npm registry → 绕过GitHub审查/CI/CD
为什么GitHub上看不出来?
- npm registry和GitHub是两个独立系统
- 有维护者的npm token就能直接往registry发包
- 合法的axios版本使用GitHub Actions的OIDC Trusted Publisher机制发布
- 恶意版本
1.14.1没有OIDC绑定,没有gitHead
2. 隐蔽的"养号"策略
攻击者先发布干净版本plain-crypto-js@4.2.0,建立发布历史:
- 目的:在npm上建立发布历史,避免新包被依赖时触发安全警报
- 时间:18小时的"养号"期
- 策略:内容就是合法crypto-js的复制品,零恶意代码
这种"先干净后恶意"的策略,让安全检测系统难以在发布瞬间识别威胁。
3. postinstall脚本的利用
恶意代码隐藏在postinstall钩子中:
{
"scripts": {
"postinstall": "node malicious-script.js"
}
}
执行流程:
npm install触发postinstall- 检测操作系统
- 下载远控木马
- 连接攻击者服务器
- 自我删除
- 替换
package.json为干净版本
关键:事后翻node_modules,什么痕迹都没有。
三、攻击的精妙之处
1. 供应链的"信任链"攻击
这次攻击针对的不是代码本身,而是发布链的信任机制:
开发者信任链:
GitHub代码 → CI/CD验证 → npm registry → 我的项目
攻击者破坏点:
GitHub代码 ──X── CI/CD验证 ──X── npm registry ← 攻击者直接注入
2. 版本号的"兼容性"陷阱
axios使用语义化版本控制,攻击者发布1.14.1和0.30.4:
^1.14.0会自动升级到1.14.1^0.29.0会自动升级到0.30.4- 大多数项目的
package.json都使用^前缀
教训:兼容性升级机制成了攻击的放大器。
3. 速度优势
- 恶意载荷在npm install开始后2秒内就开始回传信息
- 比npm解析完其他依赖还快
- 用户来不及按Ctrl+C
四、防护措施:从被动到主动
第一层防护:lockfile锁定
检查lockfile:
# 搜索关键词
grep -E "axios@1\.14\.1|axios@0\.30\.4|plain-crypto-js" package-lock.json
为什么lockfile重要?
- lockfile锁定的是具体的版本和hash
- 即使package.json使用
^,lockfile也会阻止自动升级 - CI/CD环境依赖lockfile保证构建一致性
第二层防护:版本锁定策略
去掉^前缀:
{
"dependencies": {
"axios": "1.14.0" // 不是 "^1.14.0"
}
}
更激进的方案:
# .npmrc
ignore-scripts=true
⚠️ 注意:这会禁用所有postinstall脚本,可能影响esbuild、sharp等包的构建。
第三层防护:审计install脚本
# 检查所有postinstall脚本
grep -r '"postinstall"' node_modules/*/package.json
# 或使用npm audit
npm audit
第四层防护:缓存清理
# 本地清理
npm cache clean --force
# CI环境也要清理
# GitHub Actions、GitLab CI的node_modules缓存和npm缓存
为什么重要?
- 如果缓存中残留恶意tarball,下次构建还会命中
- 清理缓存强制重新下载,此时恶意版本已被下架
第五层防护:Registry元数据检查
检查发布者签名:
- 合法的axios版本使用OIDC Trusted Publisher
- 检查npm registry元数据中的
gitHead和签名信息 - 手动发布的版本缺少这些标记
五、供应链安全的系统性思考
1. 大包≠安全
很多开发者的直觉:axios每周1亿下载,总不会出事吧?
现实:越热门的包,攻击者的投入产出比越高。
- 投毒每周100下载的包 → 没人在乎
- 投毒每周1亿下载的包 → 3小时窗口覆盖成千上万个项目
2. 信任链的重新设计
我们需要重新思考从代码到生产环境的信任链:
理想信任链:
源码 → 签名 → 可重现构建 → 哈希验证 → 生产
当前问题:
源码 ──X── 签名 ──X── 可重现构建 ──X── 哈希验证 → 生产
改进方向:
- 强制使用OIDC Trusted Publisher
- 可重现构建(Reproducible Builds)
- 供应链SBOM(Software Bill of Materials)
- 实时依赖监控
3. 安全左移
传统的安全策略是"右移"——在生产环境发现问题。
左移策略:
- 开发阶段:依赖扫描(Snyk、Dependabot)
- CI阶段:构建时验证
- 发布阶段:签名和审计
- 运行阶段:行为监控
六、给开发者的建议
短期(立即执行)
- ✅ 检查lockfile中是否有
axios@1.14.1或axios@0.30.4 - ✅ 清理npm缓存
- ✅ 锁死axios版本(去掉
^) - ✅ 审计其他依赖的postinstall脚本
中期(本周内)
- 🔍 启用依赖自动扫描(GitHub Dependabot、Snyk)
- 🔍 审查CI/CD中的缓存策略
- 🔍 建立依赖变更通知机制
长期(本月内)
- 🛡️ 评估使用私有registry(Verdaccio、Nexus)
- 🛡️ 实施依赖签名验证
- 🛡️ 建立供应链安全审计流程
七、结语
axios投毒事件不是终点,而是起点。
它揭示了JavaScript生态供应链安全的系统性脆弱:
- 我们默认信任npm registry
- 我们默认信任版本号的语义
- 我们默认信任postinstall脚本
这些"默认信任"在攻击者面前不堪一击。
关键认知转变:
不是"axios源码没问题就安全",而是"从代码到registry的整个链路都需要验证"。
供应链安全不是有空再搞的事,而是需要融入日常开发流程的基础设施。
参考来源:
- axios供应链投毒事件分析报告
- StepSecurity安全公司评估
- npm registry安全最佳实践
标签: #供应链安全 #npm #axios #JavaScript安全 #postinstall #lockfile
发布时间: 2026年4月5日 20:00