软件供应链投毒全景:PyPI 与 npm 生态对比

副标题:从 event-stream 到 Shai-Hulud——开源包仓库八年来的攻击演化、生态差异与防御格局

软件包投毒(Package Poisoning)正在以指数曲线穿透公共开源生态。Sonatype 的口径显示,2019 年全网恶意包接近于零,到 2025 年单年新增已达 454,600 个,自 2019 年累计阻断超过 1,233,000 个,其中 99% 以上集中在 npm[Sonatype 2026 SSSC]。从 2018 年 event-stream 维护者自愿转交开始,到 2024 年 XZ Utils 历时两年的国家级社工渗透,再到 2025 年 9 月 Shai-Hulud 蠕虫把 ATO(账户接管)放大为可自我复制的运动战——「上传即下架」式的偶发威胁,已经演化为「持续运动战」。本文作为本研究系列的入口篇,目标是为后续的攻击事件深扒、防御技术、产品矩阵、地缘治理和趋势数据建立一套共享的概念底盘:定义投毒的边界、对比 PyPI 与 npm 两大生态的治理模型、给出系统化的攻击分类法(taxonomy)、还原从攻击到受害的完整链路,并给出后续 02–08 篇的阅读路径。


一、概念边界:什么是「软件包投毒」

1.1 与「软件供应链攻击」的关系

「软件供应链攻击」(Software Supply Chain Attack, SSCA)是一个伞形概念,覆盖从开发者工作站、源代码托管、构建系统、制品仓库到分发渠道的整条 SDLC 链路。学术界对 SSCA 最系统的分类来自 Ladisa et al. 在 IEEE S&P 2023 的 SoK,其攻击树(attack tree)梳理出 107 种攻击向量、94 起真实事件、33 项缓解措施,并在 17 名安全专家与 134 名开发者中做用户调研验证[Ladisa SoK 2023]。

「软件包投毒」(Package Poisoning)是 SSCA 的真子集,特指攻击者将含恶意逻辑的软件包以合法身份发布到公共开源包仓库(npm、PyPI、RubyGems、Crates、Maven Central、Packagist 等),通过包管理器(npm/pnpm、pip/uv、gem、cargo、maven、composer)的依赖解析机制进入受害者环境的过程。它的特征面是:

  • 入口窄:聚焦在公共仓库的发布动作,而非企业内网的横向移动;
  • 触达广:一次发布即可到达所有未禁用直连公网的下游消费者;
  • 取证难:包通常在数小时内被下架,但 CDN/镜像同步会让恶意工件继续被拉取数日;
  • 司法灰:维护者本人发布的「protestware」(如 colors.js v1.4.44-liberty-2)和 ATO 后的攻击者发布,在协议层不可区分。

与之相邻但不在本系列重点范围的,是上游构建系统级攻击(SolarWinds Orion、3CX 双重供应链[Mandiant])、CI/CD 第三方组件攻击(tj-actions/changed-files CVE-2025-30066[CISA])以及 AI 模型仓库投毒(Hugging Face pickle 后门)——它们与包投毒共享 TTP(如 SHA pinning 缺失、维护者 ATO),但宿主生态不同,本系列会作为参考案例出现,不做主线分析。

1.2 Package Poisoning 的攻防对称性

与漏洞类(CVE)相比,包投毒呈现强烈的攻防对称失衡:

  • 攻击侧门槛极低:注册一个 npm 账号、写一段 100 行的 postinstall 脚本,理论上即可触达全球。Phylum 在 2024 Q2 单季扫描 290 万包、1.9 亿文件,发现「约 1/4 npm 新发布包是 spam 或 SEO 投毒」[Phylum Q2 2024]。
  • 防御侧需多层联动:要做到可靠拦截,至少需要 SBOM(CycloneDX/SPDX)、签名(Sigstore/PEP 740/npm provenance)、SLSA Build Track L2+、私服 + Repository Firewall、运行时 egress 控制——五层中任一缺失都会让攻击者保留可行路径。
  • 可观测性差:Snyk 数据库内 npm 漏洞中 48.58%、PyPI 中 13.92% 是 CWE-506「故意嵌入恶意代码」而非偶发缺陷——远高于业界对「漏洞 vs 恶意」的直觉比例[Snyk Malicious Packages]。

二、PyPI 与 npm 生态对比

2.1 规模与流量

