软件供应链攻击正在利用我们的信任假设

本文深入分析软件供应链中的隐性信任假设如何被攻击者利用,详细介绍了近年来发生的典型攻击案例,包括仿冒包、凭证窃取、构建链污染和恶意维护者等攻击方式,并探讨了TypoGard、Zizmor、PyPI可信发布、Homebrew构建溯源和Go Capslock等新兴防御技术。

供应链攻击正在利用我们的信任假设

每次运行 cargo addpip 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每周有超过100万次下载。

2025年,一名攻击者修改了reviewdog/actions-setup GitHub Action的v1标签,指向一个包含代码以转储秘密的恶意版本。这很可能通过其对tj-actions/eslint-changed-files的依赖导致另一个流行的Action tj-actions/changed-files被入侵,而tj-actions/eslint-changed-files又依赖于被入侵的reviewdog Action。这种级联入侵影响了数千个使用changed-files Action的项目。

虽然污染的流水线攻击相对于仿冒包或凭证窃取较为罕见,但它们代表了攻击者复杂度的升级。随着更强的防御措施到位,攻击者被迫向供应链上游移动。最坚定的攻击者愿意花费数年时间为一次攻击做准备。

恶意维护者

2024年3月发现的XZ Utils后门几乎破坏了全球数百万个Linux系统。攻击者花费超过两年时间对项目做出合法贡献,然后获得维护者访问权限。他们随后滥用这种信任,通过一系列看似无害的提交插入了一个复杂的后门,该后门将授予对任何使用受感染版本的系统的远程访问权限。

最终,你必须信任你的依赖项的维护者。安全的构建流水线无法保护免受决定插入恶意代码的受信任维护者的影响。随着开源维护者越来越不堪重负,以及AI工具使得大规模生成令人信服的贡献更容易,这种信任模型正面临前所未有的挑战。

新防御措施

随着攻击变得越来越复杂,防御者正在构建匹配的工具。这些新方法使信任假设变得明确和可验证,而不是隐性和可利用。每种方法针对攻击者已取得成功的供应链的不同层面。

TypoGard和Typomania

大多数包管理器现在包含某种形式的仿冒包保护,但它们通常使用传统的相似性检查,如测量Levenshtein距离,这会产生过多的误报,需要手动审查。

TypoGard通过使用多个上下文感知指标来填补这一空白,以低误报率和最小开销检测仿冒包:

  • 重复字符(例如rustdeciimal)
  • 基于键盘布局的常见拼写错误
  • 交换字符(例如reqeusts而不是requests)
  • 软件包流行度阈值以专注于高风险目标

该工具针对npm,但概念可以扩展到其他语言。Rust基金会发布了一个Rust端口Typomania,已被crates.io采用,并成功捕获了多个恶意软件包。

Zizmor

Zizmor是GitHub Actions的静态分析工具。Actions有较大的攻击面,编写复杂工作流可能困难且容易出错。工作流有许多微妙的方式可能引入漏洞。

例如,Ultralytics通过其一个工作流中的模板注入被入侵。

