Git repositories store valuable source code and are used to build applications that work with sensitive data. If an attacker was able to compromise a GitHub account with a vulnerable repository, they could send malicious commits directly to production. Signed commits help ensure that doesn’t happen.
What are signed commitments?
Signed commits involve adding a digital signature to your commits, using a private cryptographic key, usually GPG, although SSH or X.509 is also supported. Once created, you need to add the signing key to both your GitHub profile and your local Git client.
Signed commits provide an extra layer of security by ensuring that the commit hasn’t been tampered with, and more importantly, that it originates from a trusted source that owns the GPG key and has the authority to access the GitHub account.
To push commits to a repository that only allows signed commits, the attacker must be able to compromise the victim’s private GPG key, which typically means gaining access to their entire computer, not just their GitHub account. Since this is a fairly rare and difficult attack vector, signed commits provide a great way to verify that the commits are from who they say they are.
Setting a new GPG key
First, you’ll need to create a new GPG key used for signing, and then you’ll need to tell both your Git client and GitHub about it.
To generate the key, you will need the
gpg command installed on your system. This should be there by default, but if it isn’t you can get it from your package manager. Then you can generate a new key:
You can press Enter for most prompts, but you must enter a passphrase and you must enter your GitHub email address. If you want your address to be private, you can use the “no reply” address for your GitHub account.
Then list the keys and export the public key block for the key ID you just created:
gpg --list-secret-keys gpg --armor --export (keyID)
Next, we’ll add it to your GitHub account. From your user settings, click on “SSH and GPG Keys” and add a new GPG key. You can also turn on watchdog mode here, which will mark commits to GitHub that don’t use these keys as unverified.
Set a name and paste the public key block you exported with
The next thing is to tell your local Git client about your key. If you use a GUI Git client like GitKraken, you might be able to just import it, but otherwise you’ll have to do it from the command line.
Since this is bound to your user, you’ll probably want to set it as a global setting, but you can also use repository level settings. Place
user.signingkey to the GPG key ID you used to export.
git config --global user.signingkey (keyID)
Then you’ll want to tell Git to sign all commits by default. This can be set for individual repositories if you wish:
git config --global commit.gpgsign true
Otherwise, you can use the
-S flag to manually sign commits.
Enforce signed commits with Branch Protection
Branch protection rules place restrictions and guidelines on specific branches of your repository. While they are commonly used to enforce a specific pull request and merge workflow, and restrict access to release branches, they can also be configured to only accept signed commits.
Only accepting signed commits provides an additional layer of security and forces each contributor to have a GPG key associated with their account.
It’s easy to create a new branch protection rule from the “Branches” tab in your repository settings. You will need to set the filter to “*” to include all branches.
Then select “Require Signed Confirmations”.
From now on, all commits sent to all branches of this repository must contain GPG signing signatures.