github

How to Spoof Any User on Github…and What to Do to Prevent It

Why GitHub Commit Signing is Important
How to Spoof Any User on Github…and What to Do to Prevent It
Matt Calhoun
Published October 8, 2020

Here at Gruntwork, we want to ensure that no one introduces malicious code into our GitHub repositories and realized the native GitHub Branch Protection is missing some features; not the least of which is the ability to notify us when Branch Protection is turned off! To this end, I was recently working on building an internal code scanning tool that ensures every commit in our GitHub repos has been (a) added via a Pull Request and (b) that at least one person who didn’t write the code has reviewed and approved the Pull Request. It was during this work that I discovered it’s actually pretty easy to impersonate another user when committing code to GitHub with the git CLI!

Let’s take a look at how this is possible:

I’m Matt and my co-worker here at Gruntwork is Jim. Take a look at how easy it is to commit some code to GitHub pretending to be Jim:

$ git config user.name "Jim Brikman"
$ git config user.email "jims-email@gruntwork.io"
$ touch badcode.js
$ git add .
$ git commit -m 'add some bad code as jim'
$ git push

Now, if we head over to the GitHub UI, we can see a new commit that appears to have been added by Jim!

Signed Commits to the Rescue

Yikes, that’s not good! I just committed code as Jim! So how can this be possible? It turns out that, by default, git, and therefore GitHub, doesn’t validate the author of commits. So as long as you have write access to the repository, you can push code as any valid repo user. Luckily there’s a solution to this problem…signed commits!

If you cryptographically sign each commit with a key that only you have access to, GitHub will verify your signature and mark your commits as Verified when they appear in the GitHub Web UI.

Any commits you make directly through the GitHub Web UI editor are automatically signed, so let’s look at how we can set up our local machine to sign our git CLI commits by default, so they show up as verified on GitHub.

Please note that these steps apply specifically to MacOS and can easily be adapted on other *nix platforms, but the steps to set this up on Windows, I suspect, will be much different and is not covered here.

  1. Install packages
$ brew install gnupg pinentry
  1. Generate and list your GPG Key
$ gpg --gen-key
Real name: Matt Calhoun
Email address: myemail@gruntwork.io
You selected this USER-ID:
"Matt Calhoun <myemail@gruntwork.io>"
Change (N)ame, (E)mail, or (O)kay/(Q)uit? O [Enter]
┌──────────────────────────────────────────────────────┐
│ Please enter the passphrase to                       │
│ protect your new key                                 │
│                                                      │
│ Passphrase: ________________________________________ │
│                                                      │
│       <OK>                              <Cancel>     │                                                           └──────────────────────────────────────────────────────┘
$ gpg --list-secret-keys --keyid-format LONG

You should see output that looks like this:

/Users/matt/.gnupg/pubring.kbx
------------------------------
sec   rsa2048/FB3F3623BCF1391C 2020-10-05 [SC] [expires: 2022-10-05]

NOTE: FB3F3623BCF1391C is the unique id of the key I generated on my machine. Yours will be different.

  1. Add Your Additional Email Addresses

Follow the procedure below to add any additional email addresses you use with GitHub to your key:

$ gpg --edit-key FB3F3623BCF1391C
$ gpg> adduid
Real Name: Matt Calhoun
Email address: myemailaddress@mypersonaldomain.com
Comment: GitHub key
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O [Enter]
Q [Enter]
  1. Upload Your Key to GitHub
$ gpg --armor --export FB3F3623BCF1391C

You’ll see output similar to this:

-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=uoVP
-----END PGP PUBLIC KEY BLOCK-----

Now, browse to the SSH and GPG keys section of GitHub’s settings page, click the New GPG Key button and paste the contents of the key (including the BEGIN and END lines) into the box and click the Add GPG Key button.

5. Configure GPG Agent

In your shell’s init script (.bash_profile or .zshrc), add the following to ensure the GPG Agent is running on your machine:

# This check to make sure the GPG Agent is running and if not, starts it
if [[ -f "~/.gnupg/.gpg-agent-info" && -n "$(pgrep gpg-agent)" ]]; then
source ~/.gnupg/.gpg-agent-info
export GPG_AGENT_INFO
else
eval $(eval $(gpg-agent --daemon --options ~/.gnupg/gpg-agent.conf))
fi

Now let’s add a couple of new config files:

~/.gnupg/gpg-agent.conf

# Allows gpg-agent to use the OSX keychain to store the gpg key's
# passphrase so you don't have to type it each time you commit to
# git
pinentry-program /usr/local/bin/pinentry-mac

~/.gnupg/gpg.conf

use-agent
batch
no-tty

Finally, let’s restart the gpg-agent for our new config to take effect:

$ gpg-connect-agent reloadagent /bye

6. Setup Git to Sign Commits

Finally, let’s tell git to sign all of our commits with our gpg key. Switch into any directory that is a git repo on your machine and run the following commands:

$ git config user.signingkey FB3F3623BCF1391C
$ git config commit.gpgsign true

OK, let’s test it out! Start a new terminal session and then run the following:

$ touch goodcode.js
$ git add .
$ git commit -m 'good code as matt'
$ git push

Now, if we take a look at the GitHub UI, we can see the new commit is marked as Verified!

Next Steps: Protected Branches

Now, you can see in the GitHub UI which commits are Verified and which are not so you can have confidence who the author of each commit really is. But, it is a manual process to scan through the list of commits in order to ensure each one is signed. If you want to take your code protection to the next level, you can enable Github’s Branch Protection to only allow commits that are signed.

Let’s take a look at how to do this:

  1. Browse to GitHub’s Branch Protection settings for your repo (https://github.com/YOUR-ORG/YOUR-REPO/settings/branches)
  2. Add a new rule for your default branch (master) by clicking the Add rule button
  3. Click the “Require signed commits” option along with the other options that are applicable to your organization and click Save changes

And that’s it; GitHub will now only allow signed commits into your default branch.

A Gruntwork, we are now using a combination of GitHub Branch Protection, Signed Commits, the GitHub Checks API, and a custom code scanner to ensure all code that enters our repos has been reviewed and approved by someone who wasn’t involved in writing the code. If you’d like more info on our setup, feel free to email us at support@gruntwork.io.