npm供应链投毒攻击深度解析:从axios事件看JavaScript生态的安全防线

1 阅读6分钟

本文基于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"
  }
}

执行流程

  1. npm install触发postinstall
  2. 检测操作系统
  3. 下载远控木马
  4. 连接攻击者服务器
  5. 自我删除
  6. 替换package.json为干净版本

关键:事后翻node_modules,什么痕迹都没有。

三、攻击的精妙之处

1. 供应链的"信任链"攻击

这次攻击针对的不是代码本身,而是发布链的信任机制

开发者信任链:
GitHub代码 → CI/CD验证 → npm registry → 我的项目

攻击者破坏点:
GitHub代码 ──X── CI/CD验证 ──X── npm registry ← 攻击者直接注入

2. 版本号的"兼容性"陷阱

axios使用语义化版本控制,攻击者发布1.14.10.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阶段:构建时验证
  • 发布阶段:签名和审计
  • 运行阶段:行为监控

六、给开发者的建议

短期(立即执行)

  1. ✅ 检查lockfile中是否有axios@1.14.1axios@0.30.4
  2. ✅ 清理npm缓存
  3. ✅ 锁死axios版本(去掉^
  4. ✅ 审计其他依赖的postinstall脚本

中期(本周内)

  1. 🔍 启用依赖自动扫描(GitHub Dependabot、Snyk)
  2. 🔍 审查CI/CD中的缓存策略
  3. 🔍 建立依赖变更通知机制

长期(本月内)

  1. 🛡️ 评估使用私有registry(Verdaccio、Nexus)
  2. 🛡️ 实施依赖签名验证
  3. 🛡️ 建立供应链安全审计流程

七、结语

axios投毒事件不是终点,而是起点。

它揭示了JavaScript生态供应链安全的系统性脆弱:

  • 我们默认信任npm registry
  • 我们默认信任版本号的语义
  • 我们默认信任postinstall脚本

这些"默认信任"在攻击者面前不堪一击。

关键认知转变

不是"axios源码没问题就安全",而是"从代码到registry的整个链路都需要验证"。

供应链安全不是有空再搞的事,而是需要融入日常开发流程的基础设施。


参考来源:

  • axios供应链投毒事件分析报告
  • StepSecurity安全公司评估
  • npm registry安全最佳实践

标签: #供应链安全 #npm #axios #JavaScript安全 #postinstall #lockfile

发布时间: 2026年4月5日 20:00