1
2
3
4
5
6
- name: Commit and Push Changes
  if: (... || github.event_name == 'pull_request_target' || ...
  run: |
      ...
      git pull origin ${{ github.head_ref || github.ref }}
      ...

由pull_request_target事件触发的工作流以对仓库秘密的写权限访问运行。攻击者从一个具有恶意名称的分支打开了一个拉取请求。当工作流运行时,github.head_ref变量扩展为恶意分支名称,并作为run命令的一部分以工作流的提升权限执行。

reviewdog/actions-setup攻击也是通过将Action的v1标签更改为指向恶意提交来部分实施的。任何在其工作流中使用reviewdog/actions-setup@v1的人都会静默开始获取恶意版本,而无需对其自己的工作流进行任何更改。

Zizmor标记了以上所有情况。它包括一个dangerous-trigger规则来标记由pull_request_target触发的工作流,一个template-injection规则,以及一个unpinned-uses检查,该检查会在使用reviewdog/actions-setup@v1时警告Action不要使用可变引用(如标签或分支名称)。

PyPI可信发布和证明

PyPI通过两个互补的功能采取了重要步骤来解决几个隐性信任假设:可信发布和证明。

Trail of Bits与PyPI合作开发了可信发布,它消除了对长期存在的API令牌的需求。开发者不是存储可能被盗的秘密,而是一次性配置信任关系:“此GitHub仓库和工作流可以发布此软件包。”当工作流运行时,GitHub向PyPI发送一个短期存在的OIDC令牌,其中包含关于仓库和工作流的声明。PyPI验证此令牌由GitHub的密钥签名,并响应一个短期存在的PyPI令牌,工作流可以使用该令牌发布软件包。使用自动生成的、最小范围的、短期存在的令牌大大降低了入侵风险。

没有长期存在和过度特权的API令牌,攻击者必须转而入侵发布的GitHub工作流本身。虽然Ultralytics攻击表明CI/CD流水线入侵仍然是一个真实威胁,但消除用户手动管理凭证的需求消除了用户错误的来源,并进一步减少了攻击面。

在此基础上,Trail of Bits在2024年底再次与PyPI合作,通过PEP 740引入了索引托管的数字证明。证明使用Sigstore将每个发布的软件包加密绑定到其构建来源。使用PyPI发布GitHub Action的软件包自动包含证明,这些证明作为可验证的记录,准确显示软件包在何处、何时以及如何构建。

超过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变得 significantly harder。

1
2
3
4
5
% brew verify --help
Usage: brew verify [options] formula [...]

Verify the build provenance of bottles using GitHub's attestation tools.
This is done by first fetching the given bottles and then verifying their provenance.

每个证明包括Git提交、运行的工作流和其他构建时元数据。这将信任假设(“我信任此bottle是从我期望的源代码构建的”)转变为可验证的事实。

证明的实现通过“回填”过程处理了历史上的bottle,为系统就位之前构建的软件包创建证明。因此,所有官方的Homebrew软件包都包括证明。

brew verify 命令使得检查来源变得简单,尽管该功能仍处于测试阶段,并且默认情况下验证不是自动的。有计划最终将此功能扩展到第三方仓库,将相同的安全保证带给更广泛的Homebrew生态系统。

Go Capslock

Capslock是一个工具,静态识别Go程序的能力,包括以下内容:

  • 文件系统操作(读取、写入、删除文件)
  • 网络连接(出站请求、监听端口)
  • 进程执行(生成子进程)
  • 环境变量访问
  • 系统调用使用
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
% capslock --packages github.com/fatih/color
Capslock is an experimental tool for static analysis of Go packages.
Share feedback and file bugs at https://github.com/google/capslock.
For additional debugging signals, use verbose mode with -output=verbose
To get machine-readable full analysis output, use -output=jso`

Analyzed packages:
    github.com/fatih/color v1.18.0
    github.com/mattn/go-colorable v0.1.13
    github.com/mattn/go-isatty v0.0.20
    golang.org/x/sys v0.25.0

CAPABILITY_FILES: 1 references
CAPABILITY_READ_SYSTEM_STATE: 41 references
CAPABILITY_SYSTEM_CALLS: 1 references

这种方法代表了供应链安全的转变。能力分析不是关注谁编写了代码或它来自哪里,而是检查代码实际可以做什么。一个意外获得网络访问的JSON解析库会立即引发红旗,无论变更来自受损的供应链还是直接来自维护者。

在实践中,静态能力检测可能很困难。语言特性如运行时反射和不安全操作使得不可能完全准确地静态检测能力。尽管有局限性,能力检测作为分层防御的一部分,提供了关键的安全网,以抵御供应链攻击。

Capslock为Go开创了这种方法,并且该概念已成熟,可在其他语言中采用。随着供应链攻击变得越来越复杂,能力分析提供了一条有前途的前进道路。验证代码能做什么,而不仅仅是它来自哪里。

我们何去何从

供应链攻击并未放缓。如果有什么变化的话,它们正变得更加自动化、更加复杂和更加精密,以 targeting broader audiences。仿冒包活动正 targeting 下载量达数十亿的软件包,发布者令牌和CI/CD流水线正被入侵以在源头污染软件,耐心的攻击者正花费数年时间建立声誉然后发动攻击。

使软件生态系统能够扩展的隐性信任正被武器化来对付我们。理解你的信任假设是第一步。问自己这些问题:

  • 我的生态系统是否阻止仿冒包?
  • 它如何保护免受受损发布者令牌的侵害?
  • 我可以验证构建来源吗?
  • 我知道我的依赖项有什么能力吗?

一些生态系统已经开始构建防御措施。了解有哪些工具可用,并立即开始使用它们。在发布到PyPI或crates.io时使用可信发布。用Zizmor检查你的GitHub Actions。使用It-Depends和Deptective来了解软件实际依赖什么。在可行的地方验证证明。使用Capslock查看Go软件包的能力,更重要的是,当引入新能力时要意识到。

但没有一个生态系统被完全覆盖。在工具缺乏的地方推动更好的默认设置。每一个验证的证明、每一个捕获的仿冒包和每一个标记的易受攻击的GitHub Action都使整个行业更具弹性。我们不能完全从供应链中消除信任,但我们可以努力使这种信任变得明确、可验证和可撤销。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计