两个生态今天的体量已经不在一个量级。Sonatype 2024 报告披露:npm 2024 年接收 4.5 万亿次包请求(YoY +70%),PyPI 全年估算 5,300 亿次(YoY +87%)[Sonatype SSSC 2024]。npm 包数量与请求量大约都是 PyPI 的 8–10 倍,平均一个 npm 项目带 79 个传递依赖、39 个独立维护者,Express.js 单一框架就有 47 个传递依赖[传递依赖测算]。这一规模差直接解释了 99%+ 恶意包集中在 npm 的核心成因——攻击面与可被劫持的「巨型节点」绝对数都远高于 PyPI。

2.2 治理模型与关键里程碑

维度PyPInpm
强制 2FA2024-01-01 全员强制[PyPI 2FA 公告]2022 起对 top 100 维护者;2025-09 后弃用 TOTP,强推 FIDO/WebAuthn
OIDC Trusted Publisher2023 GA(GitHub Actions/GitLab/Google Cloud/ActiveState)[Trusted Publishers]2025-07-31 GA[npm OIDC GA]
Classic Token 弃用仅 project-scoped token 推荐2025-12-09 全面弃用
构建签名PEP 740 attestations + Sigstore(2024–2025 推进)npm provenance(2023-04 GA),SLSA L2 等级[npm provenance]
Lifecycle 钩子setup.py 任意 Pythonpreinstall/install/postinstall
锁文件强制哈希pip --require-hashes(默认不开)package-lock.json integrity 默认 SRI/sha512
命名空间全局扁平,无 scope支持 @org/name scope,是 dependency confusion 关键缓解
注册表恶意包扫描人工 + 报告 + 部分自动;2024 曾因投毒过载短暂关闭新包注册npm 内部扫描 + Dependabot Malware Alerts(2026-03-17 GA for npm)[Dependabot Malware]

两点关键差异需要单独强调:

第一,npm 仍是攻击主战场。GitHub Advisory Database 的「malware」类目目前仅覆盖 npm,PyPI 端依赖 PSF 自有 advisories 与 OSV MAL- 编号;2025 年 npm 的 advisory 数同比 +69%[GitHub 2025 趋势]。

第二,PyPI 在治理速度上反而走在前面:2FA 强制时间早一年、OIDC GA 早两年,sdist 的 setup.py 任意执行历史包袱却让攻击面在工程实现上更难收口(74.81% PyPI 恶意包通过 sdist 进入下游[Guo et al., ASE 2023])。

2.3 监管侧的杠杆点

公共仓库治理的杠杆主要握在三处:

  1. OpenSSF(Linux Foundation 旗下)以 Scorecard、Sigstore、SLSA、GUAC、Allstar、Package Analysis 六个项目矩阵覆盖签名、可达性、行为分析;
  2. 欧盟 CRA(Regulation (EU) 2024/2847)已于 2024-12-10 生效,2026-09-11 起对制造商触发 24h 漏洞与重大事故报告义务,2027-12-11 起全部 PDE(含数字元素的产品)必须满足 SBOM、生命周期、5 年漏洞维护、CE 标志全套强制要求[EU CRA];
  3. CISA:2025-09-23 对 Shai-Hulud 与 chalk/debug 联动事件发布行业级告警,把 npm 治理事实上推到关键基础设施层级[CISA Alert]。

国内层面,GB/T 43698-2024《网络安全技术 软件供应链安全要求》 自 2024-11-01 实施,构建「123456」体系(1 总目标、2 类主体、3 个阶段、4 类活动、5 项基线、6 类对象),可视为中国版 SSDF + SCRM 的体系化输出。


三、攻击分类法(Taxonomy)

软件包投毒的攻击向量在过去八年走过了从「广撒网拼写错误」到「自动繁殖蠕虫」的完整光谱。综合 Ladisa SoK 2023 的攻击树、Sonatype 2026 SSSC 的细类、以及 2024–2026 涌现的新型 TTP,可以归纳为下表 8 类。

3.1 攻击向量分类表

