供应链攻击正在利用我们的信任假设
每次运行 cargo add 或 pip install 时,你都在进行一次信任的跳跃。你相信下载的代码包含你期望的内容,来自你期望的人,并且做你期望的事情。这些期望对现代开发如此基础,以至于我们很少思考它们。然而,攻击者正在系统地利用这些假设中的每一个。
仅2024年,PyPI和npm就移除了数千个恶意软件包;多个知名项目在构建过程中被直接注入了恶意软件;XZ Utils后门几乎进入了全球数百万个Linux系统。
依赖扫描只能捕获已知漏洞。它无法捕获仿冒包窃取凭据、被入侵的维护者发布恶意软件,或攻击者毒化构建管道本身的情况。这些攻击之所以成功,是因为它们利用了使现代软件开发成为可能的信任本身。
本文分析了使软件供应链易受攻击的信任假设,剖析了最近利用这些假设的攻击,并重点介绍了跨生态系统构建的前沿防御措施,旨在将隐性信任转变为明确、可验证的保证。
隐性信任
对许多开发人员而言,软件供应链始于软件物料清单(SBOM)和依赖扫描,它们共同回答了两个基本问题:你拥有什么代码,以及它是否包含已知漏洞?但了解你拥有什么只是最低要求。随着复杂攻击变得越来越普遍,你还需要了解代码的来源以及它如何到达你手中。
你信任安装的是你期望的包。你假设运行 cargo add rustdecimal 是安全的,因为rustdecimal是一个知名且广泛使用的库。或者等等,也许它拼写为 rust_decimal?
你信任包是由包维护者发布的。当一个流行的包开始附带预编译二进制文件以节省构建时间时,你可能会决定信任包作者。然而,许多注册表缺乏强有力的验证机制来确认发布者身份。
你信任包是从包源代码构建的。你可能在一个安全意识强的团队工作,在升级依赖项之前会审计公共仓库中的代码更改。但如果分发的包是从未出现在仓库中的代码构建的,这就毫无意义。
你信任维护者本身。最终,安装第三方代码意味着信任包维护者。审计你依赖的每一行代码是不切实际的。我们假设那些成熟且广泛采用的包的维护者不会突然决定添加恶意代码。
这些假设超越了传统的包管理器。当你运行GitHub Action、使用Homebrew安装工具或执行方便的 curl ... | bash 安装脚本时,存在相同的信任关系。理解这些隐性信任关系是评估和减轻供应链风险的第一步。
近期攻击
攻击者正在利用供应链每一层的信任假设。最近的事件范围从简单的仿冒包到多年策划的活动,展示了攻击者的策略如何演变并变得更加复杂。
欺骗性仿冒包
仿冒包攻击涉及发布与合法包名称相似的恶意包。运行 cargo add rustdecimal 而不是 rust_decimal 可能会安装恶意软件而不是预期的合法库。这种确切的攻击于2022年在crates.io上发生。恶意的rustdecimal模仿了流行的rust_decimal包,但包含一个在调用时执行恶意二进制的Decimal::new函数。
这种攻击的简单性使得攻击者能够发起众多大规模活动,特别是针对PyPI和npm。自2022年以来,已有多个仿冒包活动针对每周总下载量达12亿的包。仅PyPI和npm就发布了数千个恶意包。这类攻击发生得如此频繁,以至于例子多得无法在此列举。2023年,研究人员记录了一项活动,注册了40个流行PyPI包的900个仿冒包,并发现恶意软件在crates.io上暂存。攻击只会加剧,在2024年的一次活动中发布了500个恶意包。
依赖混淆采取了不同的方法,直接利用包管理器逻辑。安全研究员Alex Birsan在2021年演示并命名了这种攻击类型。他发现许多组织使用的内部包名称要么被泄露,要么可猜测。通过将具有与这些内部包相同名称的包发布到公共注册表,Birsan能够诱使包管理器下载他的版本。Birsan的概念验证识别了三种编程语言和35个组织中的漏洞,包括Shopify、Apple、Netflix、Uber和Yelp。
2022年,一名攻击者使用这种技术在PyTorch的夜间版本中包含了恶意代码,持续五天。一个名为torchtriton的内部依赖项托管在PyTorch的夜间包索引中。一名攻击者向PyPI发布了一个同名的恶意包,该包具有优先权。结果,PyTorch的夜间版本在恶意软件被发现前包含了恶意代码五天。
虽然这些攻击发生在安装点,但其他攻击通过直接破坏发布过程本身采取了更直接的方法。
被盗密钥
被入侵的账户是另一个常见的攻击向量。攻击者获取泄露的密钥、被盗的令牌或猜出的密码,并能够直接代表受信任实体发布恶意代码。一些最近的事件显示了这种攻击的规模:
- ctrl/tinycolor (2025年9月):自我传播的恶意软件收集了npm API凭据,并使用这些凭据发布额外的恶意包。超过40个包被入侵,每周下载量超过200万次。
- Nx (2025年8月):一个被入侵的令牌允许攻击者发布包含利用已安装的AI CLI工具(Claude、Gemini、Q)进行侦察、窃取加密货币钱包、GitHub/npm令牌和SSH密钥的脚本的恶意版本,然后在将数据外泄到公共GitHub仓库之前影响数千名开发人员。
- rand-user-agent (2025年5月):一个包含恶意软件的恶意版本仅在研究人员注意到最近发布而源代码数月未更改后才被发现。
- rspack (2024年12月):被盗的npm令牌使攻击者能够在每周总下载量为50万次的包中发布加密货币矿工。
- UAParser.js (2021年10月):一个被入侵的npm令牌被用于发布包含加密货币矿工的恶意版本。该库在攻击时每周有数百万次下载。
- PHP Git服务器 (2021年3月):被盗凭据允许攻击者直接将后门注入PHP源代码。幸运的是,更改内容在发布前被PHP团队轻松发现并移除。
- Codecov (2021年1月):攻击者在公共Docker镜像层中找到了部署密钥,并使用它修改了Codecov的Bash Uploader工具,在发现前数月静默外泄环境变量和API密钥。
被盗密钥仍然是最可靠的供应链攻击向量之一。但随着组织实施更强的身份验证和更好的密钥管理,攻击者正从窃取密钥转向入侵使用这些密钥的系统。
被毒化的管道
一些攻击者没有窃取凭据,而是通过入侵构建和分发系统本身,通过合法渠道分发恶意软件。代码审查和其他安全检查通过直接将恶意代码注入CI/CD管道而被完全绕过。
2020年的SolarWinds攻击是这类攻击中最著名的之一。攻击者入侵了构建环境,并在编译期间直接将恶意代码插入Orion软件。然后,恶意版本的Orion通过SolarWinds的合法更新渠道进行签名和分发。这次攻击影响了数千个组织,包括多个财富500强公司和政府机构。
更近的是,在2024年底,一名攻击者入侵了Ultralytics构建管道以发布多个恶意版本。攻击者使用项目GitHub Actions中的模板注入来获得对CI/CD管道的访问权限,并毒化了GitHub Actions缓存以直接在构建中包含恶意代码。在攻击时,Ultralytics每周有超过一百万次下载。
2025年,一名攻击者修改了reviewdog/actions-setup GitHub action v1标签,指向包含代码以转储密钥的恶意版本。这很可能通过其对tj-actions/eslint-changed-files的依赖导致了另一个流行操作tj-actions/changed-files的入侵,而后者又依赖于被入侵的reviewdog操作。这种级联入侵影响了数千个使用changed-files操作的项目。
虽然与被毒化管道的攻击相比,仿冒包或凭据盗窃相对较少,但它们代表了攻击者复杂性的升级。随着更强的防御措施到位,攻击者被迫向上游移动供应链。最有决心的攻击者愿意花费数年时间为一次攻击做准备。
恶意维护者
2024年3月发现的XZ Utils后门几乎入侵了全球数百万个Linux系统。攻击者花费了两年多时间向项目做出合法贡献,然后获得维护者访问权限。然后他们滥用这种信任,通过一系列看似无害的提交插入了一个复杂的后门,这将授予使用被入侵版本的任何系统的远程访问权限。
最终,你必须信任你的依赖项的维护者。安全的构建管道无法保护免受决定插入恶意代码的受信任维护者的侵害。随着开源维护者越来越不堪重负,以及AI工具使大规模生成令人信服的贡献变得更容易,这种信任模型正面临前所未有的挑战。
新防御措施
随着攻击变得越来越复杂,防御者正在构建匹配的工具。这些新方法正在使信任假设变得明确和可验证,而不是隐性和可利用。每种方法都针对攻击者已取得成功的供应链的不同层。
TypoGard和Typomania
大多数包管理器现在包括某种形式的仿冒包保护,但它们通常使用传统的相似性检查,如测量Levenshtein距离,这会产生过多的误报,需要手动审查。
TypoGard通过使用多个上下文感知指标来填补这一空白,以低误报率和最小开销检测仿冒包:
- 重复字符(例如rustdeciimal)
- 基于键盘布局的常见拼写错误
- 交换字符(例如reqeusts而不是requests)
- 包流行度阈值以专注于高风险目标
该工具针对npm,但概念可以扩展到其他语言。Rust Foundation发布了一个Rust端口Typomania,已被crates.io采用,并成功捕获了多个恶意包。
Zizmor
Zizmor是GitHub Actions的静态分析工具。Actions具有很大的攻击面,编写复杂的工作流可能很困难且容易出错。工作流有许多微妙的方式可能引入漏洞。
例如,Ultralytics通过其一个工作流中的模板注入被入侵。
|
|
由pull_request_target事件触发的工作流运行时具有对仓库密钥的写入权限访问。攻击者从一个具有恶意名称的分支打开了拉取请求。当工作流运行时,github.head_ref变量扩展为恶意分支名称,并作为运行命令的一部分以工作流的提升权限执行。
reviewdog/actions-setup攻击也是通过将操作的v1标签更改为指向恶意提交来部分实施的。任何在其工作流中使用reviewdog/actions-setup@v1的人都会静默开始获得恶意版本,而无需对其自己的工作流进行任何更改。
Zizmor标记了以上所有内容。它包括一个dangerous-trigger规则来标记由pull_request_target触发的工作流,一个template-injection规则,以及一个unpinned-uses检查,该检查会在使用reviewdog/actions-setup@v1时警告操作不要使用可变引用(如标签或分支名称)。
PyPI可信发布和证明
PyPI通过两个互补的功能采取了重要步骤来解决几个隐性信任假设:可信发布和证明。
Trail of Bits与PyPI合作开发了可信发布¹,它消除了对长期存在的API令牌的需求。开发人员不是存储可能被盗的密钥,而是配置一次信任关系:“这个GitHub仓库和工作流可以发布这个包。“当工作流运行时,GitHub向PyPI发送一个短期OIDC令牌,其中包含有关仓库和工作流的声明。PyPI验证此令牌由GitHub的密钥签名,并使用短期PyPI令牌进行响应,工作流可以使用该令牌发布包。使用自动生成的、范围最小化的、短期令牌大大降低了入侵风险。
没有长期存在且过度特权的API令牌,攻击者必须转而入侵发布的GitHub工作流本身。虽然Ultralytics攻击表明CI/CD管道入侵仍然是一个真正的威胁,但消除用户手动管理凭据的需求消除了用户错误的来源,并进一步减少了攻击面。
在此基础上,Trail of Bits再次与PyPI合作,在2024年底通过PEP 740引入了索引托管的数字证明。证明使用Sigstore将每个发布的包加密绑定到其构建来源。使用PyPI发布GitHub操作的包自动包括证明,这些证明作为可验证的记录,准确显示包在何处、何时以及如何构建。
超过30,000个包使用可信发布,“Are We PEP 740 Yet?“跟踪了最流行包中证明的采用情况(撰写本文时前360个包中的86个)。最后一部分,自动客户端验证,仍然在进行中。像pip和uv这样的客户端工具尚未自动验证证明。在那之前,证明提供透明度和可审计性,但在包安装期间不提供主动保护。
Homebrew构建来源
隐性信任假设超越了编程语言和库。当你运行brew install安装二进制包(或bottle)时,你相信你下载的bottle是由Homebrew的官方CI从预期的源代码构建的,并且它不是由找到方法入侵Homebrew的bottle托管或以其他方式篡改bottle内容的攻击者上传的。
Trail of Bits与Alpha-Omega和OpenSSF合作,帮助使用GitHub的证明向Homebrew添加构建来源。Homebrew构建的每个bottle现在都带有加密证明,将其链接到创建它的特定GitHub Actions工作流。这使得被入侵的维护者静默将bottle替换为恶意版本变得更加困难。
|
|
每个证明包括Git提交、运行的工作流和其他构建时元数据。这将信任假设(“我相信这个bottle是从我期望的源代码构建的”)转变为可验证的事实。
证明的实现通过"回填"过程处理了历史bottle,为系统到位前构建的包创建证明。因此,所有官方的Homebrew包都包括证明。
brew verify命令使检查来源变得简单,尽管该功能仍处于测试阶段,并且默认情况下验证不是自动的。有计划最终将此功能扩展到第三方仓库,为更广泛的Homebrew生态系统带来相同的安全保证。
Go Capslock
Capslock是一个静态识别Go程序能力的工具,包括以下内容:
- 文件系统操作(读取、写入、删除文件)
- 网络连接(出站请求、监听端口)
- 进程执行(生成子进程)
- 环境变量访问
- 系统调用使用
|
|
这种方法代表了供应链安全的转变。能力分析不是关注谁编写了代码或它来自哪里,而是检查代码实际可以做什么。一个意外获得网络访问的JSON解析库会立即引发危险信号,无论更改是来自被入侵的供应链还是直接来自维护者。
在实践中,静态能力检测可能很困难。像运行时反射和不安全操作这样的语言特性使得不可能完全准确地静态检测能力。尽管有局限性,能力检测作为分层防御供应链攻击的一部分提供了关键的安全网。
Capslock为Go开创了这种方法,并且该概念已准备好跨其他语言采用。随着供应链攻击变得越来越复杂,能力分析提供了一条有前途的前进道路。验证代码能做什么,而不仅仅是它来自哪里。
我们何去何从
供应链攻击并未放缓。如果有什么不同的话,它们正变得更加自动化、更加复杂和更加精密,以瞄准更广泛的受众。仿冒包活动正针对具有数十亿下载量的包,发布者令牌和CI/CD管道正被入侵以在源头毒化软件,耐心的攻击者正花费数年时间建立声誉然后发动攻击。
使软件生态系统能够扩展的隐性信任正被武器化来对付我们。理解你的信任假设是第一步。问自己这些问题:
- 我的生态系统是否阻止仿冒包?
- 它如何保护免受被入侵的发布者令牌的侵害?
- 我可以验证构建来源吗?
- 我知道我的依赖项具有什么能力吗?
一些生态系统已经开始构建防御措施。了解有哪些工具可用,并立即开始使用它们。在发布到PyPI或crates.io时使用可信发布。使用Zizmor检查你的GitHub Actions。使用It-Depends和Deptective来了解软件实际依赖什么。在可行的情况下验证证明。使用Capslock查看Go包的能力,更重要的是,当引入新能力时要意识到。
但没有一个生态系统完全覆盖。在工具缺乏的地方推动更好的默认设置。每一个经过验证的证明、每一个被捕获的仿冒包和每一个被标记的易受攻击的GitHub操作都使整个行业更具弹性。我们无法完全消除供应链中的信任,但我们可以努力使这种信任变得明确、可验证和可撤销。
如果你需要帮助理解你的供应链信任假设,请联系我们。
¹ crates.io团队于7月发布了Rust crate的可信发布。