リポジトリからの機密データの削除について
`git-filter-repo` などのツールを使ってリポジトリの履歴を変更する場合、その影響を理解することが重要です。 履歴の書き換えを成功させるには、コラボレーターとの慎重な調整が必要であり、管理する必要がある多くの副作用を伴います。
多くの場合、削除する必要がある機密データがシークレット (パスワード、トークン、資格情報など) である場合、最初の手順として、そのシークレットを失効させたり、ローテーションしたりする必要があることに注意してください。 シークレットが失効されるかローテーションされると、アクセスには使用できなくなりますが、問題を解決するには十分な場合があります。 履歴を書き換えてシークレットを削除するために追加の手順を実行しても、保証されない場合があります。
履歴を書き換えることによる副作用
履歴の書き換えには多くの副作用があります。次に例を示します。
-
**再汚染の高いリスク**: 残念ながら、機密データをリポジトリに再プッシュして、さらに大きな混乱が起こることはよくあります。 他の開発者が書き換え前のクローンを持っていて、書き換え後に単に `git pull` に続いて `git push` を実行した場合、機密データが返されます。 クローンを破棄して再クローンするか、まずクローンをクリーンアップするために複数の手順を慎重に実行する必要があります。 -
**他の開発者の作業が失われるリスク**: クリーンを試みている間に他の開発者が機密データを含むブランチの更新を続けた場合、クリーンをやり直すか、その作業を破棄する必要があります。 -
**変更されたコミット ハッシュ**: 履歴を書き換えると、機密データを導入したコミットのハッシュ "と" その後のすべてのコミットが変更されます。__ コミット ハッシュが不変であることに依存するツールや自動化は、動作しなくなったり、問題が発生したりする可能性があります。 -
**ブランチ保護の課題**: 強制プッシュを防ぐブランチ保護がある場合、機密データを削除するには、それらの保護を (少なくとも一時的に) 無効にする必要があります。 -
**クローズされたプルリクエストの差分ビューの不具合**: 機密データを削除するには、プルリクエストの差分ビューの表示に使われる内部参照を削除する必要があるため、差分は表示できなくなります。 これは、機密データを導入した 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 上でキャッシュされているビューの SHA-1 ハッシュによって直接
- 参照元の pull request によって
自分のリポジトリの他のユーザーによるクローンから機密データを削除することはできません。クローンを所有する同僚に、「他のコピーがクリーンアップされていることを確認する: 同僚のクローン」の手順をマニュアルから送信し、git-filter-repo彼ら自身で対処してもらう必要があります。 ただし、GitHub の pull request 内のキャッシュされたビューと機密データへの参照は、サイト管理者 に問い合わせることで完全に削除できます。
機密データを導入したコミットがフォークに存在する場合は、引き続きそこでアクセスできます。 フォークのオーナーと調整し、機密データを削除するかフォークを完全に削除するように依頼する必要があります。
リポジトリの履歴を書き換える場合は、これらの制限事項と課題を考慮して決めてください。
git-filter-repo を使ってローカル リポジトリの履歴からファイルを消去する
-
[ `git-filter-repo` ツール](https://github.com/newren/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重要
機密データを含むファイルが (移動または名前が変更されたため) 他のパスに存在していた場合は、そのファイルに追加の
--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 originGitHub によって読み取り専用としてマークされているため、このコマンドでは、
refs/pull/で始まる参照をプッシュできません。 これらのプッシュ エラーは、次のセクションで処理します。 他の参照がプッシュに失敗した場合は、そのブランチに対してブランチ保護が有効になっている可能性があり、一時的にオフにしてプッシュをやり直す必要があります。refs/pull/で始まる参照のみが更新に失敗するようになるまで繰り返します。
GitHub からデータを完全に削除する
`git-filter-repo` を使って機密データを削除し、変更を GitHub にプッシュした後、さらにいくつかの手順を実行して、GitHub からデータを完全に削除する必要があります。
-
サイト管理者 に問い合わせ、次の情報を提供してください。
- 問題の所有者とリポジトリ名 (例: YOUR-USERNAME/YOUR-REPOSITORY)。
- 前の手順で見つかった、影響を受ける pull request の数。 これは、影響を受ける範囲をユーザーが理解していることを確認するためにサイト管理者が使います。
-
`git-filter-repo` によって報告された最初に変更されたコミット (出力の `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 上の影響を受ける PR を逆参照するか、削除します。
- サーバー上でガベージ コレクションを実行して、機密データをストレージから消去します。
- キャッシュされたビューを削除します。
- LFS オブジェクトが関係している場合は、孤立している LFS オブジェクトを削除または消去します。
サイト管理者が到達不能な Git オブジェクトを削除する方法の詳細については、「コマンド ライン ユーティリティ」を参照してください。 サイト管理者が到達可能なコミットを識別する方法の詳細については、「到達可能なコミットを識別する」を参照してください。
-
コラボレーターは、以前の (汚染された) リポジトリの履歴から、作成したブランチをマージ_ではなく_、リベースする必要があります。 マージコミットを一度でも行うと、手間をかけてパージした汚れた履歴の一部または全部が再導入されてしまう可能性があります。 必要であれば追加の手順を実行する必要があります。「他のコピーも整理されていることを確認してください:同僚のクローン」をマニュアルで参照してください。
到達可能なコミットを識別する
不要なデータや機密データをリポジトリから完全に削除するには、最初にデータを導入したコミットをブランチ、タグ、pull request、フォークで完全に参照させないようにする必要があります。 どこかに 1つの参照が存在すると、ガベージ コレクションでデータを完全に消去することができなくなります。
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 キャッシュを無効にする] をクリックしてキャッシュ データを削除します。
将来にわたって誤ったコミットを回避する
共同作成者による誤ったコミットを防ぐことは、機密情報が公開されるのを防ぐのに役立ちます。 詳しくは、「組織でのデータ漏洩を防ぐためのベスト プラクティス」をご覧ください。
共有すべきではないものをコミットまたはプッシュしないようにするために、いくつかの対策があります。
- git で追跡すべきではない機密データがファイル内で見つかる可能性がある場合は、そのファイル名を
.gitignoreに追加します (そして、他の開発者を守るために、その変更を必ずコミットして.gitignoreにプッシュします)。 - コードにシークレットをハードコーディングしないでください。 環境変数、または Azure Key Vault、AWS Secrets Manager、HashiCorp Vault などのシークレット管理サービスを使用して、実行時にシークレットを管理および挿入します。
- 機密データのコミット前またはどこかにプッシュされる前にチェックするコミット前フックを作成するか、コミット前フックで git-secrets や gileaks などのよく知られたツールを使います (各コラボレーターに対して、選んだコミット前フックを設定するように必ず依頼してください)。
-
[GitHub Desktop](https://desktop.github.com/) や [gitk](https://git-scm.com/docs/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` マニュアル ページ](https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html) (特に「ディスカッション」セクションの「機密データの削除」サブセクション)。 -
[Pro Git: Git ツール - 履歴の書き換え](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) -
[AUTOTITLE](/code-security/secret-scanning/introduction/about-secret-scanning)