2024年3月29日,一位负责 PostgreSQL 性能优化的微软工程师 Andres Freund 注意到了一些异常情况。他连接到 Debian 测试机器的 SSH 连接耗时异常,达到了 500 毫秒。他的调查最终揭示了一个有史以来最复杂的供应链攻击之一:一个后门被故意插入到了 xz-utils 中,这是一个基础压缩库,几乎存在于所有现有的 Linux 系统上。
xz 事件是开源安全的一个分水岭。它暴露的并非代码中的漏洞,而是支撑关键基础设施的人类和组织系统中的脆弱性。一年多过去了,问题是:实际情况究竟有何改变?
事件:究竟发生了什么
这次攻击展现了耐心和社会工程的精湛技巧。一位使用 “Jia Tan” 身份的人从 2022 年开始为 xz-utils 项目做贡献。在近两年的时间里,他们通过合法的贡献建立了信任——修复错误、改进文档、回应问题。他们逐渐成为了该项目的共同维护者。
原始维护者 Lasse Collin 是一名独立开发者,将 xz-utils 作为副项目维护。他正经历职业倦怠,而 Jia Tan 主动提出分担负担。这是开源世界中一个熟悉的故事——过度劳累的维护者 desperate for help 接受志愿者的贡献。
获得信任后,Jia Tan 引入了一系列经过精心混淆的更改。后门不在 xz 源代码本身,而是在测试固件中——这些二进制文件看似是压缩的测试数据,但实际上包含了有效载荷。构建系统被修改,在编译过程中提取并注入这个有效载荷,但只针对 Debian 和基于 RPM 的特定分发版。结果是在 SSH 认证路径中植入了一个后门,可能允许未经授权的远程访问。
其复杂性令人瞩目。恶意代码被分散在数月内的多次提交中。包含有效载荷的测试文件通过了常规审查。构建系统的修改被伪装成维护改进。自动化扫描器和 CI 系统没有标记任何可疑内容。
它揭示了关于 OSS 信任的什么
xz 事件并未揭示新的漏洞。它使一个已存在的漏洞无法忽视:开源生态系统建立在信任之上,而这种信任几乎没有正式的验证机制。
维护者问题
关键基础设施依赖于由一两个人维护的项目,这些维护者通常没有报酬,常常处于倦怠状态。xz-utils 的维护者是一个单独的个体,他已经在这个项目上工作了十多年。当有人提出提供帮助时,拒绝不是一个现实的选择。这种模式在数千个关键开源项目中重复出现。
令人不安的事实是,许多生产系统所依赖的库都是由那些完全有理由放弃维护的人在维护。xz 攻击直接利用了这一现实。
身份问题
Jia Tan 是一个化名。攻击背后的身份尚未被公开确认。开源贡献平台——GitHub、GitLab、邮件列表——的身份验证非常有限。你可以创建一个账户,建立贡献历史,并获得关键项目的提交权限,而无需证明你是谁。
在许多方面,这是开源的一个特性。匿名贡献有着悠久而宝贵的历史。但 xz 事件表明,当这种模式应用于关键基础设施时,会带来安全风险。
审查问题
恶意变更之所以通过了审查,是因为它们就是为此设计的。二进制测试 fixture 不是人类可读的。构建系统的变更详细审查起来很繁琐。攻击面存在于项目中受到最少审查的领域——不是核心算法,而是周围的基础设施。
SLSA 和 Sigstore:供应链验证的进展
行业对供应链威胁的响应已经围绕两大倡议形成:SLSA(软件制品供应链安全等级)和 Sigstore。
SLSA 框架
SLSA 为软件供应链定义了一套渐进式的安全等级。在 SLSA 等级 1,构建过程被文档化。在等级 2,构建是自动化的,并生成签名的出处证明。在等级 3,构建环境得到加固,出处证明被验证。在等级 4,构建是密封的,所有依赖都被明确声明。
# 示例 SLSA 出处证明
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [{
"name": "myapp",
"digest": {"sha256": "abc123..."}
}],
"predicateType": "https://slsa.dev/provenance/v1",
"predicate": {
"buildDefinition": {
"buildType": "https://github.com/slsa-framework/slsa-github-generator",
"externalParameters": {
"source": {
"uri": "git+https://github.com/org/repo@refs/tags/v1.2.3",
"digest": {"sha1": "def456..."}
}
}
},
"runDetails": {
"builder": {
"id": "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v2.0.0"
}
}
}
}
自 xz 事件以来,SLSA 的采用显著增加。GitHub Actions 现在原生支持 SLSA 等级 3 出处证明生成。Google、npm 和 PyPI 都已将 SLSA 验证集成到他们的生态系统中。但采用仍然不均衡——许多关键项目仍停留在等级 0。
Sigstore
Sigstore 为软件制品提供无密钥签名。与传统 PKI 的开销相比,Sigstore 不需要管理长期存在的签名密钥(这些密钥本身会成为攻击目标),而是使用与 GitHub 或 Google 等身份提供商绑定的短期证书。这使得个人开发者能够实际地签名他们的发布版本,而无需传统 PKI 的开销。
npm 现在默认使用 Sigstore 签名所有软件包。PyPI 已集成 Sigstore 证明。使用 Sigstore(通过 cosign)对容器镜像进行签名已成为云原生部署中的标准实践。这些是有意义的步骤,但它们主要验证制品是否来自声明的来源 — 它们并不验证来源本身是否可信。
SBOM 的实际应用
软件物料清单(SBOM)已从理论概念转变为监管要求。美国网络安全行政命令要求向联邦政府销售的软件提供 SBOM。欧盟《网络安全法案》也有类似要求。在实践中,大多数组织正在生成 SBOM,但很少有组织能有效利用它们。
SBOM 告诉您软件中包含哪些组件。仅凭这一点,它只是一个列表,而不是安全控制措施。价值来自于您如何利用这些信息:监控新发现的漏洞、验证组件未被篡改、确保满足许可证义务。
# 使用 syft 生成 SBOM
syft packages ./myapp -o spdx-json > sbom.spdx.json
# 扫描 SBOM 查找已知漏洞
grype sbom:sbom.spdx.json
# 验证组件完整性
cosign verify-attestation
--type spdx
--certificate-identity builder@github.com
--certificate-oidc-issuer https://accounts.google.com
myregistry/myapp:latest
SBOM 生成工具已经相当成熟。Syft、CycloneDX 和语言特定工具可以为大多数技术栈生成准确的 SBOM。差距在于消费 — 将 SBOM 数据集成到安全工作流程、采购决策和事件响应过程中。
依赖审查工具
xz 事件加速了依赖审查工具的采用,这些工具在合并项目依赖更改之前会分析这些更改。
GitHub 依赖审查会自动标记拉取请求中的依赖更改,突出显示新引入的漏洞、许可证更改和有风险版本升级。Socket.dev更进一步,分析包行为以检测供应链攻击 — 寻找网络访问、文件系统写入和混淆代码等指标,这些表明恶意意图而非已知 CVE。
Deps.dev 由 Google 提供,全面展示了开源依赖关系图,包括安全建议、SLSA 来源证明和 OpenSSF Scorecard 评分。它正在成为评估开源包可信度的首选资源。
这些工具很有价值,但有一个根本局限性:它们最适用于已知的攻击模式。xz 攻击专门设计用于规避自动检测。新颖、复杂的攻击将继续绕过自动化工具。
可重现构建
可重现构建——即能够独立验证二进制文件是否从特定源代码构建——本可以使 xz 攻击变得可检测。如果任何人都能从源代码构建 xz-utils 并将结果与分发的二进制文件进行比较,注入的后门就会显而易见。
可重现构建项目已取得显著进展。Debian 现在跟踪其整个档案的可重现性——超过 95% 的包是可重现的。Arch Linux、NixOS 和 Guix 也有类似举措。但对于具有复杂构建系统、非确定性工具链或嵌入时间戳的项目,可重现性仍然具有挑战性。
实际上,很少有组织验证他们部署的二进制文件与他们审查的源代码是否匹配。这就是可重现构建旨在填补的差距,采用率正在增长,但远未普及。
开源中的社会工程学
xz 事件中最令人不安的教训或许是,攻击向量并非技术性的——而是社会性的。攻击者通过合法、有用的贡献获得信任。通过傀儡账户对维护者施压,抱怨发布速度慢,并建议给 Jia Tan 更多权限。
这极难防御。你不可能同时拥有一个开放、欢迎的社区,并对每个贡献者进行严格的身份验证。OpenSSF 已发布了维护者安全实践指南,包括对关键变更进行多维护者批准的建议、代码与发布流程的职责分离,以及对社会压力活动的警惕。
但这些指南已经给本已不堪重负的维护者增加了摩擦和开销。根本的紧张关系依然存在:开源模式依赖于信任和低贡献门槛,而安全则需要验证和把关。
组织实际应该做什么
鉴于以上所有情况,运行开源软件的组织实际应该做什么?以下是一个优先级明确的实用清单。
1. 了解你的关键依赖关系。生成软件物料清单(SBOM)并识别哪些开源组件位于关键路径上。并非所有依赖关系都带来同等风险——重点关注处理身份验证、加密、网络访问和数据处理的组件。
2. 监控依赖关系健康状况。使用 OpenSSF Scorecard、Deps.dev 和 Socket.dev 来评估关键依赖关系的健康状况和安全态势。没有持续集成(CI)、未签名发布且无审查流程的单维护者项目属于高风险项目——不是因为维护者有恶意,而是因为他们容易受到攻击。
3. 锁定和验证依赖关系。使用锁定文件、哈希验证和 SLSA 来源验证检查。不要在生产构建中使用最新版本。每个依赖关系更新都应是经过深思熟虑和审查的决定。
4. 在 CI 中实现依赖关系审查。每个修改依赖关系的拉取请求都应触发自动分析。至少,应检查已知漏洞和许可证变更。理想情况下,使用 Socket.dev 等行为分析工具。
5. 为你的依赖关系提供资金支持。如果你的业务依赖于某个开源项目,请为其提供资金支持。这不是利他主义——这是风险管理。获得资金支持的维护者不易倦怠,更能维持安全实践,也不易受到敌对贡献者以提供帮助为名的社会工程学攻击。
6. 构建组织能力。你组织中应该有人了解你的依赖关系树、关键组件的安全态势以及应对供应链事件的流程。这种能力不会自动存在——必须刻意构建。
发生了什么变化,什么没有变化
xz 事件是一个催化剂。它加速了已经 underway 的趋势——SLSA 的采用、Sigstore 的集成、SBOM 的要求以及依赖关系审查工具。安全工具生态系统比 2024 年 3 月有了显著改善。
没有变化的是开源生态系统的基础结构。关键基础设施仍然依赖于资金不足、负担过重的维护者。贡献者的身份验证仍然 minimal。社会工程学仍然是一种可行的攻击向量。而绝大多数使用开源软件的组织仍然没有足够的流程来评估和管理供应链风险。
xz 事件是一名工程师注意到 500 毫秒延迟而被发现的。我们很幸运。问题是,行业是否会做足够的工作,确保下次不再需要依靠运气。
