Git哈希链可塑性
摘要
一篇研究论文揭示,Git提交哈希可以被篡改,从而生成第二个具有不同哈希但内容相同且签名有效的有效提交,这破坏了提交哈希完整性和供应链安全性。
暂无内容
查看缓存全文
缓存时间: 2026/07/08 02:16
# Git 哈希链可塑性 来源:https://arxiv.org/html/2607.02820 ###### 摘要 Git 提交签名被广泛信任,作为提交哈希唯一且不可变地标识特定签名内容证据。我们证明这一不变性并不成立。给定任何已签名的提交,攻击者无需访问签名密钥,也无需破解 SHA2,就能生成第二个不同的提交,该提交具有相同的树、相同的元数据、有效的签名,并且能从 GitHub 等 Git 平台获得“已验证”徽章,仅提交哈希不同。修改后的提交会级联地改变所有后续依赖提交哈希的值,因此我们引入术语“哈希链可塑性”来描述这一现象。签名 Git 哈希中的可塑性是由于构成提交的许多数据表示中存在固有的可塑性。在本文中,我们展示了三种这样的可塑化路径:(i) ECDSA 的代数逆变换 ↦ n−ss↦n-s;(ii) RSA 和 EdDSA 的未哈希 OpenPGP 子包(RFC 4880 §5.2.3)的结构性插入;以及 (iii) S/MIME 的 CMS 信封内非规范 DER 长度重新编码(X.690 §10.1)。对于 ECDSA 签名的代数逆变换和子包插入,均能通过本地验证(`git verify-commit`),并且所有三种方法都能在 GitHub 上产生持久、独立的“已验证”记录。我们讨论了 Git 哈希链可塑性对基于哈希的提交阻止、依赖锁定(Nixpkgs、Go 模块、GitHub Actions)以及将提交哈希视为内容可寻址主键的可重现构建系统的影响,并提供了自动化所有三种路径的概念验证工具。 ## 1 引言 Git 通过每个对象的序列化内容的哈希来标识它。对于一个提交,该哈希覆盖了树、父列表、作者和提交者元数据、提交消息,以及——关键地——任何嵌入签名的原始字节。由于哈希是这些字节的确定性函数,提交哈希通常被当作全局唯一、不可变、内容可寻址的标识符:CI 管道依赖它、依赖管理器锁定它、事件响应工具通过它阻止或回滚。引用 Git 自己的文档: > “使用密码学安全的哈希函数带来了额外优势:对象名称可以被签名,第三方可以信任该哈希来寻址签名对象及其引用的所有对象。”—— Git 的 `hash-function-transition` 文档[6](#bib.bib1) 提交签名旨在加强对受信任提交哈希的信任。对提交负载的签名旨在作为可验证、不可伪造的方法来追踪贡献来源;因此,对供应链安全漏洞敏感的项目或生态系统通常会强制采用它。事实上,最近 *trivy-action* 供应链入侵的事后分析表明,替换原始提交上传的恶意提交是通过其 *签名有效性* 来区分的[2](#bib.bib2),直接展示了签名提交的实用性和安全优势。 在本文中,我们展示了签名有效性和哈希唯一性以一种消费者未必期望的方式相互作用。数字签名方案经常允许*可塑性*:给定一个有效签名,通常可以在不知道私钥的情况下,推导出对同一消息和密钥进行验证的第二个语法上不同的签名。应用于已签名提交,可塑化会改变签名字节,从而改变提交哈希,同时保留内容和验证结果。可塑化的提交会级联地可塑化所有后续依赖提交哈希的值,因此我们引入术语来描述这一现象:“哈希链可塑性”。 在本文中,我们做出以下贡献: - • 我们描述了三种适用于提交签名的签名可塑化技术:数学可塑化、结构性可塑化和编码可塑化。这三种技术共同使得 GitHub 支持的所有基于 GPG 的签名方案(包括 ECDSA、RSA、EdDSA 和 S/MIME/CMS)的提交签名能够成功可塑化。 - • 我们观察到这些不仅仅是本地解析器的怪癖:GitHub 的服务端验证器接受上述所有签名方案的可塑化,并且其基于提交哈希键控的持久验证记录[7](#bib.bib3)为相同内容生成多个独立的“已验证”条目(§4)。 - • 我们分析了这些对基于哈希的阻止、依赖锁定和可重现构建验证的影响,其中提交哈希被用作内容主键(§5)。 - • 我们提供了自动化所有三种可塑化技术的概念验证工具,适用于任意提交图,包括图一致地重写后代提交(§6)。 我们的所有代码和概念验证均公开可用,并可通过 Nix Flake 轻松重现:https://github.com/JakeGinesin/git-chain-malleator。 ## 2 背景 一个 Git 提交是一个纯文本对象,其对象名称是其完整序列化的哈希值。该序列化包括树、有序的父列表、作者和提交者行、提交消息,以及当提交被签名时的 `gpgsig` 头部(包含签名)。被签名的负载正是移除了 `gpgsig` 头部的提交对象;因此签名是针对除自身之外的所有内容计算的,然后被重新折叠回哈希覆盖的对象中。 这导致了两个后果,它们共同构成了本文中每种技术的先决条件。首先,由于签名字节位于哈希区域内,对这些字节的任何改变都会改变提交哈希,同时保持树、父列表和消息不变。其次,不那么明显的是,验证约束签名的*语义*内容,但不约束其*精确字节编码*:验证者检查的是存在某个对负载有效的签名,而不是存在其唯一可接受的编码。“一个有效签名”和“这个签名”之间的差距就是可塑化者操作的空间。 我们强调,这与提交哈希函数的抗碰撞性正交,无论是当前使用的 SHA-1 还是 Git 正在过渡的目标 SHA-256(即 SHA-2 系列)[6](#bib.bib1)。我们并不展示两个不同内容共享一个哈希。我们展示一个逻辑内容可以接受许多字节不同但语义相同的序列化,每个都带有有效签名,并且每个哈希都产生*不同*的值。因此,我们描述的可塑性在 SHA-256 迁移后仍然不受影响。 ## 3 可塑化技术 我们将提交签名的可塑化组织为三种技术,根据引入字节更改的层次进行区分。*数学可塑化*改变签名值本身,利用签名方案的代数对称性,并产生一个仍然在同一密钥下有效的签名值。*结构性可塑化*保持签名值不变,并在签名未覆盖的区域编辑周围的容器。*编码可塑化*保持签名值和容器的逻辑内容不变,并将某些字段重新序列化为等效但非规范的字节编码。哪种技术适用取决于签名方案,如下详述并在表 1 中总结。 ### 3.1 数学可塑化 (ECDSA) ECDSA 的可塑性是该方案的经典属性[1](#bib.bib7),并且从比特币交易可塑性文献中为人熟知[22](#bib.bib8)。对于阶为 \(n\) 的曲线和签名 \((r, s)\),对 \((r, n-s)\) 是同一消息上同样有效的签名:验证恢复一个曲线点并仅测试其 \(x\) 坐标,而 \(s\) 和 \(n-s\) 产生的点关于 \(x\) 轴对称,因此共享它。该变换仅使用公共参数 \(n\);私钥从未涉及。 我们从 `gpgsig` 头部中的 OpenPGP 多精度整数恢复 \(s\),从标量的位长推断曲线(从而将其映射到 P-256、P-384 或 P-521 的阶),并重新发出 \(n-s\) 的整数。由于重新编码的标量可能占用与原始标量不同的字节数,因此会重新计算包含该标量的数据包的长度头部。由于结果签名真正有效,不仅被 GitHub 接受,也被严格的本地 `git verify-commit` 接受。 ### 3.2 结构性可塑化 (RSA 和 EdDSA) RSA 在代数意义上不可塑,而 EdDSA 被设计为完全排除方案级别的可塑性。对于这些方案,我们保持签名值不变,而是攻击 OpenPGP 容器。版本 4 签名包携带两个子包区域:一个*已哈希*区域(被签名覆盖)和一个*未哈希*区域(根据 RFC 4880 §5.2.3,“未受签名密码学保护,应仅包含建议性信息”[4](#bib.bib4))。符合规范的验证者仅将包体到已哈希子包末尾的部分折叠到其摘要中;未哈希区域被有意排除。 因此,我们在未哈希区域附加一个格式良好的子包:一个非关键的私有/实验类型 100 子包,根据 §5.2.3.1,符合规范的实现应忽略它。然后我们相应调整未哈希长度字段,由于包体变大,以长格式重新发出外部包头。由于修改完全落在签名区域之外,签名所证明的摘要不变,因此签名保持有效。因此,可塑化的提交被*两个*验证者(GitHub 和严格的本地 `git verify-commit`)都接受。有趣的是,RFC4880 依赖来允许建议性元数据的属性(即未哈希子包未被签名)正是允许在保持验证的同时重写提交哈希的原因。 ### 3.3 编码可塑化 (S/MIME) S/MIME 提交签名是一个 CMS SignedData 结构[9](#bib.bib5),以 ASN.1 编码并嵌入在 `gpgsig` 头部中。编码必须是 DER,即 BER 的“规范”子集:X.690 §10.1 强制每个长度字段使用最短的可接受形式[10](#bib.bib6)。长度 32 必须写为单字节 `0x20`,而不能是两字节的 BER 长形式 `0x81 0x20`,尽管 BER 解码器接受两者为等价。 我们在 CMS 信封内部元素上选择一个长度字段(具体不是最外层的 SEQUENCE 的值),并将其从规范的短形式重写为等效的长形式,将由此产生的尺寸变化向外传播通过每个包含的长度字段。该块不再是有效的 DER,但仍然是有效的 BER;签名内容和签名值均未触及,因此签名仍然验证通过。由于操纵发生在信封编码中而非签名中,它是*算法无关的*:无论 S/MIME 密钥是 RSA 还是 ECDSA,它都成功,我们已通过 RSA-2048、RSA-4096、ECDSA P-256 和 ECDSA P-384 签名者确认了这一点。 在这里,本地和服务器的行为出现分歧。强制执行 DER 的严格 CMS 解析器(例如 libksba,因此也包括 gpgsm)拒绝非规范长度,因此本地验证失败;GitHub 更宽松的解析器接受它,并将提交标记为“已验证”。同一个对象因此从两个名义上都实现 CMS 的验证者那里得到相反的判决。 表 1:每种方案的修改技术,以及严格的本地验证(OpenPGP 使用 `git verify-commit`,S/MIME 使用 `gpgsm`)与 GitHub 服务端验证器的结果。每种技术都保留了 tree,并产生了一个不同的提交哈希。只有 S/MIME 的编码修改会被本地捕获;三种 OpenPGP 路径即使严格的本地验证也能通过。结果截至撰写时是最新的 GitHub 状态。 ## 4 服务端接受与记录重复 将这些技术从本地奇观提升到供应链关注点的是,GitHub 的服务端验证器接受了所有它们。GitHub 在验证前不会规范化签名容器:它不对 CMS 信封强制执行 DER 严格性,不会剥离或规范化 OpenPGP 未哈希子包,并接受非规范 ECDSA 标量。每个被接受的提交随后被颁发一个独立的验证记录,GitHub 持久化该记录并以提交哈希为键;根据设计,此记录永远不会被重新评估,因此即使签名密钥被轮换或撤销,提交也会保留其“已验证”状态[7](#bib.bib3)。 一个单一的已签名逻辑提交可以由一个无界的字节不同哈希族来表示,每个哈希都被 GitHub 独立且持久地标记为“已验证”,其界面或 API 中没有任何内容表明它们编码了相同的内容。如果推送到两个分支,原始提交及其可塑化双胞胎在 GitHub 的比较视图中呈现为不相关的历史,显示“领先 1 个提交,落后 1 个提交”,尽管树和元数据字节相同。因此,已签名、已验证的提交哈希是特定签名内容的唯一句柄这一不变性在 GitHub 上不成立,并且通过相同的方法,在任何在验证签名前未对其编码进行规范化的 Git 平台上也不成立。 ## 5 影响 #### 基于哈希的阻止绕过。 通过哈希阻止或回滚特定恶意提交的事件响应和推送保护工具可以被静默绕过。一个阻止目标为 `hash(original)`;攻击者重新推送一个字节不同但内容相同且仍然有效的已签名提交,其哈希为 `hash(ghost) ≠ hash(original)`,该哈希不在阻止列表中。不需要更改元数据或树,替换在提交历史中不留痕迹。 #### 依赖锁定与供应链混淆。 锁定到提交哈希的系统(例如 Go 模块代理、Nix flake 锁定、GitHub Actions 引用、指定特定提交的 Dockerfile 等)依赖于假设哈希是给定签名者下内容的唯一句柄。可塑性使该假设失效:一个签名内容产生多个有效、“已验证”的哈希。我们认为 Nixpkgs 尤其暴露,因为它严重依赖不可变哈希来保证可重现性,而其贡献者几乎普遍使用 GPG 签名。一个被攻破或恶意的镜像还可以向克隆者提供与规范平台持有的不同但所有提交仍然验证通过的有效签名哈希链。这与 *trivy-action* 漏洞利用标签可变性破坏的信任类别相同[2](#bib.bib2);前者是通过重定向引用的方式,而这里是通过为相同内容生成一个替代的有效签名哈希。 #### 可重现构建与出处验证。 采用 Git 哈希作为可重现构建或工件出处真相基础的系统整体继承了该弱点:一个已签名、“已验证”的哈希不再是内容唯一承诺,
相似文章
新研究:经过“验证”的GitHub提交并非唯一
Jacob Ginesin的一篇新预印本揭示,经过验证的GitHub提交可以被篡改,产生多个具有不同哈希值的有效签名,这破坏了唯一性的假设,并影响了供应链安全。
@IntCyberDigest: 最新研究显示,你可以将任何已签名的 GitHub 提交复制为另一个看起来完全相同的提交,而无需作者私钥,从而创建一个具有相同树结构、相同元数据、有效签名和 GitHub“已验证”徽章的不同提交。
卡内基梅隆大学的最新研究表明,可以创建一个与任何现有已签名提交外观完全相同的不同已签名提交,且具有有效签名和 GitHub“已验证”徽章,这破坏了提交哈希作为加密指纹的独特性。
深入Ghostcommit:恶意PNG如何绕过AI代码审查者
Ghostcommit是一种新型供应链利用方式,它使用包含文本指令的恶意PNG图像来绕过AI代码审查者,导致开发者环境中的数据泄露。
GitLost:我们诱骗GitHub的AI代理泄露私有仓库
Noma Labs发现GitHub的Agentic Workflows中存在一个严重的提示注入漏洞,允许未经身份验证的攻击者通过在同一组织的公共仓库中发布精心构造的GitHub Issue,从私有仓库中窃取数据。
Megalodon:通过CI工作流大规模后门GitHub仓库
一项安全研究揭示了一种名为Megalodon的技术,通过利用CI工作流大规模后门GitHub仓库。