DEV Community

Michael Crump
Michael Crump

Posted on • Edited on

Remove passwords committed to GitHub on accident

BONUS - You can also watch me talk about this on my Twitch Stream

Remove passwords committed to GitHub on accident

Writing code day after day means secrets, connection strings and more get added to your code accidentally. And if you are like me, they get committed to your GitHub repo and then you have to live in shame. :) In this post, I'll walk you through removing secrets from a GitHub repo that you've already committed the secret to.

Part 1 - Initial setup:

Scenario: You have committed a password with the value of qph@}uC,7cGLBdsX to your GitHub repo. This password should be confidential and not stored in the code.

How do you fix it?

  • Ensure you have the repo on your local disk or clone a fresh copy with HTTPS or SSH. I'll use SSH git clone git@github.com:mbcrump/crumpbot.git as a sample.
  • Clone a copy of your repo that has the secret stored using the mirror option, like the following git clone --mirror git@github.com:mbcrump/crumpbot.git.
  • You'll now have a BARE repo. CD into it with cd crumpbot.git and run ls -l to list out the contents on macOS or dir on Windows.

Below is an example of my repo.

[mbcrump@Michaels-MBP-3]:[~/Documents/code]$ cd crumpbot.git
[mbcrump@Michaels-MBP-3]:[~/Documents/code/crumpbot.git] (BARE:master)$ ls -l
total 32
-rw-r--r--   1 mbcrump  staff   23 Dec  1 19:47 HEAD
-rw-r--r--   1 mbcrump  staff  211 Dec  1 19:47 config
-rw-r--r--   1 mbcrump  staff   73 Dec  1 19:47 description
drwxr-xr-x  13 mbcrump  staff  416 Dec  1 19:47 hooks
drwxr-xr-x   3 mbcrump  staff   96 Dec  1 19:47 info
drwxr-xr-x  27 mbcrump  staff  864 Dec  1 19:48 objects
-rw-r--r--   1 mbcrump  staff  105 Dec  1 19:47 packed-refs
drwxr-xr-x   4 mbcrump  staff  128 Dec  1 19:47 refs
Enter fullscreen mode Exit fullscreen mode

Part 2 - Create a file of passwords that you'd like to remove:

  • Create a passwords.txt file and place and enter the passwords that you'd like to remove from your GitHub repo.

I created mine on macOS with touch passwords.txt or echo some-text > passwords.txt on Windows and added the password that I accidentally committed:

qph@}uC,7cGLBdsX
Enter fullscreen mode Exit fullscreen mode
  • Save the file.

Part 3 - Install BFG:

Enter BFG. According to the author:

BFG is a simpler, faster alternative to git-filter-branch for cleansing bad data out of your Git repository history:
Removing Crazy Big Files
Removing Passwords, Credentials & other Private data

  • Install BFG with brew install bfg assuming you have Homebrew installed and using a Mac or download the JAR file if you are on Windows.

Part 4 - Clean up the passwords previously committed:

  • Run bfg --replace-text passwords.txt crumpbot.git on Mac or java -jar bfg.jar --replace-text passwords.txt crumpbot.git if using the JAR file.

  • Below is output from that command:

[mbcrump@Michaels-MBP-3]:[~/Documents/code]$ bfg --replace-text passwords.txt crumpbot.git

Using repo : /Users/mbcrump/Documents/code/crumpbot.git

Found 2489 objects to protect
Found 2 commit-pointing refs : HEAD, refs/heads/master

Protected commits
-----------------

These are your protected commits, and so their contents will NOT be altered:

 * commit 58969937 (protected by 'HEAD')

Cleaning
--------

Found 11 commits
Cleaning commits:       100% (11/11)
Cleaning commits completed in 96 ms.

Updating 1 Ref
--------------

        Ref                 Before     After   
        ---------------------------------------
        refs/heads/master | 58969937 | 3f9041c9

Updating references:    100% (1/1)
...Ref update completed in 24 ms.

Commit Tree-Dirt History
------------------------

        Earliest      Latest
        |                  |
        D D D D DD D D m m m

        D = dirty commits (file tree fixed)
        m = modified commits (commit message or parents changed)
        . = clean commits (no changes to file tree)

                                Before     After   
        -------------------------------------------
        First modified commit | 39e68d03 | 95e6f9f4
        Last dirty commit     | 2007b5c5 | 0f57a693

Changed files
-------------

        Filename   Before & After                               
        --------------------------------------------------------
        bot.js   | 1b55a8d0 ⇒ 02758dd8, cba19782 ⇒ db95f8c2, ...


In total, 19 object ids were changed. Full details are logged here:

        /Users/mbcrump/Documents/code/crumpbot.git.bfg-report/2019-12-01/19-48-22

BFG run is complete! When ready, run: git reflog expire --expire=now --all && git gc --prune=now --aggressive
Enter fullscreen mode Exit fullscreen mode

Part 5 - Pushing to GitHub:

  • Run git reflog expire --expire=now --all && git gc --prune=now --aggressive as indicated by the output.
  • Run git push to push it to your repo.

Part 6 - Wrap-up and verify your repo was updated successfully:

If you go back to your GitHub repo and look at prior commits, then you should see *REMOVED* like the following:

var tmi = require("tmi.js")
var channel = "mbcrump"

var config = {
    options: {
        debug: true
    }, 
    connection: {
        cluster: "aws", 
        reconnect: true
    },
    identity: {
        username: "mbcrump",
        password: "***REMOVED***"
    },
    channels: [channel]
}
Enter fullscreen mode Exit fullscreen mode

As pointed out by https://twitter.com/grierson_david

Such commits should always be treated as the equivalent of a password leak.

There is no way to guarantee that the exposed passwords have not been copied elsewhere so should always be considered compromised and changed.

I hope this helps someone out there and if you want to stay in touch then I can be found on Twitch, Twitter or GitHub.

Top comments (3)

Collapse
 
mindstormer619 profile image
Siddarth Iyer

Thanks for the post! I think one important thing to emphasize is that if this is a shared project, it'll require all other users to stop development on their local Git clones, and re-clone the entire repository again once the central repo has been purged.

Collapse
 
djangotricks profile image
Aidas Bendoraitis

Thanks for the post. It seems very useful.

Is there a way to control the string to which the secrets will be replaced? For example, I would like to have an empty string instead of ***REMOVED***.

Collapse
 
mbcrump profile image
Michael Crump

It appears to be hard coded. But you could download the source and change that line to what you want and build it.