Should You Sign Git Commits?
Maybe, but probably for a different reason than you think
TLDR; If you’re using GitHub, signing your commits is slightly better than not signing them. But probably not for the reason you think. The answer might shock you!
Let’s start with what most people thinking signing your commits does:
Most people seem to think commit signing is an additional protection for your GitHub account, or just do it to get the green checkmark so your commits show up as “Verified” without even thinking that much about it. But that’s not actually what commit signing does, or protects against. Let’s explain why. I’ll start with an overview of how it all works.
How does commit signing work?
Again, I’m focusing on GitHub. The basic flow is pretty simple to get started with. The full documentation from GitHub is here, but at a high level, you:
- Generate a GPG keypair locally with something like:
- Log into your GitHub account, and upload the public key
- Sign your commit with:
git commit -S
git pushand watch the green checkmark appear!
When you push, GitHub will examine the signature and the
Author: email address contained in the commit, and check the signature against the public keys present in the account for that email address. If the signature matches, you get a green check! The flow looks roughly like:
The problem is that most people stop here, without using any form of PKI beyond the GitHub account. When you get a new laptop, you add a new public key. No one uses PGP’s web-of-trust, so anyone checking the signatures just has to rely on the public keys uploaded to the GitHub account, and that account is just protected by the same password you used to push.
Anyone that compromised your GitHub account and gained access to push commits would also have access to just upload a new public key. Unless you store and distribute your public keys using some other system (in a separate trust domain from GitHub), you don’t really gain any protections from having your account compromised here. An attacker would have to go through a few extra steps, but they’re just point and click in the UI.
So Why Do It At All?
I’ll outline a few possible reasons here, none of which are compelling enough to make me personally start signing.
Web Of Trust
If you’re actually using Web-Of-Trust (for some reason…) or are using some third-party service (like KeyBase) to manage your identity, you might get some protections against a GitHub account compromise. I think it would be better to just configure MFA using a hardware token and not worry about this at all, but I digress.
You might gain some protections against a GitHub infrastructure compromise itself, assuming an attacker had access to the repository itself but not access to the list of public keys. If you’re worried about this, I’d recommend running your own SCM system in as secure of an environment as makes you comfortable instead.
Here’s the only one that might convince me to start signing commits someday. The high level issue is that the author of a commit is whoever shows up in the
Author: field, which can be any random string. GItHub manages permissions on a repo using a GitHub account, which may or may not use the same email addresses in a commit. Anyone can push commits to their own repositories with anyone else’s email address.
This came up quite publicly in early 2021 when a group of GitHub users started protesting the takedown of Youtube-dl, and “forged” a bunch of commits, misattributing them to Nat Friedman, the CEO of GitHub. GitHub even happily shows the profile photo from an account with the forged email address, adding to the confusion. GitHub has since made some improvements to make this a little easier to notice, but it’s still easy enough to play a joke on someone.
Why does GitHub allow you to push commits from other people? It’s non obvious at first, but this is actually core to the Git protocol. Any time you sync a remote repo to your fork, you’re pushing commits from other people. Anytime you rebase a branch, or cherry-pick something, you’re pushing commits from other people.
Git commit signing is the only in-band way to protect against this. If you sign all of your commits, distribute your public keys, and tell anyone that cares to check the signatures, you can protect against someone impersonating you.
Unless you’re really worried about the impersonation angle, I don’t really recommend signing git commits for most people. Take care of your basic account hygiene with MFA and hardware tokens, and that’s plenty for most people. If you run your own Git infrastructure, signed pushes can help protect against infrastructure and account compromises as well.
PGP isn’t something I can really recommend at all, but unfortunately it is kind of the only option for signing git commits today. If you must use commit signatures for some reason, figure out an out-of-band (off of GitHub) place to distribute your public keys.
The Linux kernel uses a separate repository and webpage served over HTTPS to look up the current known public keys for developers. Some people get away fine with KeyBase. If you’ve actually managed to figure out the web-of-trust, more power to you!
There’s some work going on to improve this situation today. Dave Huseby has been working on decoupling PGP from the Git client, to allow for more flexible signature schemes and alternative PKIs in the TrustFrame project. There’s a git-did spec for decentralized identifiers if that’s your thing. In the Sigstore project, I’ve been working on some fun signing mechanisms using ephemeral keys and transparency logs.
I got nerd-sniped into writing this today by Christian Rebischke, but I’ve been thinking about this and explaining it to people for awhile. Thanks for the prompt!