Branch Protection with Required Signatures in GitHub
How GitHub turns a simple checkbox into a cryptographic guarantee that every commit reaching a protected branch was really written by the person it claims to be from — and why some of the world's most security-conscious engineering teams refuse to merge anything without it.
Imagine a bank vault that only opens when the manager’s fingerprint is scanned — not when someone types the manager’s name into a form. That is, in essence, what “Required Signatures” does to a Git repository. A branch name and an author email in a commit are just text; anyone can type them. A cryptographic signature is a fingerprint that cannot be faked without the private key that only the real author holds. This article walks through everything a beginner needs to understand this feature completely: what a commit signature actually is, how GitHub verifies one, how it fits into the wider system of branch protection rules, why organizations like Google, Microsoft, and the Linux kernel project quietly rely on it, and the mistakes that trip up teams who turn it on without understanding what they just switched on.
1Core Concepts: What Is a Commit Signature?
Every time someone runs a commit in Git, a small text record is created: who made the change, when, what the message says, and a pointer to the exact state of every file at that moment. That record has an identity field — a name and an email address — but Git never checks whether the person typing the command actually owns that email address. Anyone with commit access, or anyone who can push to a fork, can set their Git configuration to say author: Linus Torvalds and Git will happily accept it. The commit history, on its own, is a claim, not a proof.
A digital signature closes that gap. It is a small block of cryptographic data, generated using a private key that never leaves the signer’s machine, and attached to the commit (or to a Git “tag”). Anyone holding the matching public key can mathematically verify that the signature could only have been produced by someone in possession of the private key, and that the commit’s content has not been altered by even a single byte since it was signed. Change one character in the commit message after signing, and the signature no longer matches — verification fails immediately.
Think of a wax seal on a medieval letter. Anyone could write “From the King” at the top of a letter, but only the King’s actual signet ring pressed into wax proves it. If someone tampered with the letter after sealing it, the wax would crack or not match the shape at all. A commit signature is a digital wax seal: it proves who really sent the message, and it proves nothing was changed after the seal was pressed.
GitHub supports three signing mechanisms today: GPG (GNU Privacy Guard, the traditional PGP-style key pair), SSH signatures (reusing the same SSH key pair many developers already use to push code), and S/MIME (X.509 certificate-based signing, more common in enterprises with existing certificate infrastructure). All three plug into the same GitHub feature: once a commit or tag carries a valid signature from a key GitHub has on file for that user, GitHub marks it Verified in the UI, with a small green badge next to the commit.
Signing a commit proves authorship and integrity. It has nothing to do with encryption — the commit content is still fully readable by anyone. Signing answers “who really wrote this, and has it been touched since,” not “who is allowed to read this.”
What “Required Signatures” Actually Requires
“Require signed commits,” the specific branch protection rule this article focuses on, is a switch a repository administrator can flip on a protected branch. Once enabled, GitHub’s server-side push logic rejects any push that contains a commit without a valid, verifiable signature attached. It does not matter how good the code is, how trusted the contributor normally is, or whether the pull request has ten approving reviews — an unsigned commit simply cannot land on that branch. This is enforced at the Git protocol level during the push, not as a cosmetic warning after the fact.
Proves Who
Confirms the commit came from someone holding a specific private key linked to a verified GitHub account.
Proves Untouched
Confirms the commit’s exact content, at the byte level, has not been altered since the signature was created.
Proves It Can’t Be Denied
Because only the signer’s private key could produce the signature, the signer cannot credibly claim “that wasn’t me.”
Blocks at the Gate
GitHub refuses the push outright if any commit in it is unsigned, rather than merely flagging it after merge.
2Why This Exists: The Problem It Solves
To understand why a whole cryptographic system was built around something as small as a commit, it helps to look at what goes wrong without it. Git’s design makes the author and committer fields on every commit fully user-editable plain text, set locally with a simple configuration command. Nothing stops a person from configuring their local Git identity to impersonate a well-known maintainer, a teammate, or even a bot account, and then pushing a commit that appears, in the history and in most tools, to have come from that person.
This is not a theoretical concern. Security researchers have repeatedly demonstrated commit spoofing as a proof of concept, and it has shown up as a real attack vector: an attacker with access to a shared build system, or a compromised CI credential, can push a malicious commit that is falsely attributed to a trusted senior engineer, making the change look far more credible during a quick review and slowing down detection. In a large open-source project accepting contributions from thousands of pseudonymous accounts, there is no inherent guarantee that the “author” name on a patch corresponds to a real, consistent identity at all.
Required signatures exist to remove this blind spot for the specific branches that matter most — typically main, release branches, and anything that feeds a production deployment pipeline. It converts “the commit says it’s from Priya” into “cryptography proves this could only have come from a private key Priya controls, and it has not been altered since.”
The Supply-Chain Angle
This problem became sharply more urgent industry-wide following a string of software supply-chain incidents where attackers inserted malicious code deep inside dependency chains, sometimes by compromising a maintainer’s account or build infrastructure rather than breaking cryptography directly. Frameworks like SLSA (Supply-chain Levels for Software Artifacts) and the broader push toward software provenance explicitly call out commit signing as a foundational control: if you cannot cryptographically prove who authored a change, you cannot build a trustworthy chain of custody for the software built from it.
GPG · SSH · S/MIME
UNSIGNED PUSH TEXT
VERIFIED COMMITS
3Architecture & Components
1. The Key Pair
Every signer generates (or already has) an asymmetric key pair: a private key kept secret on their own machine, and a public key that is safe to share widely. For GPG or S/MIME this is a dedicated signing key; for SSH signing, many developers reuse the exact same key pair they already use to authenticate pushes over SSH, which is one reason SSH signing has become popular — there is often nothing new to generate.
2. The Local Git Client
When signing is turned on locally, the Git client (or the code editor wrapping it) automatically runs the signing operation as part of creating each commit, attaching the resulting signature block to the commit object before it is ever transmitted anywhere.
3. GitHub’s Public Key Store
Each GitHub account has a settings page where the user uploads their public key (GPG, SSH signing key, or an S/MIME certificate). GitHub stores this and associates it with that account’s identity. This is the “known fingerprint on file” that later verification checks against.
4. The Signature Verifier
On every push, GitHub’s backend runs each commit’s signature through standard cryptographic verification, checking that the signature mathematically matches the commit content and that it was produced by a private key whose public counterpart is on file and trusted for that identity.
5. The Branch Protection Rule Engine
This is the enforcement layer. Branch protection rules are evaluated per push, per branch. “Require signed commits” is one of many possible conditions (alongside required reviews, required status checks, and required linear history) that all must pass before a push is accepted onto that specific branch.
What Counts as “Trusted” for Verification
A signature only earns the green “Verified” badge if the key is uploaded to a GitHub account, the key has not expired or been revoked, and (for GPG) the email in the commit matches an email verified on that GitHub account. A perfectly valid mathematical signature from an unknown or unlinked key still shows as unverified in GitHub’s UI, though the branch protection check specifically cares about whether the commit is signed with a recognized, trusted key.
“Walk me through what happens, end to end, when a developer pushes a signed commit to a branch that requires signatures.” A strong answer traces exactly the five components above, in order, and explains where the push would be rejected if any one of them failed.
4Internal Working: How Verification Actually Happens
At the heart of every signature scheme GitHub supports is the same mathematical idea: asymmetric cryptography, where two keys are generated together as a pair, and an operation done with one key can only be undone or verified with the other. For signing specifically, the private key produces a signature over the commit’s content, and the public key is used to check that signature — but the public key alone cannot forge a new valid signature.
When a commit is signed, the signing tool does not encrypt the whole commit. Instead, it computes a cryptographic hash — a fixed-length fingerprint of the commit’s exact content (tree pointer, parent commit, author, committer, timestamp, and message) — and then signs that hash with the private key. Because a hash function is designed so that even a one-character change produces a completely different fingerprint, the signature effectively locks in every detail of the commit at once.
Commit Object Assembled
Git builds the commit’s raw content: tree hash, parent hash, author/committer info, timestamp, message.
Content Is Hashed
A cryptographic hash function reduces that content to a short, unique fingerprint.
Hash Is Signed
The signing tool uses the private key to produce a signature over that fingerprint.
Signature Attached
The signature block is embedded inside the commit object itself, travelling with it wherever it goes.
GitHub Recomputes the Hash
On receipt, GitHub independently recomputes the hash of the commit content it received.
Public Key Checks the Signature
Using the sender’s public key on file, GitHub verifies that the signature matches both the recomputed hash and the claimed signer.
Verdict Recorded
The commit is marked Verified, Unverified, or Partially Verified, and the branch protection engine reads that verdict.
Think of it like a tamper-evident seal on a medicine bottle. The manufacturer (private key holder) presses a seal onto the exact bottle they filled. A pharmacist (GitHub) checks the seal against the manufacturer’s known pattern (public key). If even the cap has been twisted and replaced, the seal will show it. The pharmacist never needs to trust the label on the bottle — the seal itself carries the proof.
Why Changing History Breaks Signatures
Because the signature covers the commit’s parent pointer as well, any attempt to rewrite history — even something as small as amending an old commit message deep in the chain — produces a completely new hash for that commit and every commit built on top of it, invalidating every signature downstream unless each one is re-signed. This is precisely why signed commit history is considered strong evidence against silent, undetected tampering: an attacker cannot quietly edit an old commit without either breaking every signature after it or needing access to every relevant private key to re-sign the whole chain.
A signed commit does not mean the code inside it is safe, bug-free, or well-reviewed. It only proves who wrote it and that it hasn’t changed since. Signing and code review solve two completely different problems and are meant to be layered together, not substituted for one another.
5Data Flow & Lifecycle of a Signed Commit
Tracing a single commit from a developer’s keyboard to a protected branch makes the whole system easier to hold in your head as one continuous flow rather than a list of disconnected facts.
| Stage | What Happens | Where Rejection Can Occur |
|---|---|---|
| Local commit creation | Git client signs the commit using the configured private key as part of the commit operation. | Client fails locally if no signing key is configured and signing is required by local settings. |
| Local commit stored | Signature is embedded in the commit object in the local repository history. | — |
| git push initiated | Commit objects, including their signatures, are transmitted to GitHub’s servers. | Network/auth failures unrelated to signing. |
| Server-side pre-receive check | GitHub evaluates all active branch protection rules for the destination branch before accepting the push. | Push rejected here if “Require signed commits” is on and any commit lacks a valid signature. |
| Signature verification | Each commit’s signature is checked against public keys on file for the claimed author. | Rejected if the key is unknown, expired, revoked, or the signature doesn’t match. |
| Other branch rules evaluated | Required reviews, required status checks, linear history, etc. are checked independently. | Rejected if any other enabled rule fails, even with a perfectly valid signature. |
| Commit accepted onto branch | The branch pointer moves forward; the commit becomes part of permanent, protected history. | — |
| UI badge rendered | GitHub displays the green “Verified” badge on the commit in the web interface and API responses. | — |
Pull Requests Add a Layer
Most teams do not push directly to a protected branch; they open a pull request from a feature branch, which is merged later. Required signatures still applies at the moment of merge (or at the moment individual commits land, depending on the merge strategy chosen): a “squash and merge” typically produces one new merge commit that itself must be signed by whoever or whatever performs the merge, while “rebase and merge” or a fast-forward merge preserves each original commit’s own individual signature. This distinction surprises many teams the first time they turn the setting on, because it changes which actor’s key needs to be trusted at merge time.
If your team squash-merges pull requests, the person or bot clicking “Merge” in the GitHub UI is effectively the one whose signature needs to satisfy the rule for the resulting commit — not necessarily the original contributor. Many teams solve this by having GitHub’s own merge operation produce a signature GitHub itself vouches for, or by requiring merges to happen via a signed, automated process.
6Advantages, Disadvantages & Trade-offs
Advantages
- Cryptographically proves authorship, closing the door on simple identity spoofing via forged author fields.
- Detects any post-signing tampering with commit content, down to a single character.
- Strengthens software supply-chain assurances, supporting frameworks like SLSA and audit requirements in regulated industries.
- Creates a durable, non-repudiable audit trail useful in security incident investigations.
- Encourages good key hygiene practices across an engineering organization as a side effect.
Disadvantages & Costs
- Adds real friction: every contributor must generate, upload, and maintain a signing key before they can contribute at all.
- Breaks automation and bots that push commits unless they are also given a properly configured signing identity.
- Complicates squash/rebase workflows, since merge commits need their own valid signer.
- A lost or expired private key can lock a contributor out of pushing until keys are regenerated and re-uploaded.
- Signing proves identity, not code quality or security — teams can develop false confidence that “signed” means “safe.”
The Central Trade-off
Required signatures trades short-term contributor convenience for long-term, provable integrity. For a small internal tool with two trusted maintainers, this trade-off is rarely worth the operational overhead. For a widely-forked open source project, a regulated financial system, or any codebase feeding a software supply chain that other organizations depend on, the trade-off usually favors turning it on, because the cost of an undetected forged commit vastly outweighs the friction of key management.
ASSURANCE GAINED
FRICTION ADDED
PERFORMANCE IMPACT
7Security
Required signatures is, at its root, a security control, so it is worth examining what it defends against precisely — and, just as importantly, what it does not.
Author Impersonation
Stops an attacker from pushing a commit that falsely appears to come from a trusted contributor’s name and email.
Silent History Tampering
Any retroactive edit to a signed commit invalidates its signature, making silent rewrites detectable.
Unauthenticated Automation
Forces even bots and CI pipelines to authenticate with a real, traceable signing identity before contributing code.
A Compromised Signing Key
If an attacker steals a legitimate private key, they can produce fully valid, “Verified” signatures indistinguishable from the real owner’s.
Malicious Code From a Trusted Author
A verified signature says nothing about whether the code itself is safe, correct, or free of vulnerabilities.
A Compromised GitHub Account
If an account is taken over and a new signing key is uploaded to it by the attacker, subsequent forged commits verify as “Verified” too.
The entire trust model collapses to one question: is the private key actually private? Teams that require signed commits but store private keys carelessly (shared build servers, unencrypted disks, keys committed to other repositories) get almost none of the promised security benefit while paying the full operational cost.
Layering With Other Controls
Because of these gaps, mature security programs never treat required signatures as a complete solution on its own. It is typically layered with required pull request reviews (a second human must approve the change), required status checks (automated tests and security scans must pass), required linear history (no confusing merge topology to hide changes in), and, increasingly, hardware-backed signing keys (stored on a physical security key rather than a laptop disk) for the highest-privilege contributors such as release managers.
8Monitoring, Logging & Metrics
Turning a control on is only half the job; a security-conscious team also needs visibility into how it is behaving day to day.
Verified vs. Unverified Ratio
GitHub’s API and UI expose the verification status of every commit, letting teams audit what proportion of history is properly signed over time.
Audit Log Entries
Organization-level audit logs (on GitHub Enterprise plans) record when a push is blocked by a branch protection rule, including which rule fired.
Expiry & Revocation Tracking
Signing keys typically carry expiration dates; monitoring upcoming expirations avoids sudden, surprise push failures for legitimate contributors.
CI Signing Failures
Automated pipelines that push commits need their own monitored signing setup; a silent failure here can quietly halt all automated merges.
Organizations at scale often pipe this data into their existing security dashboards, alongside secret-scanning and dependency-vulnerability metrics, treating “percentage of commits verified on protected branches” as a tracked health indicator, not a one-time setup task.
“How would you notice if required signatures on your main branch silently stopped being effective?” A strong answer mentions periodically auditing verification status via the API, watching the audit log for unexpected rule bypasses (such as admin overrides), and alerting on CI signing failures rather than assuming the control “just works” forever.
9Design Patterns & Anti-Patterns
Pattern
Tiered enforcement: apply required signatures only to main and release branches, while leaving feature branches unrestricted, so contributors sign only at the point where code becomes permanent and trusted history.
Why It Works
It concentrates the security guarantee exactly where it matters — the branches that feed deployments — without forcing every experimental commit on a throwaway branch through the same overhead.
Pattern
Dedicated machine identities for automation: CI/CD systems and bots get their own GitHub account or GitHub App identity with its own signing key, rather than sharing a human’s key or being exempted from the rule.
Why It Works
Every entry in history stays individually attributable and verifiable, and a compromised bot key can be revoked without touching any human contributor’s identity.
Pattern
Exempting administrators from the rule “for convenience,” so repository admins can push unsigned commits directly to a protected branch while everyone else is blocked.
Why It Fails
Admin accounts are typically the highest-value target for attackers precisely because of privileges like this. An admin bypass quietly reintroduces the exact spoofing risk the whole control was built to remove, and it is the path an attacker who compromises an admin account will use first.
Pattern
Storing a shared team signing key on a common build server or in a widely-accessible secrets store, used by multiple humans or systems to “make signing easier.”
Why It Fails
Once a key is shared, individual non-repudiation is gone — you can no longer prove which specific person or system actually authored a given commit, which was the entire point of the control.
10Best Practices & Common Mistakes
Roll Out Gradually
Enable required signatures on a low-traffic branch first, let the team get comfortable with key setup, then extend it to main.
Document Key Setup Once, Centrally
A single clear onboarding guide for generating and uploading a signing key removes most of the friction contributors otherwise hit individually.
Set Key Expiry Reminders
Track expiration dates for organization signing keys so pushes don’t fail unexpectedly the day a key lapses.
Use Hardware-Backed Keys for High Privilege
Release managers and admins should sign with keys stored on hardware security devices, not on a laptop’s disk.
Common Mistakes
Turning It On Without Warning the Team
Pushes suddenly start failing for everyone who hasn’t configured a key, with confusing errors and no context.
Forgetting About Bots
CI pipelines that push automated commits (version bumps, changelogs) start failing silently the moment the rule is enabled.
Ignoring Merge Strategy Interaction
Teams assume the original contributor’s signature “carries through” a squash merge, when in fact the merge commit needs its own valid signer.
Treating “Verified” as “Safe”
Reviewers relax scrutiny on a change simply because it carries a green Verified badge, forgetting signing says nothing about code quality.
No Key Revocation Process
When a contributor leaves or a laptop is lost, there is no clear runbook for revoking that key promptly, leaving a window of risk.
11Real-World & Industry Examples
The Linux Kernel
One of the earliest and most influential adopters of commit signing culture, the Linux kernel project has long relied on GPG-signed tags and a strict chain-of-trust model for maintainers, given the scale of contributions and the criticality of the software running on billions of devices worldwide.
Large Cloud Providers’ Internal Repositories
Major cloud and enterprise software vendors commonly require signed commits on release branches feeding production infrastructure, treating it as a baseline software supply-chain control alongside code review and automated security scanning.
Regulated Financial and Healthcare Software
Organizations subject to strict audit and compliance regimes often adopt required signatures specifically to produce a defensible, cryptographically-backed audit trail of exactly who introduced each change to production code, satisfying regulatory change-control requirements.
Open Source Foundations
Projects under foundations like the Cloud Native Computing Foundation increasingly reference commit signing as part of their security maturity guidelines (such as the OpenSSF Scorecard project), which many downstream consumers now check before depending on a package.
12Frequently Asked Questions
13Summary & Key Takeaways
Key Takeaways
- Required signatures is a GitHub branch protection rule that blocks any push containing an unsigned commit from landing on a protected branch.
- A commit signature proves two things cryptographically: who really authored the commit, and that its content has not been altered since signing.
- GitHub supports GPG, SSH, and S/MIME signing, all converging on the same green “Verified” badge and the same branch protection check.
- The system relies on a public key stored on the signer’s GitHub account; verification recomputes the commit’s hash and checks it against that key.
- It exists to close the gap left by Git’s default trust model, where author names and emails are simply user-editable text with no built-in proof.
- It is one layer of defense, not a complete solution — it says nothing about code quality and provides no protection if a private key or GitHub account is itself compromised.
- Successful adoption depends far more on disciplined key management — onboarding, expiry tracking, revocation, and hardware-backed keys for high-privilege roles — than on the feature toggle itself.