コンテンツにスキップ

GitHubに機密情報を誤って公開した時の完全対処ガイド

公開リポジトリでは数分以内にボットがスキャンする

機密情報がGitHubに公開された場合、即座に対処が必要。

この記事のポイント

  • 最優先: 鍵の無効化 漏洩した認証情報を即座に無効化し、被害拡大を防ぐ
  • 履歴からの完全削除 git filter-repo / BFGで全コミットから機密情報を除去
  • 再発防止 .gitignore、プリコミットフック、Secret Scanningで予防

事故の典型パターン

OpenAI APIキーを含む config.json を誤ってpush:

{
  "openai_api_key": "sk-abcd1234...",
  "database_url": "postgresql://user:password@localhost/db"
}

結論: 鍵の無効化が最優先。 Gitの履歴削除より先に、漏洩した認証情報を該当サービスの管理画面で無効化する。履歴を消している間にも悪用される可能性がある。急いでいるならフェーズ1へ。


緊急対処手順

フェーズ1: 機密情報の無効化(0-5分)

ここが最も重要。 Git操作より先に実行する。

漏洩した情報対処
APIキー(AWS, OpenAI, Stripe等)サービスの管理画面で即座に削除・再発行
データベース認証情報パスワードを即座に変更、該当ユーザーの権限を一時停止
SSHキー・証明書失効させ、再生成
GitHub Personal Access TokenSettings → Developer settings → 該当トークンを削除

パブリックリポジトリの場合は、並行してリポジトリをプライベートに変更(Settings → Change repository visibility)。

関係者やセキュリティ担当者への通知も速やかに行う。

フェーズ2: Git履歴からの完全削除(5-30分)

Step 1: 現在のブランチから追跡を解除

# ファイルをGitの追跡から除外(ローカルファイルは保持)
git rm --cached config.json

# .gitignoreに追加
echo "config.json" >> .gitignore

# コミット・プッシュ
git add .gitignore
git commit -m "Remove config.json from tracking"
git push origin main

これだけでは不十分

git rm --cached は最新コミットからファイルを削除するが、過去のコミット履歴には残る。完全削除にはStep 2が必要。

Step 2: 全履歴から削除

3つのツールから選ぶ。

ツール位置づけ特徴
git filter-repo推奨Pythonベース。高速で安全。Git公式ドキュメントが推奨
BFG Repo-Cleaner代替手段Javaベース。パスワード文字列の一括置換に便利
git filter-branchレガシーGit 2.36以降で非推奨警告が出る。新規利用は避ける

方法A: git filter-repo(推奨)

# インストール
pip install git-filter-repo

# 特定ファイルを全履歴から削除
git filter-repo --invert-paths --path config.json

# 複数ファイルを削除
git filter-repo --invert-paths \
  --path config.json --path secrets.yml

git filter-repoの注意点

  • fresh clone以外で実行すると警告が出て停止する。--force オプションで回避可能
  • リモートの設定を削除するため、実行後に git remote add origin <URL> で再設定が必要

方法B: BFG Repo-Cleaner

# ダウンロード
# 最新版は公式サイトで確認:
#   https://rtyley.github.io/bfg-repo-cleaner/
java -jar bfg.jar --delete-files config.json .git

# パスワードやAPIキーの文字列を置換
java -jar bfg.jar --replace-text passwords.txt .git

BFGの制約

BFGはHEADコミットのファイルを保護する設計。先にStep 1で git rm --cached + コミットを済ませてから実行する。

方法C: git filter-branch(レガシー・非推奨)

Git 2.36以降で非推奨警告が表示される。既存のCI/CDスクリプト等で使われている場合の参考として残す。新規には git filter-repo を使うこと。

git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch config.json' \
  --prune-empty --tag-name-filter cat -- --all

Step 3: クリーンアップと強制プッシュ

BFGまたはfilter-branchを使った場合は、ローカルの参照を消去する。git filter-repo は内部でこの処理を行うため不要。

# BFG / filter-branch 使用時のみ必要
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# リモートに強制プッシュ(⚠️ チームに事前通知必須)
git push origin --force --all
git push origin --force --tags

フォークには反映されない

強制プッシュはフォークには伝播しない。フォークが存在する場合は、オーナーに連絡するかGitHub Supportに削除を依頼する。

Step 4: 削除の確認

# 履歴にファイルが残っていないか確認
git log --all --full-history -- config.json

# 特定の文字列が履歴に残っていないか確認
git log -S "sk-abcd1234" --all --oneline

