首页/知识库/开源依赖投毒攻击的防御机制:从 npm 事件到零信任供应链

开源依赖投毒攻击的防御机制:从 npm 事件到零信任供应链

⚖️AI 伦理与安全进阶✍️ AI Master📅 创建 2026-05-26📖 20 min 阅读
💡

文章摘要

系统解析 2025-2026 年 npm/PyPI 供应链攻击链路的完整技术细节,从 Shai-Hulud 蠕虫到 TeamPCP 级联攻击,构建从锁文件验证到零信任供应链的防御体系

1为什么依赖投毒是 AI 生态的头号安全威胁?

软件依赖投毒是指攻击者通过污染开源包仓库(npm、PyPI 等),将恶意代码注入到开发者日常使用的第三方库中。对于 AI 生态来说,这个威胁被进一步放大——AI 项目的依赖树比传统项目更加庞大复杂,一个典型的大语言模型应用可能涉及数百个依赖包。

2025 到 2026 年,开源生态经历了史上最严重的供应链攻击浪潮:

Shai-Hulud 蠕虫(2025 年 9 月):这是 npm 生态系统的分水岭事件。攻击者发布了一个自我复制的恶意蠕虫,在 12 天内感染了超过 500 个 npm 包。这个蠕虫不再只是传统的"恶作剧"级别威胁,它会自动窃取 npm Token 和 GitHub Personal Access Token(PAT),然后利用这些凭证自动感染和重新发布合法包。这标志着 npm 攻击进入了高后果威胁时代

TeamPCP 级联攻击(2026 年 3 月):这是史上最复杂的供应链攻击之一。攻击链为:攻陷 Trivy 安全扫描器 → 窃取 CI/CD 凭证 → 发布自复制 npm 蠕虫 → 后门 LiteLLM(AI 模型网关)和 Telnyx SDK。整个过程涉及两个包生态系统(npm 和 PyPI)、三种投递机制(GitHub Actions 标签劫持、Python .pth 文件注入、npm postinstall 钩子),以及至少两个不同的威胁组织。

