リポジトリからの機密データの削除について
git filter-repo
などのツールを使ってリポジトリの履歴を変更する場合、その影響を理解することが重要です。 履歴の書き換えを成功させるには、コラボレーターとの慎重な調整が必要であり、管理する必要がある多くの副作用を伴います。
多くの場合、削除する必要がある機密データがシークレット (パスワード、トークン、資格情報など) である場合、最初の手順として、そのシークレットを失効させたり、ローテーションしたりする必要があることに注意してください。 シークレットが失効されるかローテーションされると、アクセスには使用できなくなりますが、問題を解決するには十分な場合があります。 履歴を書き換えてシークレットを削除するために追加の手順を実行しても、保証されない場合があります。
履歴を書き換えることによる副作用
履歴の書き換えには多くの副作用があります。次に例を示します。
- 再汚染の高いリスク: 残念ながら、機密データをリポジトリに再プッシュして、さらに大きな混乱が起こることはよくあります。 他の開発者が書き換え前のクローンを持っていて、書き換え後に単に
git pull
に続いてgit push
を実行した場合、機密データが返されます。 クローンを破棄して再クローンするか、まずクローンをクリーンアップするために複数の手順を慎重に実行する必要があります。 - 他の開発者の作業が失われるリスク: クリーンを試みている間に他の開発者が機密データを含むブランチの更新を続けた場合、クリーンをやり直すか、その作業を破棄する必要があります。
- 変更されたコミット ハッシュ: 履歴を書き換えると、機密データを導入したコミットのハッシュ "と" その後のすべてのコミットが変更されます。__ コミット ハッシュが不変であることに依存するツールや自動化は、動作しなくなったり、問題が発生したりする可能性があります。
- ブランチ保護の課題: 強制プッシュを防ぐブランチ保護がある場合、機密データを削除するには、それらの保護を (少なくとも一時的に) 無効にする必要があります。
- 中止された pull request の差分ビューの破損: 機密データを削除するには、pull request の diff ビューの表示に使われる内部参照を削除する必要があるため、これらの diff は表示できなくなります。 これは、機密データを導入した PR だけでなく、機密データ PR がマージされた後のバージョンの履歴に基づいて構築されるすべての PR にも当てはまります (その後の PR が機密データを含むファイルを追加または変更しなかった場合でもそうです)。
- 開いている pull request との連携が不十分: コミット SHA が変更されると、別の PR diff が生じ、古い PR diff に対するコメントが無効になって失われる可能性があります。その結果、作成者やレビュー担当者が混乱する可能性があります。 リポジトリからファイルを削除する前に、開いているすべての pull request を結合または閉じることをお勧めします。
- コミットとタグのシグネチャが失われる: コミットまたはタグのシグネチャはコミット ハッシュに依存しています。コミット ハッシュは履歴の書き換えによって変更されるため、シグネチャは無効になり、多くの履歴書き換えツール (
git filter-repo
を含む) では単にシグネチャが削除されます。 実際、git filter-repo
の場合、機密データの削除より前の日付のコミット シグネチャとタグ シグネチャも削除されます (技術的には、必要に応じて--refs
オプションをgit filter-repo
に設定することでこれを回避できますが、その場合は、履歴に機密データが含まれているすべての参照を指定し、その範囲に機密データが含まれているコミットを含めるように注意する必要があります)。 - 他のユーザーを機密データに直接誘導する: Git は、悪意のある個人が気付かれることなくサーバーに侵入して履歴を変更できないように、コミット識別子に暗号チェックを組み込むように設計されています。 これはセキュリティの観点からは役立ちますが、機密データの観点から見ると、機密データの消去は非常に複雑な調整のプロセスであることを意味しています。さらに、履歴を変更すると、既存のクローンを持っている知識豊富なユーザーであれば、履歴の相違に気付き、それを利用して、中央リポジトリから削除したクローン内にまだ残っている機密データを短時間で簡単に見つけることができることを意味します。
機密データの公開について
リポジトリから機密データを削除するには、次の 4 つの大まかな手順が必要です。
- git-filter-repo を使ってリポジトリをローカルで書き換える
- ローカルで書き換えられた履歴を使って GitHub 上のリポジトリを更新する
- 同僚と連携して、存在する他のクローンをクリーンアップする
- 再発を防ぎ、将来の機密データの流出を回避する
履歴を書き換えてフォース プッシュするだけの場合、機密データを含むコミットは他の場所からアクセスできる可能性があります。
- リポジトリのクローンまたはフォーク内
- GitHub Enterprise Server のキャッシュされたビューで SHA-1 ハッシュを直接介して
- それらを参照するプル リクエストを通じて。
自分のリポジトリの他のユーザーによるクローンから機密データを削除することはできません。git filter-repo
マニュアルの「他のコピーがクリーンアップされていることを確認する: 同僚のクローン」の手順を相手に送信して、クリーンアップしてもらう必要があります。 ただし、GitHub Enterprise Server の pull request 内のキャッシュされたビューと機密データへの参照は、サイト管理者 に問い合わせることで完全に削除できます。
機密データを導入したコミットがフォークに存在する場合は、引き続きそこでアクセスできます。 フォークのオーナーと調整し、機密データを削除するかフォークを完全に削除するように依頼する必要があります。
リポジトリの履歴を書き換える場合は、これらの制限事項と課題を考慮して決めてください。
git-filter-repo を使ってローカル リポジトリの履歴からファイルを消去する
-
git filter-repo
ツールの最新リリースをインストールします。--sensitive-data-removal
フラグの設定されたバージョン (つまり、バージョン 2.47 以降) が必要です。git filter-repo
は手動で、またはパッケージ マネージャーを使用してインストールすることができます。 たとえば、HomeBrew でツールをインストールするには、brew install
コマンドを使用します。brew install git-filter-repo
詳細については、
newren/git-filter-repo
リポジトリ内の INSTALL.md ファイルを参照してください。 -
リポジトリをローカル コンピューターにクローンします。 「リポジトリをクローンする」をご覧ください。
git clone https://HOSTNAME/YOUR-USERNAME/YOUR-REPOSITORY
-
リポジトリの作業ディレクトリに移動します。
cd YOUR-REPOSITORY
-
git filter-repo
コマンドを実行して機密データをクリーンアップします。すべてのブランチ、タグ、参照から特定のファイルを削除する場合は、次のコマンドを実行して、
PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
を、ファイル名だけでなく、削除するファイルの git パスに置き換えます (例:src/module/phone-numbers.txt
)。git filter-repo --sensitive-data-removal --invert-paths --path PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
Important
機密データを含むファイルが (移動または名前が変更されたため) 他のパスに存在していた場合は、そのファイルに追加の
--path
引数を追加するか、代替パスを指定してこのコマンドを 2 回実行する必要があります。リポジトリの履歴内で任意の場所にあるバイナリ以外のファイルから、
../passwords.txt
に記載されているすべてのテキストを置換する場合は、次のコマンドを実行します。git filter-repo --sensitive-data-removal --replace-text ../passwords.txt
-
リポジトリの履歴から必要なものをすべて削除したことをダブルチェックします。
-
この履歴の書き換えによって悪影響を受ける pull request の数を確認します。 この情報は後で必要になります。
$ grep -c '^refs/pull/.*/head$' .git/filter-repo/changed-refs 4
-c
をドロップすると、どの pull request が影響を受けるかを確認できます。$ grep '^refs/pull/.*/head$' .git/filter-repo/changed-refs refs/pull/589/head refs/pull/602/head refs/pull/604/head refs/pull/605/head
この出力には、2 つ目と 3 つ目のスラッシュの間に pull request 番号が含まれています。 影響を受ける pull request の数が予想より多い場合は、このクローンを破棄しても問題ありません。そして、書き換えをやり直すか、機密データの削除を中止することができます。 次の手順に進むと、書き換えは元に戻せなくなります。
-
リポジトリの状態に問題がない場合は、ローカルの変更を強制プッシュして、お使いの GitHub Enterprise Server インスタンス のリポジトリを上書きします。
--force
は--mirror
で暗示されていますが、あなたはすべてのブランチ、タグ、参照を強制的に更新しており、リポジトリのクリーンアップ中に他のユーザーがそれらの参照に対して行った変更がある場合は破棄することになることを示すために、以下に記載しています。git push --force --mirror origin
それらは GitHub Enterprise Server によって読み取り専用とマークされているため、このコマンドでは
refs/pull/
で始まる参照をプッシュできません。 これらのプッシュ エラーは、次のセクションで処理します。 他の参照がプッシュに失敗した場合は、そのブランチに対してブランチ保護が有効になっている可能性があり、一時的にオフにしてプッシュをやり直す必要があります。 更新に失敗するのがrefs/pull/
で始まる参照のみになるまで繰り返します。
GitHub
からデータを完全に削除する
git filter-repo
を使って機密データを削除し、変更を GitHub Enterprise Server にプッシュした後は、さらにいくつかの手順を実行して、GitHub Enterprise Server からデータを完全に削除する必要があります。
-
サイト管理者 に問い合わせ、次の情報を提供してください。
- 問題の所有者とリポジトリ名 (例: YOUR-USERNAME/YOUR-REPOSITORY)。
- 前の手順で見つかった、影響を受ける pull request の数。 これは、影響を受ける範囲をユーザーが理解していることを確認するためにサポートが利用します。
git filter-repo
によって報告された "First Changed Commit(s)" (出力のNOTE: First Changed Commit(s)
を探してください)。- git-filter-repo の出力に
NOTE: There were LFS Objects Orphaned by this rewrite
がある場合 ("First Changed Commit" の直後)、"LFS Objects Orphaned" があることを伝え、指定されたファイルもチケットにアップロードします。
PR 以外のすべての参照を正常にクリーンアップし、機密データへの参照を含むフォークがない場合、サポートは次のことを行います。
- GitHub Enterprise Server 上の影響を受ける PR を逆参照または削除します。
- サーバー上でガベージ コレクションを実行して、機密データをストレージから消去します。
- キャッシュされたビューを削除します。
- LFS オブジェクトが関係している場合は、孤立している LFS オブジェクトを削除または消去します。
サイト管理者が到達不能な Git オブジェクトを削除する方法の詳細については、「コマンド ライン ユーティリティ」を参照してください。 サイト管理者が到達可能なコミットを識別する方法の詳細については、「到達可能なコミットを識別する」をご覧ください。
-
コラボレーターは、以前の (汚染された) リポジトリの履歴から、作成したブランチをマージ "ではなく"、リベースする必要があります。__ マージコミットを 1 回でも行うと、パージで問題が発生したばかりの汚染された履歴の一部または全部が再導入されてしまいます。 必要に応じて追加の手順を実行します。
git filter-repo
マニュアルの「他のコピーがクリーンアップされていることを確認する: 同僚のクローン」を参照してください。
到達可能なコミットを識別する
不要なデータや機密データをリポジトリから完全に削除するには、最初にデータを導入したコミットをブランチ、タグ、pull request、フォークで完全に参照させないようにする必要があります。 どこかに単一の参照が存在すると、ガベージ コレクションによってデータを完全に消去できなくなります。
SSH 経由でアプライアンスに接続されている場合は、次のコマンドを使用して既存の参照を確認できます。 最初に機密データを導入したコミットの SHA が必要になります。
ghe-repo OWNER/REPOSITORY -c 'git ref-contains COMMIT_SHA_NUMBER'
ghe-repo OWNER/REPOSITORY -c 'cd ../network.git && git ref-contains COMMIT_SHA_NUMBER'
これらのコマンドのいずれかが結果を返した場合、コミットが正常にガベージ コレクションされる前に、それらの参照を削除する必要があります。 2 番目のコマンドは、リポジトリのフォークに存在する参照を識別します (リポジトリにフォークがない場合は、実行をスキップできます)。
refs/heads/
またはrefs/tags/
で始まる結果は、それぞれ問題のあるコミットへの参照がまだ含まれているブランチとタグを示しており、変更されたリポジトリからコミットが完全に消去されていないか、強制的にプッシュさていないことを示しています。refs/pull/
またはrefs/__gh__/pull
で始まる結果は、問題のあるコミットを参照する pull request を示しています。 コミットをガベージ コレクションできるようにするには、これらの pull request を削除する必要があります。 プル リクエストは、https://HOSTNAME/stafftools/repositories/OWNER/REPOSITORY/PULL_REQUESTS/<PULL-REQUEST-NUMBER>
のサイト管理者ダッシュボードで削除できます (<PULL-REQUEST-NUMBER>
を pull request 番号に置き換えます)。
いずれかのフォークで参照が見つかった場合、結果は類似しますが、refs/remotes/NWO/
から始まります。 フォークを名前で識別するには、次のコマンドを実行します。
ghe-nwo NWO
機密データをリポジトリのフォークから削除するには、そのクローンに移動し、クリーンアップされたリポジトリからフェッチしてから、機密データを含むすべてのブランチとタグを、クリーンアップされたリポジトリの関連するブランチまたはタグ上にリベースします。 または、フォークを完全に削除できます。必要に応じて、ルート リポジトリのクリーンアップが完了したら、リポジトリを再フォークすることもできます。
コミットの参照を削除したら、コマンドを再実行して再確認します。
どちらの ref-contains
コマンドでも結果がない場合は、次のコマンドを実行すると、参照されていないコミットを削除する --prune
フラグを指定してガベージ コレクションを実行できます。
ghe-repo-gc -v --prune OWNER/REPOSITORY
ガベージ コレクションによってコミットが正常に削除されたら、https://HOSTNAME/stafftools/repositories/OWNER/REPOSITORY
でリポジトリのサイト管理者ダッシュボードに移動します。次に、[ネットワーク] を選択し、[Git キャッシュを無効にする] をクリックしてキャッシュ データを削除します。
将来にわたって誤ったコミットを回避する
共同作成者による誤ったコミットを防ぐことは、機密情報が公開されるのを防ぐのに役立ちます。 詳しくは、「organization でのデータ 漏洩を防ぐためのベスト プラクティス」をご覧ください。
共有すべきではないものをコミットまたはプッシュしないようにするために、いくつかの対策があります。
- git で追跡すべきではない機密データがファイル内で見つかる可能性がある場合は、そのファイル名を
.gitignore
に追加します (そして、他の開発者を守るために、その変更を必ずコミットして.gitignore
にプッシュします)。 - コードにシークレットをハードコーディングしないでください。 実行時にシークレットを管理および挿入するには、環境変数またはシークレット管理サービス (Azure Key Vault、AWS Secrets Manager、HashiCorp コンテナーなど) を使います。
- 機密データのコミット前またはどこかにプッシュされる前にチェックするコミット前フックを作成するか、コミット前フックで git-secrets や gileaks などのよく知られたツールを使います (各コラボレーターに対して、選んだコミット前フックを設定するように必ず依頼してください)。
- GitHub Desktop や gitk などのビジュアル プログラムを使用して変更をコミットします。 ビジュアルプログラムは通常、各コミットでどのファイルが追加、削除、変更されるかを正確に把握しやすくするものです。
- コマンド ラインでの catch-all コマンド
git add .
とgit commit -a
を回避するには、代わりにgit add filename
とgit rm filename
を使用してファイルを個別にステージします。 git add --interactive
を使用して、各ファイル内の変更を個別に確認およびステージします。git diff --cached
を使用して、コミットのステージした変更を確認します。 これは、git commit
フラグを使用しない限り-a
で生成される正確な差分です。- リポジトリのプッシュ保護を有効にして、ハードコーディングされたシークレットを含むプッシュがコードベースにコミットされないようにします。 詳しくは、「プッシュ保護について」をご覧ください。
参考資料
git filter-repo
マニュアル ページ (特に「ディスカッション」セクションの「機密データの削除」サブセクション)。- Pro Git: Git ツール - 履歴の書き換え
- シークレット スキャンについて