GitHub にパスワードとかセンシティブなファイルを push してしまったときの対処法

Posted on
qiita Git GitHub

この記事はQiitaの記事をエクスポートしたものです。内容が古くなっている可能性があります。

.gitignore し忘れて他人に見えちゃマズいファイル(パスワードをベタ書きしたファイルや AWS_SECRET_ACCESS_KEY を書いたファイルとか)を git commit しちゃった!そんなときは

$ git reset --hard HEAD~

すればすぐ何もなかったことにできます。

が!そこで気付かずに GitHub へ git push してしまった!こうなると容易に何もなかったことにはできません。

この記事では、こういうときに何もなかったことにする方法を紹介します。

そのデータを無効にする

特に Public Repository の場合はすでにそのデータが他人の目に触れていた…ということも十分ありえます。AWS_SECRET_ACCESS_KEY なんかは取得用のクローラが存在するとも聞きます。ので、まずは不正利用されても影響が出ないように、__パスワードの書き換えやトークンの無効化__を施しましょう。

(この時点でもう何もなかったことになってない気がする)

git の履歴から該当のファイルを消す

git resetgit filter-branch 2つの方法があります。

git reset

(2015-12-29 15:00 追記) git reset だとセンシティブファイル以外の作業履歴もすべて消去されてしまうので、それらを残しておきたい場合は後述 git filter-branch でコミットを書き換えるようにしてください。 (追記終わり)

該当ファイルを git commit してすぐ気づいた (3コミット以内) なら、まだ git reset で消せます。

$ git reset --hard HEAD~2 # 消すコミットの数

git filter-branch

気づいたのはそこから何コミットもしたあと…だと git reset でそこまでの履歴を全部消すのは現実的ではありません。そんな時に役立つのが git filter-branch です。filter-branch は普段見慣れない(であってほしい)ですが、__大量のコミットを機械的に書き換える__コマンドです。今回みたいにファイルを消す以外にも、リポジトリ全体のコミットオーサーの書き換えとかも一発でできます。

現在作業中の Pull Request に間違えてコミットしてしまったときは、そのブランチにだけ filter-branch を適用するので以下の様なコマンドになります。/path/to/file_to_rm は消したいファイル、1234abcd はブランチ分岐元のコミットのハッシュ値、branch_name は操作対象のブランチ名です。

$ git filter-branch --force --index-filter \
    'git rm --cached --ignore-unmatch /path/to/file_to_rm' \
    --prune-empty -- 1234abcd...branch_name --first-parent

ここで 1234abcd...branch_name --first-parent は、git rev-list というコマンドの引数と同等であり、書き換えるコミットを指定しています。実際に

$ git rev-list 1234abcd...branch_name --first-parent

を叩いてみれば、Pull Request の Commits に出てくるのと同じコミットハッシュの一覧が得られます。

他のブランチすべてを含めた全体から削除するときは以下のようにします。

$ git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch /path/to/file_to_rm' \
  --prune-empty --tag-name-filter cat -- --all

これで履歴から該当ファイルが完全に取り除かれます。

git push -f

日頃忌避される、というか何もないときは__絶対に__叩いてはいけない git push -f をここで使います。これしかやりようがないので…。GitHub のリモートリポジトリ上の履歴を書き換えるためです。

$ git push -f origin branch_name

(2015-12-30 15:10 追記) ローカルリポジトリから完全に痕跡を消す

ここまで resetfilter-branch した内容ですが、実はローカルリポジトリに reflog という形で作業履歴が残ってしまっています。

git最強のオプション filter-branch - Qiita

の記事にもあるように、直接コミットハッシュを選択すると中身が見えてしまいます…これらも消しましょう。

$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --aggressive --prune=now

これで、ローカルリポジトリからは完全にセンシティブ情報が抹消されました。

(コメントでご指摘くださった @sachin21 さんありがとうございました :bow:)

他のチームメンバに手元のリポジトリを取得し直すようお願いする

他のチームメンバの手元にチェックアウトしてあるローカルリポジトリの履歴と、 GitHub 上の履歴は別物になってしまいました。なので、(自分しか触ってないような)トピックブランチならまだしも filter-branch で master 含む全体を書き換えた場合は、再度 git clone し直すなりブランチ消して fetch し直すよう他のメンバーにお願いします。

GitHub のサポートに連絡する

これで GitHub 上で history や Pull Request のページからは該当ファイル・該当コミットにアクセスする道がなくなったので一見一段落に見えます。

…が、実はコミット単体のページは GitHub 上にキャッシュとして存在しており、https://github.com/dtan4/terraforming/commit/64f752dd8d93c5b7326175a69cece9e742fd010b のように__コミットハッシュを直接指定すると普通に閲覧できます__。

そこで、https://github.com/contact から GitHub のサポートにキャッシュを消すよう連絡する必要があります。勿論英語です。名前とメールアドレスは既に入っているので、

  • Subject:
Request to remove page caches including sensitive data
  • Body:
Hello,

We accidentally commited sensitive data into the repository.
According to https://help.github.com/articles/remove-sensitive-data/, we already ran `git filter-branch` on the branch and force-pushed.
Sensitive data was disappeared from git tree.

I'm sorry to trouble to you, but could you remove cached views of these commits from github.com?

*Target repository*

https://github.com/user/repo

*Commit hashes to be removed*

...
...
...
...

Thanks,
Daisuke

のように送ればよいです。 ここで記載するのはコミットハッシュだけで良いです。一応と思って Pull Request の URL も載せると Pull Request も一緒に消されます(実話)。

そうすれば、わりとすぐに(早ければ一時間かからないかも)「消しましたよ!」というメールが届きます。確認できれば、これで本当の一件落着です。お疲れ様でした :tada:

おわりに

GitHub 上からセンシティブなファイルを削除する方法を紹介しました。この記事のお世話にならないよう、日頃から__センシティブなファイルは .gitignore する__、__コミット前に diff を見る__ことを忘れないようにしましょう。

REF