TanStack 生态沦陷(2026 年 5 月):攻击者利用 pull_request_target Pwn Request、GitHub Actions 缓存投毒和 OIDC Token 从 Runner 进程内存提取的三步链式攻击,在 42 个 @tanstack/* 包中发布了 84 个恶意版本。CVE 评分高达 9.6。受影响的包括 TanStack Query、Table、Router 等 170+ npm 包。

Axios 二次攻击(2026 年 3 月):最受欢迎的 npm HTTP 客户端库(周下载 5000 万+)被植入恶意依赖 plain-crypto-js,通过 postinstall 脚本窃取环境变量和 API 密钥。

这些事件揭示了一个核心事实:你的代码写得再安全,只要依赖链中有一个环节被攻破,整个系统就暴露了

图表加载中…

理解依赖投毒的关键在于认识到:安全边界不再只是你自己的代码,而是你整个依赖树的每一个节点。一个典型的 npm 项目有 86 个直接依赖和超过 1000 个间接依赖,人工审查每一个包是不现实的。

不要以为只有小项目会被攻击。Axios 周下载 5000 万次、Trivy 被数百万开发者使用——攻击者专门挑选使用量最大的包,因为投毒一个流行包的收益远超投毒一百个小包。

2依赖投毒的六大攻击向量深度解析

要构建有效的防御体系,必须理解攻击者的完整入侵路径。以下是 2025-2026 年真实攻击中使用的六大攻击向量。

向量一:npm 生命周期钩子注入

npm 提供了多个脚本执行钩子:preinstall、install、postinstall、prepublish 等。攻击者在恶意包的 package.json 中嵌入 postinstall 脚本,当开发者执行 npm install 时,恶意代码以安装者的权限自动执行。这是最常见的投毒方式。Axios 事件中,恶意依赖 plain-crypto-js 正是通过 postinstall 脚本运行 setup.js 来窃取凭据。

向量二:Python .pth 文件持久化

TeamPCP 在 LiteLLM 的投毒中使用了一个创新技巧:在 Python site-packages 目录中放置一个 .pth 文件。这个文件会在每次 Python 解释器启动时自动执行,即使恶意包被标准包管理器删除,.pth 文件仍然存在,实现了持久化驻留。

向量三:GitHub Actions 标签劫持

攻击者入侵 Trivy 的 GitHub 仓库后,通过伪造的 commit 和标签劫持,让 CI/CD 流水线在构建过程中注入恶意代码。这种攻击的隐蔽性极高——最终发布的包签名、哈希、元数据全部正常,但构建过程被篡改了

向量四:pull_request_target Pwn Request

TanStack 事件中,攻击者利用 GitHub 的 pull_request_target 事件触发器,在一个看似无害的 PR 中注入了恶意 GitHub Actions 工作流。当仓库维护者审核 PR 时,工作流以仓库的权限运行,从而窃取 OIDC Token 并获取发布权限

向量五:自复制蠕虫传播

Shai-Hulud 蠕虫引入了蠕虫式自动传播机制:一旦感染一个开发者的机器,它会扫描本地存储的 npm Token,然后用这些 Token 自动登录 npm 并发布恶意版本的该开发者维护的所有包。这意味着一次感染可以级联传播到数百个包

向量六:OIDC Token 从 Runner 内存提取

在 TanStack 攻击中,攻击者不仅窃取了 GitHub Actions 的环境变量,还进一步从 Runner 进程内存中提取了 OIDC Token。这使得攻击者能够模拟 CI/CD 流水线的身份,以合法的凭证发布恶意包。

图表加载中…
攻击向量技术原理隐蔽性影响范围典型案例

npm 生命周期钩子

postinstall 自动执行

所有安装者

Axios + plain-crypto-js

Python .pth 持久化

解释器启动时加载

所有 Python 项目

TeamPCP → LiteLLM

GitHub Actions 劫持

CI/CD 构建过程篡改

极高

CI 构建产物

Trivy 安全扫描器

Pwn Request

pull_request_target 触发

维护者审核时

TanStack 生态沦陷

自复制蠕虫

窃取 Token 自动传播

极高

维护者的全部包

Shai-Hulud 500+ 包

OIDC 内存提取

Runner 进程内存读取

极高

CI/CD 身份冒用

TanStack 84 个恶意版本

理解攻击向量的最佳方式是查看真实的攻击链。上述六种向量在 2025-2026 年的真实攻击中全部出现过,说明攻击者已经掌握了从「单点投毒」到「级联传播」的完整工具箱。

不要只看 postinstall 这一个入口。TeamPCP 展示了攻击者会使用 .pth 文件、CI/CD 劫持、内存提取等多种手段组合攻击。防御必须覆盖所有向量,不能只防一种。

3第一道防线:锁文件完整性验证

锁文件(Lockfile)是开发者最熟悉但也最容易被忽视的安全防线。package-lock.json(npm)和 requirements.txt / Pipfile.lock(Python)记录了依赖树中每个包的精确版本号和内容哈希。

锁文件的核心安全价值在于:它锁定了你实际安装的每一个依赖的版本和哈希值。即使攻击者在仓库中发布了新的恶意版本,只要你的锁文件没有更新,你安装的仍然是安全的旧版本。

但锁文件也有局限性:

第一,锁文件只保护你,不保护你的间接依赖的新版本。如果你的锁文件中某个间接依赖被标记为可更新,下次 npm update 时可能拉取到恶意版本。

第二,锁文件可以被篡改。如果攻击者已经获得了对你的代码仓库的写入权限,他们可以同时修改锁文件,使其指向恶意版本。

第三,锁文件不验证包的真实性。它只验证哈希值匹配,但如果攻击者在投毒时同时更新了锁文件中的哈希值,校验就会通过。

最佳实践:

启用 lockfileVersion 3:npm v7+ 默认使用 lockfileVersion 3,包含更完整的依赖树信息和 integrity 哈希。确保你的项目使用最新版本。

CI/CD 中强制锁文件校验:在 CI 流水线中运行 npm ci(而不是 npm install),它会严格根据锁文件安装依赖,如果锁文件与 package.json 不一致则直接失败。

定期审查锁文件变更:在 Code Review 中,将锁文件的变更视为与安全相关的变更。任何新增或修改的依赖条目都应该被审查。

使用 npm audit:npm 内置了安全审计功能,可以检测已知漏洞。虽然它不能检测零日投毒,但能帮助你发现已知的安全问题。

图表加载中…

在你的 CI 流水线中,用 npm ci 替代 npm install。npm ci 会删除现有的 node_modules 并严格按照锁文件重新安装,如果锁文件与 package.json 不一致会直接报错——这是防止恶意依赖混入的最简单有效的方法。

锁文件不是银弹。Shai-Hulud 蠕虫证明,即使有锁文件,如果攻击者窃取了你维护者的 Token 并修改了锁文件,校验就会通过。锁文件必须与其他防御手段配合使用。

4第二道防线:代码签名与包验证(Sigstore/Cosign)

锁文件解决的是「版本锁定」问题,但不能解决「包是否来自可信发布者」的问题。这正是代码签名技术要解决的。

Sigstore 是一个由 Linux 基金会托管的开源代码签名基础设施,它提供了一套完整的软件供应链安全工具链。对于 npm 和 PyPI 生态,Sigstore 的核心组件包括:

Cosign:用于容器镜像和软件制品的签名和验证工具。开发者可以用 Cosign 对发布的包进行签名,使用者在安装前验证签名是否匹配。

Fulcio:基于 OpenID Connect 的短期证书颁发机构。它为开发者颁发短期有效的签名证书,避免了传统 PKI 的复杂性。

Rekor:一个不可篡改的透明日志,记录所有的签名事件。任何人都可以查询某个包在某个时间点是否被签名过。

npm 从 v9 开始内置了对 Sigstore 的支持。当你在 npm 上发布包时,npm 会自动使用 Sigstore 进行签名。使用者可以通过以下命令验证包的签名:

npm install --verify-signatures

这会检查包的签名是否与 Sigstore 透明日志中的记录匹配。如果签名不匹配或缺失,安装将被拒绝。

对于 PyPI,可以使用 sigstore-python 工具对发布的 wheel 包进行签名。虽然 Python 生态的代码签名支持不如 npm 成熟,但 Sigstore 正在快速发展。

更进阶的方案是在 CI/CD 中集成 SLSA(Supply chain Levels for Software Artifacts) 框架。SLSA 是由 Google 提出的供应链安全等级标准,从 Level 0(无安全保证)到 Level 4(最高级别保证),每一级都对构建过程的来源性、完整性和防篡改性提出了更严格的要求。

如果你的项目发布公共 npm 包,立即启用 Sigstore 签名。在你的 package.json 中设置 publishConfig.provenance: true,npm 发布时会自动生成 Sigstore 证明。这让你的用户能够验证包的来源。

代码签名不是万能的。如果攻击者窃取了开发者的签名密钥(如通过窃取 npm Token),他们可以以合法身份签名恶意包。因此签名必须与密钥轮换、多因素认证等措施配合使用。

5第三道防线:CI/CD 流水线安全加固

2026 年的供应链攻击(特别是 TeamPCP 和 TanStack 事件)揭示了一个关键事实: 攻击者的目标不再只是包仓库本身,而是 CI/CD 流水线。一旦攻陷 CI/CD,攻击者就能以「合法身份」发布恶意包。GitHub Actions 安全最佳实践

第一,禁用 pull_request_target 或严格限制。pull_request_target 以目标仓库的权限运行工作流,攻击者可以通过构造恶意 PR 来触发工作流并窃取权限。如果必须使用,确保工作流中不包含敏感操作(如发布包)。

第二,使用环境(Environments)保护发布步骤。将 npm publish 等敏感操作放在受保护的环境中,需要人工审批才能执行。这样即使工作流被注入恶意代码,也需要额外的审批步骤。

第三,限制 GITHUB_TOKEN 权限范围。在工作流文件中使用 permissions 字段将 Token 权限限制为最小必要权限。例如,如果工作流只需要读取代码,就不要授予写入或发布权限。

第四,固定 Actions 的版本哈希。不要在工作流中使用 actions/checkout@v3 这样的标签引用,而是使用 actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 这样的完整 SHA 哈希。这防止攻击者劫持标签指向恶意版本。

第五,启用 OIDC 身份验证的细粒度控制。如果使用 OIDC Token 进行云资源认证(如 AWS、GCP),确保 OIDC 的条件限制只允许特定的仓库、分支和工作流触发。

第六,定期轮换 secrets 和 Token 740。npm Token、GitHub PAT、云服务商的 API Key 都应该定期轮换。TeamPCP 攻击之所以影响广泛,部分原因是受害组织的 CI/CD 凭证长期未轮换。 CI/CD 安全架构建议

在 CI/CD 流水线中引入 软件物料清单(SBOM)生成步骤。SBOM 是软件所有组件的完整清单,包括名称、版本、许可证、来源等信息。当供应链攻击发生时,SBOM 可以帮助你快速判断哪些组件受影响。

使用 CycloneDX或SPDX格式生成 SBOM,并将其作为构建产物存档。工具如 syft、trivy sbom 可以自动从你的项目中生成 SBOM。

图表加载中…

立即行动:检查你所有 GitHub 仓库的工作流文件,将 actions/checkout@v3 等标签引用替换为具体的 SHA 哈希。这是一个 5 分钟就能完成的操作,但能防御标签劫持攻击。

CI/CD 安全不仅仅是技术问题。TeamPCP 攻击的第一步是通过钓鱼邮件获取了 Trivy 维护者的 GitHub 账号。即使 CI/CD 配置再安全,维护者账号被盗也会让整个防御体系崩溃。

6第四道防线:运行时检测与应急响应

即使前面所有防线都失效了,你仍然可以在运行时检测异常行为。这是最后一道防线。

npm 包运行时检测

当你在 CI 或生产环境中安装 npm 依赖时,可以使用以下工具进行实时检测:

Socket.dev:这是一个专门针对 npm 包的实时监控平台。它会对每个包进行静态分析,检测可疑行为,如:网络请求、文件系统访问、子进程执行、环境变量读取等。当发现可疑行为时,Socket 会发出告警。

npm 审计自动化:将 npm audit 集成到 CI 流水线中,每次安装依赖时自动运行审计。可以配置为当发现中危或高危漏洞时阻断构建。

自定义 postinstall 脚本扫描:编写脚本检查 node_modules 中所有包的 package.json,列出所有包含 scripts.postinstall 的包。这能帮助你快速识别哪些依赖会在安装时执行自定义脚本。

应急响应流程

当发现供应链攻击时,以下是标准化的应急响应流程:

第一步:识别影响范围。使用 SBOM 或 npm ls 命令列出所有受影响的依赖。确定哪些包被投毒、哪些版本受影响、你的项目是否使用了这些版本。

第二步:立即轮换凭据。TeamPCP 攻击的核心目标是窃取 API Key 和 CI/CD 凭据。一旦确认被攻击,立即轮换所有可能泄露的凭据,包括 npm Token、GitHub PAT、云服务商 API Key、数据库密码等。不要等确认「是否真的被窃取」——假设已经被窃取。

第三步:清除恶意包。删除 node_modules 目录,清理 npm 缓存(npm cache clean --force),然后使用已知安全的版本重新安装。如果使用 Python,检查 site-packages 目录中是否有可疑的 .pth 文件。

第四步:修复漏洞根源。分析攻击是如何发生的——是锁文件被篡改?是 Token 泄露?还是 CI/CD 配置不当?修复根源后,重新评估整个防御体系。

第五步:上报和共享信息。将事件上报到 npm 安全团队、GitHub 安全团队或相关的 CVE 数据库。供应链安全是社区问题,共享信息能帮助其他开发者避免同样的攻击。

在你的项目中添加一个安全检查脚本,在每次 npm install 后自动运行,检查 node_modules 中所有包含 postinstall/preinstall 脚本的包。这能帮助你快速识别哪些依赖可能在安装时执行自定义代码。

应急响应中最常见的错误是「只修复表面症状」。如果你只删除了恶意包但没有轮换凭据,攻击者仍然可以用窃取的 Token 继续攻击。凭据轮换是应急响应的第一步,不是最后一步。

7第五道防线:零信任供应链架构

将前面所有防线整合起来,就构成了一个零信任软件供应链(Zero Trust Software Supply Chain)架构。零信任的核心理念是:不信任任何外部代码,无论它来自哪里

零信任供应链的五个核心原则:

原则一:最小信任边界。每个外部依赖都应该被视为不受信任的,直到它被验证。验证包括:签名验证、哈希校验、行为分析、社区声誉检查。

原则二:纵深防御。不依赖单一防线。锁文件、代码签名、CI/CD 加固、运行时检测、SBOM——每一层都能独立提供一定的安全保障,多层叠加形成纵深防御。

原则三:持续监控。供应链安全不是一次性的。你需要持续监控依赖的更新、新的漏洞公告、社区的投毒报告、以及你自己的 SBOM 变更。

原则四:快速响应。当发现供应链攻击时,响应速度决定了损失大小。自动化凭据轮换、自动回滚到安全版本、自动通知相关人员——这些都应该在应急预案中提前规划。

原则五:社区协作。供应链安全是集体行动问题。共享威胁情报、参与安全社区、报告漏洞、贡献安全工具——每个参与者都从社区安全中受益。

AI 项目的特殊考虑

AI 项目除了传统的代码依赖,还有模型依赖数据依赖

模型依赖:从 Hugging Face 等仓库下载的预训练模型。模型的序列化文件(如 pickle)可能包含恶意代码。建议使用 safetensors 格式(不包含可执行代码)而非 pickle。

数据依赖:训练数据集可能被投毒。攻击者向数据集中注入带有特定触发器的样本,使得模型在推理时产生异常行为。使用数据完整性校验和数据集签名来防范。

Prompt 模板依赖:开源的 Agent 框架和 Prompt 模板库可能被植入恶意指令。在使用开源模板前,审查其内容是否包含可疑的系统提示。

图表加载中…

零信任不是一步到位的。从最容易实施的开始:启用 lockfile、固定 Actions 哈希、添加 npm audit 到 CI。然后逐步升级到代码签名、SBOM 生成、行为监控。每升级一次,你的供应链安全性就提升一个等级。

零信任架构的最大挑战不是技术,而是文化和流程。它要求开发者改变「npm install 就可以」的习惯,增加验证步骤、审批流程、监控告警。这些会增加开发摩擦,但相对于供应链攻击的代价,这是必要的投资。

8实战:构建你的 AI 项目供应链安全体系

下面是一个具体的实施清单,帮助你在 AI 项目中逐步建立供应链安全防御。

立即执行(今天就能做)

确保 package-lock.json 存在并提交到 Git。在 CI 中使用 npm ci 而非 npm install。运行 npm audit 修复所有已知漏洞。检查 GitHub Actions 工作流中是否有使用标签引用的 Actions,替换为 SHA 哈希。

一周内完成

在你的项目中添加锁文件变更审查的 Code Review 规则。启用 GitHub 的 Dependabot 安全更新。安装 Socket.dev CLI 工具对你的依赖进行行为分析。编写一个脚本定期检查 node_modules 中的 postinstall 脚本。

一个月内完成

为你的公共 npm 包启用 Sigstore 签名(publishConfig.provenance: true)。在 CI 中生成 SBOM 并存档。建立凭据轮换计划(至少每季度一次)。制定供应链攻击应急响应预案。

持续改进

参与开源安全社区,关注 npm 安全公告和 PyPI 安全公告。定期审查你的依赖列表,移除不再需要的包(减少攻击面)。将 SLSA 框架作为长期目标,逐步提升供应链安全等级。考虑使用内部私有仓库代理(如 Verdaccio、Nexus),在代理层统一进行包的验证和过滤。

AI 项目的额外检查清单

检查所有 Hugging Face 模型是否使用 safetensors 格式。审查 Agent 框架的 Prompt 模板是否包含可疑指令。对训练数据集进行完整性校验。监控 AI 依赖(torch、tensorflow、langchain 等)的安全公告。

将供应链安全检查纳入你的 Code Review 流程。每次有依赖更新时,不仅检查功能变更,还要检查:这个新版本是否有安全公告?锁文件变更是否合理?是否有新增的 postinstall 脚本?

不要陷入「安全完美主义」陷阱。你不可能防御所有攻击。目标是建立一个成本效益最优的防御体系——用合理的工作量降低最大的风险。从立即执行的 5 项开始,逐步推进。

9扩展阅读与参考资源

以下是进一步学习软件供应链安全的推荐资源。

官方文档

Sigstore 官方文档:sigstore.dev — 学习代码签名的完整工具链。SLSA 框架文档:slsa.dev — 了解供应链安全等级标准。npm 安全指南:docs.npmjs.com — npm 内置的安全功能和最佳实践。GitHub Actions 安全指南:docs.github.com — CI/CD 安全配置。

安全工具

Socket.dev — npm 包行为实时监控平台。Trivy — 容器和依赖漏洞扫描器(Aqua Security 出品)。syft — SBOM 生成工具。cosign — Sigstore 代码签名工具。npm audit — npm 内置安全审计。

经典案例分析

Shai-Hulud 蠕虫分析报告(Palo Alto Networks Unit 42)— 理解自复制 npm 蠕虫的工作原理。TeamPCP 攻击技术分析(Cloud Security Alliance)— 学习级联供应链攻击的完整链路。TanStack 事件事后分析(TanStack 官方)— 了解 Pwn Request + 缓存投毒 + OIDC 提取的三步攻击。

社区与资讯

OpenSSF(Open Source Security Foundation)— 开源安全基金会。npm Security Advisories — npm 安全公告。PyPI Security — Python 包安全资讯。StepSecurity Blog — 开发者安全工具和分析报告。

建议从 Shai-Hulud 蠕虫的技术报告开始阅读——它是最能帮助你理解供应链攻击「为什么危险」和「如何传播」的案例分析。理解攻击,才能构建有效的防御。

供应链安全领域发展极快。今天的安全最佳实践可能明天就被新的攻击手段打破。保持关注安全社区的最新动态,定期更新你的防御策略。

继续你的 AI 学习之旅

浏览更多 AI 知识库文章,或者探索 GitHub 上的优质 AI 项目