类别核心机制代表事件占比/趋势
1. Typosquatting注册与目标包仅 1–2 字符差异的伪冒名node-hide-console-windows(仿 -window);PyPI requets/urlib3 等持续清理Sonatype 2024 报告中「phishing 类」(typosquat + dep-confusion)合计 ~14%;Phylum 单日观察到 200+
2. Dependency Confusion抢注企业内部 scope 名 + 高版本号,迫使 CI 拉取公共版本PyTorch torchtriton(2022-12,3000+ 下载)[PyTorch 公告];s1ngularity NxPhylum Q1 2024 +121% YoY;Q2 仍 5,229 个
3. Brandjacking / Combosquatting合法品牌词 + 后缀(-utils/-async/-sdk)伪装官方扩展noblox.js-async;Lazarus 仿冒开发者工具 234 包词法相似度算法对带后缀变体不灵敏,难自动识别
4. Maintainer Compromise项目所有权通过自愿转交、过期域名、长期社工被实际控制人替换event-stream(2018,自愿转交→Copay 钱包)[npm 官方 post-mortem];ctx/phpass(2022,邮箱域名过期);XZ Utils(2024,2 年社工)平台无 KYC,时间维度长,传统扫描无法捕捉
5. Account Takeover (ATO)钓鱼/AitM/token 泄露直接夺取维护者凭据ua-parser-js(2021,周下载 800 万次)[Sonatype];Lottie Player(2024,$723k BTC 损失);chalk/debug(2025-09,周下载 20 亿+)2025 年成为主升级路径;CWE-506 占 npm 漏洞 48.58%
6. Lifecycle 滥用npm pre/post-install、PyPI setup.py/wheel entry_points 安装即执行Ultralytics 8.3.41(2024-12,XMRig 矿机,PyPI 12 小时存活)[PyPI 官方分析];Shai-Hulud bundle.js学术统计 ~2% npm 包定义 install scripts;--ignore-scripts 是双刃剑
7. Slopsquatting抢注 LLM 编程助手「幻觉」推荐的不存在包名huggingface-cli(研究者占名 3 月获 30,000+ 下载)LLM 推荐包名 19.7% 不存在;43% 同 prompt 稳定复现幻觉名[Trend Micro]
8. 蠕虫化 / 自传播偷得维护者 token 后递归发布,利用 GitHub Actions 跨仓库扩散Shai-Hulud(2025-09,500+ 包 v1,796 包/1,092 版本 v2);Sha1-Hulud;Glassworm2025 年从 0 跃升为独立类目;Sonatype 把它列为「事件→运动战」的拐点[Microsoft]

3.2 横向 TTP:混淆、time-bomb、anti-sandbox

无论上述哪一类的入口,落地阶段的 payload 常见以下「掩护层」:

  • 多层加壳:dYdX 仿冒包内观察到 100 轮 base64 迭代解码;
  • 目标围栏(target-fence):event-stream 的 payload 用 Copay 包 ID 作 AES key,普通扫描器看不到真实逻辑;
  • Time-bomb:colors.js v1.4.44-liberty-2 用版本号本身作触发条件;
  • Anti-sandbox:检测 hostname、CPU 数、是否存在研究员目录;
  • AI 痕迹:Unit 42 在 Shai-Hulud 与 React2Shell 的 bash payload 中识别出 LLM 生成的注释、emoji、命名风格异常,已成为新检测信号[Unit 42]。

四、从攻击到受害的完整链路

下图描绘一次包投毒事件从「攻击者准备」到「下游受害」的完整时序。横轴是时间,纵轴上从下到上分别是 攻击者、公共仓库、CI/CD、消费者环境。Shai-Hulud 与 chalk/debug 的实际事件可以套入此模板(前者从 ATO 自动繁殖,后者从钓鱼到 2 小时内被撤回)。

sequenceDiagram
    participant A as 攻击者
    participant V as 公共仓库 (npm/PyPI)
    participant M as 镜像/CDN
    participant CI as 下游 CI/CD
    participant Dev as 开发者机器
    participant Prod as 生产 / 终端用户

    Note over A: 准备阶段
    A->>A: 选择 TTP(typosquat / ATO / dep-confusion)
    A->>A: 构造 payload(多层加壳 + time-bomb)
    A->>V: 抢注或夺取维护者凭据后发布
    Note over V: 注册表 hooks 触发<br/>Sonatype/Socket/GuardDog/<br/>Package Analysis 沙箱

    par 同步分发
        V->>M: 镜像同步(清华 TUNA / 阿里云<br/>淘宝镜像 / Cloudflare CDN)
        V->>CI: npm install / pip install<br/>解析依赖
        V->>Dev: 个人/家用环境拉取
    end

    Note over CI,Dev: 执行阶段
    CI->>CI: postinstall / setup.py 触发<br/>读取 secrets / IMDS / token
    Dev->>Dev: 解码 payload,C2 上报
    CI->>Prod: 构建产物部署到生产
    Prod->>Prod: 浏览器端 hook window.ethereum<br/>或矿机持续运行

    Note over A: 二次扩散(worm 化)
    Dev-->>A: 偷得 npm token / GitHub PAT
    A->>V: 用偷得的 token 自动发布维护者其他包
    V->>CI: 蠕虫化跨仓库扩散

    Note over V,Prod: 响应阶段
    V-->>V: 安全团队下架(chalk: <2h / Ultralytics: 12h)
    M-->>M: 镜像延迟同步删除(数小时~数天)
    CI-->>CI: lockfile 已固化恶意 hash<br/>需手动回滚

