リポジトリからの機密データの削除について
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 ハッシュを直接介して
- それらを参照するプル リクエストを通じて。
リポジトリにある他のユーザーのクローンやフォークから機密データを削除することはできませんが、GitHub Enterprise Server 上のキャッシュされているビューや、pull request での機密データへの参照は、サイト管理者 に連絡して完全に削除することができます。
機密データを導入したコミットがフォークに存在する場合は、引き続きそこでアクセスできます。 フォークのオーナーと調整し、機密データを削除するかフォークを完全に削除するように依頼する必要があります。
リポジトリの履歴を書き換える場合は、これらの制限事項と課題を考慮して決めてください。
git-filter-repo を使ってリポジトリの履歴からファイルを削除する
Warning
変更の一時退避後に git filter-repo
を実行すると、その他の stash コマンドを使用して変更を取得できなくなります。 git filter-repo
を実行する前に、行ったすべての変更の一時退避を解除することをお勧めします。 最後に一時退避した一連の変更の一時退避を解除するには、git stash show -p | git apply -R
を実行します。 詳細については、「Git ツール - 一時退避とクリーニング」を参照してください。
git filter-repo
の動作を説明するために、リポジトリの履歴から機密データを含むファイルを削除し、それを .gitignore
に追加して確実に、誤って再コミットされないようにする方法を示します。
-
git filter-repo ツールの最新リリースをインストールします。
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 > Initialized empty Git repository in /Users/YOUR-FILE-PATH/YOUR-REPOSITORY/.git/ > remote: Counting objects: 1301, done. > remote: Compressing objects: 100% (769/769), done. > remote: Total 1301 (delta 724), reused 910 (delta 522) > Receiving objects: 100% (1301/1301), 164.39 KiB, done. > Resolving deltas: 100% (724/724), done.
-
リポジトリの作業ディレクトリに移動します。
cd YOUR-REPOSITORY
-
次のコマンドを実行します。
PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
は、ファイル名だけでなく、削除するファイルへのパスに置き換えてください。 これらの引数では、次のことが行われます。-
すべてのブランチとタグの履歴全体を Git に強制的に処理させるが、チェックアウトはしない
-
指定されたファイルと、結果として生成された空のコミットをすべて削除する
-
リモート URL など、 .git/config ファイルに格納されている一部の構成を削除する。 後で復元するために、このファイルを事前にバックアップすることができます。
-
既存のタグを上書きする
$ git filter-repo --invert-paths --path PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA Parsed 197 commits New history written in 0.11 seconds; now repacking/cleaning... Repacking your repo and cleaning out old unneeded objects Enumerating objects: 210, done. Counting objects: 100% (210/210), done. Delta compression using up to 12 threads Compressing objects: 100% (127/127), done. Writing objects: 100% (210/210), done. Building bitmaps: 100% (48/48), done. Total 210 (delta 98), reused 144 (delta 75), pack-reused 0 Completely finished after 0.64 seconds.
Important
(移動または名前が変更されたため) 他のパスに機密データが使用されたファイルが存在する場合は、これらのパスでもこのコマンドを実行する必要があります。
-
-
リポジトリの履歴から必要なものをすべて削除したことをダブルチェックします。
-
git filter-repo
ツールによって、構成済みのリモートが自動的に削除されます。git remote set-url
コマンドを使用すると、リモートが復元され、OWNER
とREPO
はリポジトリの詳細で置き換えられます。 詳しくは、「リモートリポジトリを管理する」をご覧ください。git remote add origin https://github.com/OWNER/REPOSITORY.git
-
リポジトリの状態に問題がなく、リモートの設定を適切に完了したら、ローカルの変更を強制プッシュして、お使いの GitHub Enterprise Server インスタンス 上のリポジトリを上書きします。 コミットの履歴から機密データを削除するには、強制プッシュが必要です。
$ git push origin --force --all > Counting objects: 1074, done. > Delta compression using 2 threads. > Compressing objects: 100% (677/677), done. > Writing objects: 100% (1058/1058), 148.85 KiB, done. > Total 1058 (delta 590), reused 602 (delta 378) > To https://HOSTNAME/YOUR-USERNAME/YOUR-REPOSITORY.git > + 48dc599...051452f main -> main (forced update)
-
タグ付けされたリリースから機密ファイルを削除するには、Git タグに対して強制的にプッシュする必要もあります。
$ git push origin --force --tags > Counting objects: 321, done. > Delta compression using up to 8 threads. > Compressing objects: 100% (166/166), done. > Writing objects: 100% (321/321), 331.74 KiB | 0 bytes/s, done. > Total 321 (delta 124), reused 269 (delta 108) > To https://HOSTNAME/YOUR-USERNAME/YOUR-REPOSITORY.git > + 48dc599...051452f main -> main (forced update)
GitHub
からデータを完全に削除する
git filter-repo
を使って機密データを削除し、変更を GitHub Enterprise Server にプッシュした後は、さらにいくつかの手順を実行して、GitHub Enterprise Server からデータを完全に削除する必要があります。
-
サイト管理者 にお問い合わせいただき、GitHub Enterprise Server の pull request 内のキャッシュされたビューと機密データへの参照を削除するよう依頼してください。 リポジトリの名前と削除する必要があるコミットへのリンクの一方または両方を指定してください。 サイト管理者が到達できない Git オブジェクトを削除する方法の詳細については、「コマンド ライン ユーティリティ」を参照してください。 サイト管理者が到達可能なコミットを識別する方法の詳細については、「到達可能なコミットを識別する」を参照してください。
-
以前の (汚染された) リポジトリの履歴から、作成したブランチをマージ ではなく、リベースするようにコラボレーターに指示します。 マージコミットを 1 回でも行うと、パージで問題が発生したばかりの汚染された履歴の一部または全部が再導入されてしまいます。
到達可能なコミットを識別する
不要なデータや機密データをリポジトリから完全に削除するには、最初にデータを導入したコミットをブランチ、タグ、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
で生成される正確な差分です。- リポジトリのプッシュ保護を有効にして、ハードコーディングされたシークレットを含むプッシュがコードベースにコミットされないようにします。 詳しくは、「プッシュ保護について」をご覧ください。