周一晨会,开发同事说 CI 构建挂了。日志里一条报错很眼熟:某个 npm 包版本不存在了——被作者删了。我们查了一下,那个包上个月还在正常更新,下载量也不小。
后来发现那是个「打字错误包」:有人注册了和正规包只差一个字母的名字,发布恶意版本,等开发者手滑装错。那次没出事,但供应链投毒离我们真的只有一次 npm install 的距离。
—— 网络安全 · 黑客日常 · CYLOG 供应链投毒 一行依赖,端掉整条生产线 周一晨会,开发同事说 CI 构建挂了。日志
供应链投毒:你信任的依赖,可能是后门
现代项目 80% 代码来自第三方依赖。供应链投毒 = 攻击者把恶意代码塞进依赖包,开发者一安装就中招——攻击者不需要黑你的系统,他只需要让你「主动安装」他的恶意包。
投放方式有四种:①typosquatting(伪造相似包名:lodahs 冒充 lodash);②抢注被删除的包名;③入侵正规包作者账号上传恶意版本;④编译期后门(2024 年 xz-utils 事件的手法,CVE-2024-3094——一行构建脚本差点端掉全球 SSH)。2026 年上半年 npm/PyPI 下架恶意包同比再增 45%。
下面我们来做一下预演
在开始之前先搞懂:npm 安装时发生了什么
npm install 不只是「下载文件解压」。包里的 package.json 可以声明生命周期脚本:preinstall、postinstall——安装时自动执行。正规包几乎不需要安装后执行脚本,恶意包却全靠它投毒。
所以识别投毒包的第一招:装之前看 scripts。第二招:npm 的 lockfile 会记录包哈希(integrity 字段),锁版本 + 验哈希,能挡住大部分「同包名换内容」的攻击。
好,原理懂了,继续
一、环境准备(先做对,再动手)
🖥️ 环境准备
演示环境:Ubuntu 22.04+ 或 Windows 10/11 + Node.js 18+ / Python 3.10+ 工具:npm / pip / trivy(漏洞扫描)/ syft(SBOM 生成) 本篇不限于 Kali——供应链安全是开发侧的事,任何系统都适用
$ bash
# 确认工具版本
node --version && npm --version
# 期望输出:v18.x / 9.x 或更新
# 安装扫描工具(开发机)
sudo apt install -y trivy
# 或用官方仓库安装最新版
⚠️ 错误预演(翻车现场)
trivy 装不上?①Ubuntu 旧版本源里没有,用官方 apt 仓库(aquasecurity 官方文档有完整步骤);②Windows 用户直接下 trivy.exe 扔进 PATH;③装完先 trivy --version 确认。
二、完整复现:识别投毒包 + 扫描依赖
$ bash
# 第一步:识别 typosquatting(真实案例演示)
# 看正规包 lodash 的维护者:
npm view lodash maintainers
# 期望输出:lodash <lodash-npm@...> —— 正规包维护者固定
# 对比伪造包 lodahs(注意拼写差异):
npm view lodahs maintainers
# 期望输出:可疑维护者 / 无维护者 / 版本极少
# 第二步:检查安装钩子(重点!)
npm view <包名> scripts
# 看到 preinstall/postinstall 就要警惕——正规包极少需要
关键特征对比:拼写几乎一样(lodash vs lodahs)、下载量差距巨大、维护者信息异常、带安装脚本。攻击者赌的就是你「手滑打错包名」或「盲装依赖」。
⚠️ 错误预演(翻车现场)
npm view 报 404?①包名拼错了(小心 typosquatting 的「正确」拼法可能就是错的);②npm 源不稳,换 npmmirror:npm config set registry https://registry.npmmirror.com;③查不到说明包可能刚被下架——下架本身也是线索。
$ bash
# 第三步:生成 SBOM + 扫描已知漏洞(可落地的防御动作)
# 生成项目依赖清单(SBOM,软件物料清单):
trivy fs --format cyclonedx --output sbom.json .
# 扫描已知漏洞:
trivy fs --scanners vuln --severity HIGH,CRITICAL .
# 期望输出:
# CRITICAL: CVE-2024-3094 liblzma (xz) 5.6.0 — 后门版本!
# 修复建议:升级到 5.6.1+
trivy 直接命中 xz-utils 后门(CVE-2024-3094,受影响版本 5.6.0/5.6.1)——这就是 2024 年差点打进 SSH 的供应链后门。扫描工具让「依赖里的雷」可见。
✅ 成功预演 · 结果解读
结果解读:①npm/pip 的安装钩子(preinstall)是投毒的主要载体——安装即执行;②xz-utils 事件则是更隐蔽的编译期后门:恶意代码藏在构建脚本里,随二进制分发;③防御核心一句话:不信任「安装即执行」。
预演之后,我们需要知道的事
一、新旧对比:锁版本 vs 软件供应链安全标准【技术的迭代】
| 维度 |
老做法(锁版本) |
现代标准(SLSA + 签名) |
| 锁定 |
package-lock.json 锁版本 |
锁版本 + 校验哈希(npm 默认 integrity) |
| 来源 |
只信包名 |
验证发布者签名(Sigstore/cosign) |
| 流水线 |
无 |
SLSA Level 3+:构建可复现、来源可验证 |
| 2026 现状 |
仍是基础要求 |
政府/企业采购已开始强制 SBOM |
二、我们的防御怎么落地
🛡️ 防御清单
① 装包前:npm view <包> scripts 检查安装钩子;包名拼写核对;下载量/维护者判断。 ② 装包后:trivy fs 扫漏洞;生成 SBOM 存档(供应链审计必备)。 ③ CI/CD:流水线集成 trivy + 依赖锁定(lockfile 提交进仓库)+ 禁止未锁定依赖安装。 ④ 运行时:npm audit / pip-audit 定期巡检;高危包(xz/liblzma)建版本白名单。 ⑤ 组织级:供应商准入要求提供 SBOM;关键系统最小化依赖——少一个依赖少一个雷。
$ bash
# CI 集成 trivy(GitHub Actions 示例片段):
# - name: Trivy scan
# run: trivy fs --scanners vuln --severity HIGH,CRITICAL --exit-code 1 .
# 扫描到高危漏洞 → 构建失败(exit-code 1)→ 阻断上线
# 本地快速巡检:
npm audit --audit-level=high
# 期望输出:found 0 vulnerabilities 或列出需修复的包
# 生成 SBOM 并存档:
trivy fs --format cyclonedx --output sbom.json .
# 把 sbom.json 提交进仓库/存档——审计时能说清「我们用了什么」
npm audit 输出:found 0 vulnerabilities 是理想状态;有漏洞就 npm audit fix。SBOM 存档的意义:出事时你能在十分钟内回答「这个漏洞影响我们吗」——而不是翻三个小时 package.json。
最后:让我们回到那个 CI 构建
那次构建挂了反而是好事——我们顺藤摸瓜清理了一批可疑依赖,把 SBOM 纳入了发布流程。开发同事后来开玩笑:手滑一次 npm install,可能就手滑出一场安全事故。
你的系统安全,取决于你最信任的那个依赖。SBOM + 签名验证 + 扫描三件套,让供应链后门无处藏身。
⚖️ 法律与风险边界
法律与风险边界:制作/传播恶意依赖包涉嫌《刑法》第 285 条(非法控制计算机信息系统罪)、第 286 条(破坏计算机信息系统罪);造成重大损失可能构成破坏生产经营罪。作为开发者,你「随手装错一个包」就可能把后门带进公司系统——这不是技术问题,是法律责任问题。研究恶意包请使用隔离沙箱(容器/Docker),切勿在真实环境运行。
📚 延伸阅读
延伸阅读: ① CVE-2024-3094(xz-utils 后门)分析 — nvd.nist.gov / 各大安全厂商分析报告 ② SLSA 供应链安全框架 — slsa.dev ③ OWASP Dependency-Check — owasp.org/www-project-dependency-check ④ trivy 官方文档 — aquasecurity.github.io/trivy ⑤ 美国 EO 14028 软件供应链安全 — whitehouse.gov
— · END · —
🔐 本日志仅用于网络安全教学 · 请勿用于非法用途
虚构日记 · 时间地点人物均已虚拟化 · 防御永远比攻击更有价值