すべての開発者の悪夢:コードをコミットし、リポジトリにプッシュした後、AWSアクセスキー、データベースパスワード、またはAPIトークンがGit履歴に残っていることに突然気づく。パニックに陥る。急いで認証情報を削除して再度コミットするが、被害はすでに発生している——これらの秘密情報はGit履歴に残り、リポジトリをクローンする誰もがアクセスできる状態になっている。
このシナリオは、組織全体で毎日何千回も繰り返されている。対応には、必死にGoogleで検索し、Git履歴の書き換えを試み、認証情報がまだ侵害されていないことを願うことが含まれる。しかし、適切な認証情報漏洩への対応は、単にファイルを削除するだけではない——Gitの内部構造の理解、即座の認証情報のローテーション、そして予防メカニズムの実装が必要である。
Git履歴の問題
Gitの設計により、認証情報の漏洩は特に危険である。その理由を理解するには、Gitがデータをどのように保存するかを調べる必要がある。
削除だけでは不十分な理由
認証情報を含むファイルをコミットし、後で削除しても、認証情報はGit履歴に残る:
🚫 削除の錯覚
開発者がよく行うこと
- 誤って認証情報を含むコードをコミット
- ミスに気づく
- ファイルを削除するか認証情報を削除
- 削除をコミット
- 問題が解決したと思い込む
これが失敗する理由
- Gitはすべてのコミットの完全な履歴を保持
- 以前のコミットには依然として認証情報が含まれている
- 誰でも古いコミットをチェックアウトできる
git log -pで認証情報が表示される- リポジトリのクローンには完全な履歴が含まれる
現実
- 認証情報は無期限にアクセス可能
- すべてのリポジトリクローンに漏洩が含まれる
- フォークとミラーが認証情報を保持
- 検索エンジンが漏洩をインデックス化している可能性
- 自動スキャナーが公開リポジトリを継続的に監視
新しいコミットで認証情報を削除するだけでは、オンラインで鍵を公開した後にドアに鍵をかけるようなもの——被害はすでに発生している。
Gitの不変な履歴
Gitのコンテンツアドレス可能ストレージにより、履歴の変更は複雑になる:
🔗 Gitストレージモデル
Gitがデータを保存する方法
- 各コミットには一意のSHA-1ハッシュがある
- ハッシュはコミット内容、親ハッシュ、メタデータから計算される
- コミットを変更するとそのハッシュが変わる
- すべての子孫コミットもハッシュが変わる
- 履歴の暗号チェーンを作成
認証情報削除への影響
- 認証情報の削除には履歴の書き換えが必要
- 漏洩後のすべてのコミットを再作成する必要がある
- 新しいハッシュは既存の参照を破壊
- 共同作業者はローカルコピーを破棄する必要がある
- フォークとミラーは影響を受けない
調整の課題
- すべての開発者が書き換えられた履歴を取得する必要がある
- 古いコミットはガベージコレクションされる必要がある
- リモートリポジトリは新しい履歴を強制プッシュする必要がある
- CI/CDシステムは参照を更新する必要がある
- バックアップシステムが古い履歴を保持する可能性がある
このアーキテクチャは、認証情報の漏洩を真に「元に戻す」ことはできず、ローテーションと履歴の書き換えによって緩和するしかないことを意味する。
即座の対応:まずローテーション、後でクリーンアップ
認証情報がGitに漏洩した場合、優先順位が重要である。
ステップ1:認証情報を即座にローテーション
Git履歴の操作を試みる前に、侵害された認証情報をローテーションする:
⚠️ クリーンアップ前にローテーション
ローテーションが優先される理由
- Git履歴の書き換えには時間がかかる
- 認証情報はすでに侵害されている可能性がある
- 自動スキャナーは数分以内に漏洩を検出
- Gitのクリーンアップではアクセスを取り消せない
- 攻撃者がすでにリポジトリをクローンしている可能性がある
ローテーションすべきもの
- APIキーとトークン
- データベースパスワード
- SSH秘密鍵
- OAuthクライアントシークレット
- 暗号化キー
- サービスアカウント認証情報
ローテーションチェックリスト
- 新しい認証情報を即座に生成
- 認証情報を使用するすべてのサービスを更新
- 古い認証情報を完全に取り消す
- 不正アクセスの試みを監視
- セキュリティレビューのためにインシデントを文書化
- 将来的に短期認証情報の使用を検討
認証情報を即座にローテーションすることで、脆弱性のウィンドウを制限する。Git履歴のクリーンアップは重要だが二次的である。
⏱️ 短期認証情報の推奨
短期認証情報が重要な理由
- 定義された期間後に自動的に期限切れ
- 漏洩した場合の露出ウィンドウを削減
- 手動ローテーションが不要
- 侵害の影響範囲を制限
実装オプション
- AWS STS一時認証情報(15分〜12時間)
- Vault動的シークレット(数分〜数時間)
- 短い有効期限のOAuthトークン
- TTL付きサービスアカウントトークン
インシデント後のアクション
- 可能な限り短期認証情報に移行
- 将来の漏洩の影響を軽減
- 実装の詳細については予防戦略を参照
ステップ2:露出範囲の評価
認証情報がどの程度広がったかを判断する:
🔍 露出評価
回答すべき質問
- リポジトリは公開か非公開か?
- 認証情報はどのくらいの期間露出していたか?
- 何人がリポジトリへのアクセス権を持っているか?
- フォークやミラーは存在するか?
- CI/CDシステムが認証情報にアクセスしたか?
- コミットは複数のリモートにプッシュされたか?
公開リポジトリの露出
- 認証情報は完全に侵害されたと仮定
- GitHub、GitLab、Bitbucketはセキュリティ研究者に通知
- 自動スキャナーは数分以内にシークレットを検出
- 検索エンジンがコンテンツをインデックス化している可能性
- 認証情報のローテーションは必須であり、オプションではない
非公開リポジトリの露出
- 誰がリポジトリへのアクセス権を持っているかを評価
- 予期しないアクティビティのアクセスログを確認
- 認証情報使用の監査ログをレビュー
- 内部脅威のシナリオを考慮
- 予防措置として認証情報をローテーション
露出評価により、対応アクションの緊急性と範囲が決定される。
Git履歴から認証情報を削除
認証情報をローテーションした後、将来の露出を防ぐためにGit履歴をクリーンアップする。
git filter-branchの使用(レガシーアプローチ)
従来の方法ではgit filter-branchを使用して履歴を書き換える:
# すべてのコミットから特定のファイルを削除
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch path/to/credentials.txt" \
--prune-empty --tag-name-filter cat -- --all
# 強制ガベージコレクション
git reflog expire --expire=now --all
git gc --prune=now --aggressive
⚠️ filter-branchの制限
filter-branchが問題である理由
- 大規模リポジトリでは非常に遅い
- 複雑な構文でエラーが発生しやすい
- すべてのエッジケースを処理しない
- 中断されるとリポジトリが破損する可能性
- Gitドキュメントは代替案を推奨
まだ使用される可能性がある場合
- BFGやfilter-repoのないレガシーシステム
- シンプルな単一ファイル削除シナリオ
- 履歴が限られた小規模リポジトリ
- 他のツールが利用できない場合
現代のツールは、ほとんどのシナリオでより良い代替案を提供する。
BFG Repo-Cleanerの使用(推奨)
BFG Repo-Cleanerは、より高速でシンプルなアプローチを提供する:
# BFGをダウンロード(Javaが必要)
# https://rtyley.github.io/bfg-repo-cleaner/
# 特定のファイルを削除
java -jar bfg.jar --delete-files credentials.txt repo.git
# パターンに一致するファイルを削除
java -jar bfg.jar --delete-files "*.key" repo.git
# すべてのファイル内の認証情報を置換
echo "password123" > passwords.txt
java -jar bfg.jar --replace-text passwords.txt repo.git
# クリーンアップ
cd repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
✅ BFGの利点
BFGが優れている理由
- filter-branchより10〜720倍高速
- シンプルで直感的な構文
- デフォルトでHEADコミットを保護
- 大規模リポジトリを効率的に処理
- 十分にテストされ広く使用されている
BFGの機能
- 名前またはパターンでファイルを削除
- すべてのファイルでテキストを置換
- 大きなファイルを削除
- サイズでblobを削除
- 最近のコミットを保持
BFGは、ほとんどの認証情報削除シナリオで推奨されるツールである。
git filter-repoの使用(現代的な代替案)
git filter-repoは、最も強力で柔軟なアプローチを提供する:
# filter-repoをインストール
pip install git-filter-repo
# 特定のファイルを削除
git filter-repo --path path/to/credentials.txt --invert-paths
# パターンに一致するファイルを削除
git filter-repo --path-glob '*.key' --invert-paths
# コールバックを使用して認証情報を置換
git filter-repo --replace-text <(echo "password123==>REDACTED")
# 特定のディレクトリから認証情報を削除
git filter-repo --path secrets/ --invert-paths
🔧 filter-repoの機能
高度な機能
- 大規模リポジトリでの高速パフォーマンス
- 強力なフィルタリングオプション
- Pythonベースの拡張性
- 包括的な安全性チェック
- 詳細な操作ログ
複雑なシナリオ
- 条件付きファイル削除
- コンテンツ変換
- パスの書き換え
- コミットメッセージの変更
- 作成者情報の更新
複雑なシナリオや大規模リポジトリの場合、git filter-repoは機能と安全性の最適なバランスを提供する。
強制プッシュと調整
履歴を書き換えた後、すべてのリポジトリコピーに変更を伝播する。
リモートへの強制プッシュ
書き換えられた履歴には強制プッシュが必要:
# originに強制プッシュ
git push origin --force --all
# タグを強制プッシュ
git push origin --force --tags
# 代替案:force-with-lease(より安全)
git push origin --force-with-lease --all
⚠️ 強制プッシュのリスク
強制プッシュの危険性
- リモート履歴を上書き
- 他の開発者のローカルコピーを破壊
- 調整されていない場合、コミットを失う可能性
- リポジトリ設定によってブロックされる可能性
- 特別な権限が必要
--force-with-leaseの使用
- 通常の--forceより安全
- リモートに予期しない変更があるかチェック
- 偶発的な上書きを防止
- 依然として調整が必要
- --forceより推奨
強制プッシュは、すべてのリポジトリユーザーと調整する必要がある。
チームメンバーとの調整
すべての共同作業者はローカルコピーを更新する必要がある:
👥 チーム調整ステップ
必要なコミュニケーション
- 書き換え前にすべてのチームメンバーに通知
- 履歴を書き換える理由を説明
- 更新のための明確な指示を提供
- ローカル更新の期限を設定
- 全員が正常に更新したことを確認
チームメンバーへの指示
# ローカル作業を保存
git stash
# 書き換えられた履歴を取得
git fetch origin
# リモートに合わせてリセット
git reset --hard origin/main
# 古い参照をクリーンアップ
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# ローカル作業を復元
git stash pop
**競合の処理**
- ローカルコミットは新しい履歴にリベースする必要がある
- 手動での競合解決が必要な場合がある
- 更新前にパッチの作成を検討
- 更新後に徹底的にテスト
調整が不十分だと、古い履歴が再導入される可能性がある。
フォークとミラーの処理
リポジトリのフォークとミラーは元の履歴を保持する。
フォークの問題
フォークは漏洩した認証情報を保持する独立したコピーである:
🚫 フォークは漏洩を保持
フォークが問題である理由
- フォークは独立したリポジトリ
- 親履歴の書き換えはフォークに影響しない
- フォーク所有者は漏洩に気づいていない可能性
- 公開フォークは発見可能
- フォーク更新を強制するメカニズムがない
緩和戦略
- フォーク所有者に直接連絡
- フォークの削除または更新を依頼
- 公開フォークにDMCA削除申請を提出(極端なケース)
- フォークでの認証情報使用を監視
- 一部のフォークが残る可能性を受け入れる
公開リポジトリの場合、フォークされた認証情報は永久に露出していると仮定する。
ミラーとバックアップの処理
ミラーとバックアップシステムは古い履歴を保持する可能性がある:
⚠️ ミラーの考慮事項
一般的なミラーの場所
- CI/CDシステムキャッシュ
- バックアップシステム
- コードレビューツール
- IDEリポジトリキャッシュ
- コンテナイメージレイヤー
- デプロイメントアーティファクト
クリーンアップアクション
- CI/CDキャッシュをクリア
- バックアップシステムを更新
- コンテナイメージを再構築
- アプリケーションを再デプロイ
- IDEキャッシュをクリア
- ドキュメントリポジトリを更新
包括的なクリーンアップには、すべてのリポジトリコピーの特定が必要である。
予防:より良いアプローチ
この記事は復旧に焦点を当てているが、予防は修復よりもはるかに効果的である。包括的な予防戦略には複数の層が含まれる:
🛡️ 予防層
設定
- 認証情報ファイルの
.gitignoreパターン - 個人パターンのグローバルgitignore
- ガイダンスのためのテンプレートファイル(
.env.example)
コード実践
- ハードコードされた認証情報の代わりに環境変数
- 起動時の設定検証
- 認証情報検出のためのコードレビュー
自動スキャン
- プレコミットフック(git-secrets、detect-secrets)
- CI/CDパイプラインスキャン
- プラットフォーム提供のスキャン(GitHub、GitLab)
シークレット管理
- AWS Secrets Manager、HashiCorp Vault
- ランタイムシークレット注入
- 自動認証情報ローテーション
詳細なコードサンプルと実装戦略を含む認証情報漏洩防止の包括的なガイドについては、Gitでの認証情報防止:多層防御戦略を参照してください。
予防への投資により、複雑な復旧手順、Git履歴の書き換え、緊急認証情報ローテーションの必要性がなくなる。予防システムの構築に費やす1時間ごとに、インシデント対応の数日を節約できる。
実世界のインシデント:AWSキー漏洩
一般的なシナリオが完全な対応プロセスを示している:
⚠️ インシデントタイムライン
1日目:漏洩
- 開発者が設定ファイルにAWSアクセスキーをコミット
- 公開GitHubリポジトリにプッシュ
- GitHubシークレットスキャンが5分以内にキーを検出
- AWSが通知を受け取りアカウント所有者に警告
- 自動スキャナーがキーの使用を試み始める
1時間目:発見と対応
- セキュリティチームがGitHubアラートを受信
- AWSアクセスキーを即座にローテーション
- 不正アクセスのCloudTrailログをレビュー
- 暗号通貨マイニングインスタンスが起動されていることを発見
- 不正なリソースを終了
2〜4時間目:クリーンアップ
- BFGを使用してGit履歴からキーを削除
- クリーンアップされた履歴を強制プッシュ
- フォーク所有者に連絡して更新を依頼
- CI/CDシステムを更新
- バックアップシステムをクリア
2〜7日目:事後
- プレコミットフックを実装
- AWSパターンをgit-secretsに追加
- セキュリティトレーニングを実施
- 他のリポジトリの漏洩をレビュー
- インシデントと対応を文書化
このインシデントは、即座のローテーションが重要である理由を示している——攻撃者は数分以内に漏洩した認証情報を悪用する。
結論
Gitにコミットされた認証情報の管理には、削除だけでは不十分であることを理解する必要がある。Gitの不変な履歴は、漏洩した認証情報を無期限に保持し、リポジトリへのアクセス権を持つ誰もがアクセスできる。適切な対応は、Git履歴のクリーンアップよりも即座の認証情報ローテーションを優先する——攻撃者は数分以内に漏洩した認証情報を悪用するが、履歴の書き換えには数時間または数日かかる。
Git履歴から認証情報を削除するには、BFG Repo-Cleanerやgit filter-repoなどのツールを使用してコミットを書き換え、その後強制プッシュしてすべての共同作業者と調整する必要がある。しかし、フォーク、ミラー、バックアップシステムは元の履歴を保持する可能性があり、公開リポジトリの完全な削除は不可能である。この現実は、認証情報のローテーションが必須であり、オプションではないことを強調している。
重要な洞察:リポジトリの可視性に関係なく、Gitにコミットされた認証情報は完全に侵害されたものとして扱う。即座にローテーションし、履歴を徹底的にクリーンアップし、将来のインシデントを回避するために包括的な予防メカニズムを実装する。自動的に期限切れになる短期認証情報への移行を検討する——露出ウィンドウを制限することで、将来の漏洩の影響を劇的に軽減する。適切なシークレット管理の運用負担は、認証情報侵害のコストとインシデント後の対応の複雑さよりもはるかに少ない。