Security and Supply Chain
Topic 61

Commit Signing

Security

Git identity is unauthenticated by default. Anyone can run git config user.email you@example.com, author a commit under your name, and push it — Git never checks that the author field belongs to whoever made the commit. The author and committer fields are free text, nothing more.

Commit signing closes that gap. A GPG or SSH signature cryptographically binds a commit to a key you control, and GitHub shows a green Verified badge when the signature matches a key registered on the signer's account. The badge is the difference between "this field says it was you" and "this was signed by a key only you hold."

Why Identity Is Forgeable

The author name and email are recorded exactly as configured at commit time, with no verification against your account or any credential used to push. A commit pushed with your token can still carry someone else's name, and a commit with your name can be pushed by anyone. Signing is the only mechanism that produces actual proof of authorship rather than a self-asserted label.

GPG vs SSH Signing

GPG is the original signing mechanism: you generate a GPG key, configure Git to use it, and upload the public key to GitHub. It works everywhere but adds a separate keyring to manage. SSH signing, available since Git 2.34, reuses the SSH key you already have for pushing — you point gpg.format ssh at your key and skip the second keyring entirely.

For most people SSH signing is the lower-friction choice precisely because there is one fewer key to create, back up, and rotate. GPG remains worthwhile when you already operate a GPG identity or need its web-of-trust features outside GitHub.

The Verified Badge

GitHub validates a commit's signature against the public keys registered on the signing account. A match yields Verified; an Unverified marker means the signature failed, the key is not on any account, or the commit was not signed at all. The badge is checking the signature, not the spelling of the author field — a signed commit with an unregistered key still reads as Unverified.

Vigilant Mode

By default an unsigned commit simply shows no badge, which means a forged unsigned commit in your name looks identical to your ordinary work. Vigilant mode changes that: it marks every commit attributed to you that is not validly signed as Unverified, so a forgery stands out instead of blending in. It is what makes the absence of a signature meaningful.

Enforcing Signatures

Signing is advisory until you require it. Branch protection's "Require signed commits" rejects any push to the protected branch that contains an unsigned or unverifiable commit. That turns signing from a personal habit into a branch invariant — but only do it after the whole team has keys uploaded, or every push starts failing.

Common Mistakes
  • Treating the author email as proof of who wrote a commit — it is free text and trivially spoofable, so without a signature it proves nothing.
  • Signing with a key that was never uploaded to GitHub, so every commit shows Unverified despite being correctly signed.
  • Enabling "Require signed commits" on a branch before onboarding the team's keys — everyone's pushes are rejected until their keys are registered.
  • Leaving vigilant mode off, so a forged unsigned commit in your name is indistinguishable from your real commits in the UI.
  • Losing the signing key with no rotation or revocation plan, leaving future verification broken and old signatures unmanageable.
Best Practices
  • Use SSH signing on Git 2.34+ to reuse your existing SSH key and avoid managing a separate GPG keyring.
  • Upload the signing public key to your GitHub account so signatures actually verify and earn the Verified badge.
  • Enable vigilant mode so any unsigned commit attributed to you is flagged Unverified rather than blending in.
  • Require signed commits via branch protection on main, but only after every contributor has registered a key.
  • Set commit.gpgsign true so signing happens automatically on every commit instead of per-commit flags you will forget.
Comparable toolsGitLab GPG/SSH commit verification and a "Reject unsigned commits" push ruleSigstore gitsign keyless commit signing tied to an OIDC identityGitea instance-side signature verification and required-signing policies

Knowledge Check

Why is a commit's author email not proof of who wrote it?

  • The author field is free text set at commit time and is never verified against an account or credential, so anyone can set it to your address
  • The email is stored as a one-way hash, so it cannot be read back to identify the original author
  • Git verifies the author email against the credentials of the pushing token on every push, so any mismatch between the two is rejected outright
  • It actually is solid proof, as long as the commit was pushed over an authenticated SSH connection

A commit is correctly signed but still shows Unverified. What is the most likely cause?

  • The signing key is not registered on the signer's GitHub account, so GitHub cannot match the signature
  • The commit message exceeded the byte limit GitHub allows for verification, so the badge falls back to Unverified once the signed payload grows past the cap
  • SSH-signed commits can never reach Verified status; only GPG-signed ones can
  • The author and committer emails on the commit differ from each other, so GitHub treats the signature as belonging to neither identity

What does vigilant mode change?

  • It marks every unsigned or unverifiable commit attributed to you as Unverified, instead of showing no badge
  • It retroactively signs all of your past commits using your currently registered signing key
  • It blocks any unsigned push at the branch level, rejecting it before the commit lands
  • It rotates your signing key automatically on a fixed schedule to limit how long any leaked key stays usable

Why onboard team keys before enabling "Require signed commits" on a branch?

  • The rule rejects any push containing an unverifiable commit, so contributors without registered keys are locked out until they upload one
  • The rule retroactively deletes every existing unsigned commit from the branch history once enabled
  • Keys must be uploaded in a specific contributor order or signature verification breaks for the whole team
  • It is only a cosmetic badge setting that changes how commits look in the UI, so the order and timing of enabling it does not matter at all

You got correct