链路上有几个关键观测:

  1. 下架不等于无害:PyPI 上 2024-05-19 的一组恶意包在 < 24 小时内下架,仍被下载 1,700+ 次[The Hacker News];Ultralytics 8.3.41 仅在 PyPI 上存在 12 小时,仍触达大量 Colab/HuggingFace Spaces 环境。
  2. lockfile 是双刃剑:package-lock.json 与 pip --require-hashes 在防御「同名异内容」时是关键,但当恶意 hash 已被锁入,反而把恶意版本固化到所有 fresh clone 的 CI 中——必须配合主动回滚。
  3. 镜像与 CDN 是延时通道:清华 TUNA、阿里云、华为云的公开文档主要面向「加速访问」,未明确声明对恶意包的主动审查机制,恶意包随上游同步进入国内的「时延扩散」是 2024–2025 多次事件中观察到的模式。
  4. Worm 化重写应急范式:Shai-Hulud 通过偷到 npm token + GitHub Actions workflow 写入实现自我繁殖,单个维护者一次最多发布 100 个木马版本——这意味着「事件级 MTTD/MTTR」必须从月、周降到小时级。

五、Shai-Hulud:分水岭事件

如果说 2018 的 event-stream 让业界认识到「维护者就是攻击面」,2024 的 XZ Utils 揭示了「国家级两年社工」的可能性,那么 2025-09 的 Shai-Hulud 就是 npm 生态的卡西尼弧线(Cassini line)——它把过去八年的所有 TTP 编织成了一条自动化运动战流水线:

  • 2025-09-14:第一只被 Shai-Hulud 感染的包出现(@ctrl/tinycolor);
  • 2025-09-15:Socket 公开披露 180+ 包受感染,48 小时内呈指数级蔓延;
  • 2025-09-23:CISA 发布 npm 生态广泛 compromise 警告——这是史无前例对单一公共仓库的 KEV 级响应;
  • 2025-11-09:Sha1-Hulud(变种)出现,累计 796 包/1,092 版本,执行点从 postinstall 前移到 preinstall,影响 npm ci 与 Dockerfile build 全流程;
  • 2025-12-01:Glassworm 第三波,把窃密目标从 GitHub token 扩展到 OpenAI/Anthropic API key;
  • 2025-12-09:Microsoft 发布 Shai-Hulud 2.0 防御指南,统计已有 350 个独立用户、25,000+ 恶意仓库参与扩散[Microsoft]。

蠕虫的核心创新有三:(a)自我繁殖——偷得 token 后自动发布维护者所有其他包的木马版本;(b)LLM 辅助——Unit 42 中等置信判定 bash 部分由 LLM 生成;(c)公开仓库即 C2——把窃取的秘密外发到公开的「Shai-Hulud」命名 GitHub repo,任何能写公开 repo 的 token 都成 C2 通道。这三点合起来意味着:单点 ATO 的爆破半径,不再是一两个包的下游,而是该维护者的完整命名空间,再通过被感染包的间接依赖图扩散。

GitHub 在 2025 Q4 因此推动了三件事:(1)TOTP 弃用,强推 FIDO/WebAuthn passkey;(2)Classic Token 在 2025-12-09 永久弃用;(3)npm trusted publishing 与 OIDC 强绑定写入 2026 年「更安全 npm 路线图」。这是自 2022 年 ua-parser-js 之后 npm 的第二次治理大跃迁。


六、本系列后续阅读路径