フェーズ3: チーム復旧と新しい認証情報

強制プッシュ後、チームメンバーは以下を実行:

git stash                        # 作業中の変更を退避
git fetch origin
git reset --hard origin/main
git stash pop                    # 必要に応じて作業を復元

新しい認証情報を発行し、テンプレートファイルをリポジトリに含める:

cat > config.json.example << 'EOF'
{
  "openai_api_key": "YOUR_API_KEY_HERE",
  "database_url": "YOUR_DATABASE_URL_HERE"
}
EOF

git add config.json.example
git commit -m "Add config template"
git push origin main

予防策

.gitignoreの設定

# 機密情報ファイル
*.secret
*.key
*.pem
.env
.env.local
.env.production
config.json
secrets.yml
credentials.json

# クラウド認証情報
.aws/
.gcp/

プリコミットフック

#!/bin/sh
# .git/hooks/pre-commit
# chmod +x .git/hooks/pre-commit で実行権限を付与

SENSITIVE_PATTERNS=(
    "sk-[a-zA-Z0-9]{20,}"
    "AKIA[0-9A-Z]{16}"
    "glpat-[a-zA-Z0-9]{20}"
    "api_key.*=.*['\"][a-zA-Z0-9]{20,}['\"]"
)

for pattern in "${SENSITIVE_PATTERNS[@]}"; do
    if git diff --cached --diff-filter=ACMR --name-only -z \
        | xargs -0 grep -l -E "$pattern" 2>/dev/null; then
        echo "❌ 機密情報の可能性がある文字列が検出されました"
        echo "パターン: $pattern"
        exit 1
    fi
done

echo "✅ 機密情報チェック完了"

git-secrets

# インストール(macOS)
brew install git-secrets

# リポジトリで有効化
git secrets --install
git secrets --register-aws

# カスタムパターン追加
git secrets --add 'sk-[a-zA-Z0-9]{20,}'

# 全履歴をスキャン
git secrets --scan

GitHub Secret Scanning

GitHubはAWS、OpenAI、Stripe等の主要な機密情報パターンを自動検出し、Secret scanning alertsを送信する。

Public / Privateリポジトリの違い

  • Publicリポジトリ: 無料で自動有効。設定不要で検出される
  • Privateリポジトリ: GitHub Advanced Security(Enterprise向け有料機能)が必要。個人やTeamプランでは利用不可

Secret Scanningに依存せず、プリコミットフックや git-secrets と併用するのが安全。

GitHub Actionsでの機密情報管理

# .github/workflows/deploy.yml
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy with secrets
        env:
          API_KEY: ${{ secrets.API_KEY }}
        run: |
          echo "Deploying..."

法的考慮事項

GDPRの72時間報告義務の適用範囲

GDPRの72時間以内の監督当局への報告義務は、自然人の個人情報(氏名、メールアドレス、住所等)が漏洩し、その権利・自由にリスクをもたらす場合に適用される。

報告が必要なケース: 個人情報を含むDBダンプ、ユーザーリスト、メールアドレス一覧をpushした場合など。該当する場合はデータ保護責任者(DPO)に即時報告する。

通常該当しないケース: APIキー、パスワード、SSHキー単体の漏洩。ただし、これらを通じて個人情報にアクセスされた可能性がある場合は報告を検討する。


よくある質問

Q: git rm --cachedだけで十分?

不十分。 git rm --cached は最新コミットからファイルを除去するが、過去の履歴にはそのまま残る。git filter-repo や BFG で全履歴から削除する必要がある。

Q: git filter-repoとBFGのどちらを使うべき?

git filter-repo が推奨。 Git公式ドキュメントが git filter-branch の代替として推奨しており、高速かつ安全。BFGはパスワード文字列の一括置換(--replace-text)が便利な場面で使う。git filter-branch は非推奨のため新規には使わない。

Q: フォークにも機密情報が残る?

残る。 強制プッシュはフォークには反映されない。フォークのオーナーに連絡するか、GitHub Supportに削除を依頼する必要がある。

Q: Secret Scanningで自動検出されるから大丈夫?

過信は禁物。 Secret ScanningはPublicリポジトリでのみ無料で自動有効。Privateリポジトリでは有料のGitHub Advanced Securityが必要。また、検出パターンに含まれない独自の機密情報は検出されない。プリコミットフックや git-secrets との併用を推奨。


関連記事

blobの残存は復元に使えるが、逆に削除したいものが残り続ける問題にもなる。 ステージ済み変更の復元方法はgit fsckでステージ済みファイルを復元する方法を参照。