example-petstore.com

サンプル ドメイン · 実際のサービスではありません · ブラウザーでアクセスするとこのページが表示されます · API リクエストには 410 Gone が返されます

ガイド · セキュリティ

Git の履歴からシークレットを削除する

キー、トークン、パスワードがコミットに含まれてしまった場合、新しいコミットでファイルを削除しても解決しません。古いコミットには、すべてのクローンとフォークの中にそのまま残っています。このガイドでは正しい順序を説明します。まず無効化し、次に履歴を書き換え、ホスティング プラットフォームを整理して、再発を防ぎます。

まず、シークレットを無効にする

コミットしたシークレットは、プライベート リポジトリであっても漏えいしたものとして扱ってください。クローン、フォーク、CI のログ、バックアップにすでにコピーが残っている可能性があり、公開リポジトリの新しいコミットは自動スキャナーによって数分以内に検出されます。

  1. キー、トークン、パスワードを発行したサービスで、無効にするか再発行します。
  2. 新しいものはコードではなく、設定やシークレット マネージャーに保存します。
  3. 心当たりのない利用がないか、サービスのログを確認します。

主なプロバイダーでキーを無効にする場所は、漏えいした認証情報: 今すぐ行うべきことを参照してください。履歴の書き換えはこの後に行うもので、無効化の代わりにはなりません。

履歴を書き換えるかどうか

無効にしたシークレットではもうアクセスできず、GitHub もそれで十分な場合があるとしています。それでも、次のような場合は書き換える価値があります。

  • シークレットを無効にできない場合、またはファイルに個人データや顧客記録など、ほかの機密データが含まれている場合。
  • リポジトリが公開されている場合、または今後公開する場合。
  • 古いシークレットがスキャナーに報告され続けないようにしたい場合。

書き換えると、それ以降のすべてのコミットの ID が変わります。共同作業者は自分の作業をリベースする必要があり、オープン中のプル リクエストはレビュー コメントを失うことがあり、コミットの署名は削除されます。関係者全員と実施のタイミングを決め、オープン中のプル リクエストは先にマージするかクローズしてください。

すべての出現箇所を探す

# この文字列を追加または削除したコミットは?
git log --all --oneline -S 'the-secret-value'

# どのファイルに含まれていたか?
git grep 'the-secret-value' $(git rev-list --all)

# 履歴全体から既知のシークレット形式をスキャンする
gitleaks git -v

ファイルが移動または名前変更されている場合は以前の名前も含めて、シークレットが含まれていたファイル パスをすべて記録します。

git-filter-repo で書き換える

git-filter-repo は GitHub が推奨するツールです。--sensitive-data-removal オプションを備えたバージョン 2.47 以降を使用し、新しいクローンで作業してください。

# インストール (またはパッケージ マネージャーを使用)
brew install git-filter-repo        # macOS
pip install git-filter-repo         # Python があればどこでも

git clone https://github.com/YOUR-ORG/YOUR-REPO
cd YOUR-REPO

# 方法 1: ファイル全体をすべての履歴から削除する
git-filter-repo --sensitive-data-removal --invert-paths --path config/secrets.yml

# 方法 2: シークレットが出現するすべての箇所で置き換える
git-filter-repo --sensitive-data-removal --replace-text ../replacements.txt

置換ファイルには 1 行に 1 つの値を記述します。既定では、一致した箇所はそれぞれ ***REMOVED*** になります。==> を使うと置換後の文字列を指定でき、regex: を使うとパターンで照合できます。

# ../replacements.txt (リポジトリの外に置く)
sk_live_51Hx0000000000000000000000
AKIA0000000000000000==>AWS_ACCESS_KEY_ID_REMOVED
regex:password\s*=\s*"[^"]+"==>password = "REMOVED"

git log --all -S 'the-secret-value' で結果を確認します。何も表示されなければ問題ありません。その後、リモートを上書きします。

git push --force --mirror origin

フォース プッシュをブロックするブランチ保護は、この間だけ無効にする必要があります。プッシュした後は、書き換えを元に戻すことはできません。

GitHub を整理する

フォース プッシュの後も、古いコミットにはプル リクエスト、キャッシュされたビュー、フォークを通じてアクセスできます。

  • プル リクエストとキャッシュされたビュー: 影響を受けたプル リクエストの数 (grep -c '^refs/pull/.*/head$' .git/filter-repo/changed-refs) と、git-filter-repo が出力した「First Changed Commit(s)」を添えて GitHub サポートに連絡します。GitHub が対応するのは、認証情報の再発行ではリスクを取り除けない場合に限られます。
  • フォーク: フォーク内のコミットはそのまま残ります。所有者にフォークの削除または整理を依頼してください。GitHub は所有者の連絡先を共有しません。
  • 同僚のクローン: 全員が自分のブランチを新しい履歴にリベースする必要があります。マージは使いません。一度でもマージすると、古いコミットが戻ってきます。

GitLab、Bitbucket、セルフホスト型のサーバーでも手順は同様です。古いオブジェクトとキャッシュされたビューの削除方法は、各プラットフォームのドキュメントで確認してください。

代替手段: BFG

BFG Repo-Cleaner は Java ベースの古いツールですが、現在も広く使われています。ミラー クローンに対して動作し、既定では最新のコミットには手を加えません。そのため、先に通常のコミットで現在のバージョンからシークレットを削除しておきます。

git clone --mirror https://github.com/YOUR-ORG/YOUR-REPO.git
java -jar bfg.jar --replace-text replacements.txt YOUR-REPO.git
cd YOUR-REPO.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push

次の漏えいを防ぐ

  • プッシュ保護: GitHub では、プッシュ保護付きのシークレット スキャンによって、既知のシークレット形式を含むプッシュがリポジトリに届く前にブロックされます。
  • コミット前のチェック: gitleaks または git-secrets を pre-commit フックとして実行し、フックを使わない人のために CI でも実行します。
  • コードではなく設定に: シークレットは環境変数やシークレット マネージャーから読み込み、最初のコミットの前に .env などのファイルを .gitignore に追加します。
  • コミットの前に確認: git add . や git commit -a を使わず、ファイルを 1 つずつステージして git diff --cached を確認します。

そのキーを api.example-petstore.com のようなサンプルのアドレスにも送信していた場合は、管理下にないサーバーにも届いています。API クライアントと SDK の設定を参照してください。

出典