Git Commit Hashes Are Not Unique: New Research Uncovers 'Hash Chain Malleability'
New research reveals that a signed Git commit's hash is not the immutable, unique identifier widely assumed. An attacker, without the signing key, can generate a second, 'Verified' commit with identical content, author, and date, but a different hash. This 'hash chain malleability' poses risks to systems relying on commit hashes for blocking, deduplication, and provenance.
A fundamental assumption in software development β that a signed **Git** commit's hash serves as a uniquely identifying, immutable fingerprint for its contents β has been challenged by recent research. **Jacob Ginesin**, a PhD student at **Carnegie Mellon University** and a cryptographic auditor at **Cure53**, has demonstrated that an attacker can create multiple validly signed commits with identical content, author, and date, yet distinct hashes.
Crucially, platforms like **GitHub** will still stamp these malleated commits as "Verified," potentially undermining security measures that rely on blocking or tracking specific commit hashes.
### The Malleability Problem
The core issue, termed "**hash chain malleability**" by Ginesin, stems from signature malleability. A commit's hash is computed over all its internal data, including the raw bytes of its signature. Many digital signature schemes allow for a signature to be rewritten into a different, yet still valid, form. This alteration of the signature bytes changes the commit's overall hash without affecting the actual code or files it contains.
This means that if a bad commit is blocked by its hash, an attacker can simply re-push the same malicious content under a new, still-verified hash that bypasses the blocklist. Systems for deduplication, provenance logs, and reproducible builds that key on the hash are similarly vulnerable.
It's important to clarify that this is not a hash collision, nor does it break **SHA-1** or **SHA-256**. Instead, it highlights that a single commit can be represented in multiple valid ways, each yielding a different hash.
### Three Routes to Malleability
Ginesin's research identifies three primary methods to achieve this, covering all **GPG** schemes verified by GitHub, plus **S/MIME**:
* **ECDSA keys:** Exploiting a classic piece of elliptic-curve algebra, the `s` value in the signature can be flipped (`n - s`). Both forms are valid, passing local `git verify-commit` checks and earning a GitHub badge.
* **RSA and EdDSA keys:** An extra, ignored field can be added to the signature's "unhashed" section. The signature remains valid, but the commit's bytes and hash change. Both local checks and GitHub accept this.
* **S/MIME (X.509) keys:** A length field in the signature's **DER** structure can be rewritten into a longer, non-standard form. While a strict local check (via `gpgsm`) may reject it, GitHub still marks it "Verified."
These methods are enabled because GitHub does not normalize signatures before verification, accepting non-canonical values and additional fields that alter the hash. Furthermore, GitHub files a "Verified" record against each commit hash and does not re-check it, meaning a commit can remain "Verified" even if its signing key is revoked.
### Historical Parallels and Impact
The concept of signature malleability is not new. **Bitcoin** famously grappled with **ECDSA** symmetry years ago, leading to fixes like accepting only the "low-S" form and later moving signatures out of the transaction ID with **SegWit**. The proposed solution for Git echoes this: canonicalize the encoding before trusting the hash.
This research also connects to recent **GitHub Actions** tag hijacks, such as the `tj-actions/changed-files` and `trivy-action` attacks. While the advice to pin to a full commit hash (rather than a movable tag) still holds, Ginesin's findings add a layer of caution. In previous attacks, malicious commits often lacked valid signatures. This new research warns against relying solely on a valid signature as a guarantee of a commit's unique identity.
### Who Needs to Act?
Developers who pin **GitHub Actions** or modules to a full commit hash are still protected, as the pinned hash will consistently fetch the correct code. The responsibility for addressing this flaw lies primarily with Git forges like GitHub. The paper suggests they should canonicalize signatures before trusting them.
Tooling that blocks, deduplicates, or records provenance by commit hash should also adapt, verifying and canonicalizing signatures first rather than blindly trusting the raw hash of a signed object. Systems that also pin an independent hash of the fetched files, such as **Nix's** fixed-output derivations, offer an additional layer of protection, unlike those that rely solely on a verified commit hash.
Ginesin reported the issue to **GNU** and Git in January, and to GitHub in March. As of the paper's publication, neither Git nor any forge had addressed the issue. The S/MIME case, where GitHub accepts signatures that a strict local check rejects, is highlighted as an obvious starting point for a fix.