本篇是入口。后续 7 篇会沿不同维度逐层展开,每篇可独立阅读,但相互交叉引用:

  • 02 — 攻击事件深扒(2018–2026):以 event-stream、ua-parser-js、ctx/phpass、torchtriton、3CX、Lottie Player、Ultralytics、tj-actions、Shai-Hulud、XZ Utils 十大事件为骨干,按时间线还原每起事件的入侵路径、payload 行为与复盘要点。核心问题:这些事件的根因是否可被通用的工程化控制覆盖?

  • 03 — 防御技术体系:覆盖 SBOM(CycloneDX/SPDX)、SLSA Build Track、Sigstore/PEP 740、锁文件与哈希校验、私服 Repository Firewall、CI/CD 加固(OIDC、SHA pinning、ephemeral runner)、令牌与 MFA、开发流程工程化。核心问题:七层防御中哪些是必选项、哪些是高边际成本?

  • 04 — 检测技术:从静态到 LLM:分析静态 AST/熵分析(GuardDog、Bandit、Semgrep)、动态沙箱(OpenSSF Package Analysis、gVisor)、ML 检测(Amalfi、DONAPI、MalGuard、MPHunter)、LLM 检测(SocketAI、MAL-LLM、CHASE、LAMPS)、信誉/元数据系统(Scorecard、Socket、deps.dev)。核心问题:LLM-aided triage 在精确率/成本曲线上是否真的胜出?

  • 05 — 行业产品矩阵:横向对比 Snyk、Sonatype、JFrog Xray+Curation、Socket、Phylum→Veracode、Endor Labs、GitHub Advanced Security、Checkmarx、Black Duck、Mend、ReversingLabs Spectra Assure 在静态/动态/AI/维护者画像/install-time 拦截上的能力差异,并梳理 Forrester Wave、Gartner MQ、GigaOm Radar 三家分析机构的最新位次。核心问题:为什么开源工具栈(OSV-Scanner + Grype + GuardDog + Package-Analysis)在恶意包检测上反而比部分商业产品更可信?

  • 06 — 地缘治理:美/欧/英/中/国际基金会:解读 EO 14028、NIST SSDF/SP 800-204D、OMB M-22-18/M-23-16、CISA SBOM Minimum Elements;EU CRA 与 NIS2;UK Software Security Code of Practice;OpenSSF 项目矩阵;中国 GB/T 43698-2024、网安审查办法、信通院评估、奇安信/墨菲/悬镜国产化矩阵。核心问题:2026-09-11 与 2027-12-11 两个 CRA 节点之前,国内出海企业要补哪些功课?

  • 07 — 趋势与数据:Sonatype/Snyk/ReversingLabs/GitHub Advisory 多源年度数据对照;Sonatype 2026 SSSC 攻击手法占比(Repository abuse 55.9%、PUA 27.5%、凭证窃取 3.9% 等);行业影响(金融 97% 在 npm、AI/ML 列 OWASP 2025 #1、CISA 关基告警);修复时间分布(< 2h / < 24h / 数日 / 持续运动战四档)。核心问题:指数曲线还能持续多久,蠕虫化是否会成为 2026 的常态?

  • 08 — AI 编程时代的新前线:Slopsquatting(LLM 19.7% 推荐包不存在、43% 同 prompt 稳定复现幻觉名);AI 生成 payload(s1ngularity、React2Shell、Shai-Hulud 的 LLM 痕迹);prompt-injection 反制 AI 扫描(伪装 ESLint 插件嵌入 hidden prompt);AI/ML 模型仓库投毒(Hugging Face 100+ 恶意 pickle 模型);AI vs AI 防御范式。核心问题:AI Coding Agent 在「推荐 → 安装 → 执行」全链路上需要怎样的 guardrail?


结语

软件包投毒不是一个新问题——event-stream 已是 8 年前的事——但它的发生率、自动化程度、目标精准度、监管杠杆都在 2024–2026 完成了量变到质变的跨越。对资深安全开发者而言,理解这一领域的关键不再是「有哪些攻击」,而是把握三组对照关系:攻击 TTP 与防御层的对应(taxonomy ↔ defense matrix)、生态规模差异与治理速度差异(npm 攻击主战场 vs PyPI 治理先行)、短期事件级响应与长期合规倒逼(小时级 MTTR vs 2027-12-11 CRA 全适用)。后续 7 篇将围绕这三组对照各自下钻;本篇仅做地图。

本研究系列基于 2026-05-05 之前的公开取证;事件细节、CVE 编号与时间节点以原始来源为准。文中所有数据点均可在脚注链接中追溯到一手厂商报告、监管公告或学术论文。

Comments (0)

No comments yet

imfht
May 5, 2026 · Updated May 5, 2026