世界を動かす技術を、日本語で。

GitHubの「oopsコミット」をすべてスキャンして漏洩した秘密を探しました

概要

  • GitHub では公開リポジトリの全てのコミットが 削除後も記録 されている現状
  • Force push で消したはずのコミットも GH Archive に「Zero-Commit」PushEventとして残存
  • Truffle Security と共同で、これら隠れたコミット内の シークレット検出ツール をOSS化
  • APIやGH Archive の活用により、全削除コミットの効率的なスキャンが可能
  • バグバウンティ やサプライチェーンリスク対策に直結する重要性

GitHubで「削除したはずのコミット」が消えない理由とリスク

  • GitHub は全ての公開コミットを アーカイブ し、削除操作後も永続的に保存
  • Force push (強制プッシュ)でミスやシークレット情報流出を隠そうとしても 完全消去不可
  • GH Archive プロジェクトにより、全PushEventが 自動記録 されている実態
  • Zero-Commit PushEvent は「HEADを過去に戻すだけ」の操作で、 コミットが消えたように見える
  • しかし コミットハッシュ を知っていれば、GitHub上で 内容を復元・閲覧可能

コミット削除の仕組みとGitHubの保存構造

  • git reset --hard HEAD~1git push --force で過去のコミットを参照不能状態に
  • しかし GitHub は内部的に すべてのコミット を保持し、 reflog や多層的な仕組みで消去しない
  • Pull RequestFork、監査・複製・監視など多様な用途で 履歴保存 が必須
  • そのため Force push 後も、 該当コミット は「削除済み」扱いで Web/API経由で取得可能

GitHub Event APIとGH Archiveの活用

  • GitHub Event API は全公開リポジトリの イベント情報 をリアルタイムで取得可能
    • 認証不要で利用可能
    • PushEventIssue作成 など多様なイベントをカバー
  • GH Archive はGitHub全イベントを アーカイブ し、 BigQuery 等で横断検索が可能
    • 過去の全PushEventデータを 日付単位でダウンロード 可能
  • Zero-Commit PushEvent を抽出し、「before」コミットをスキャン対象として特定

削除コミットからのシークレット発見と自動化

  • TruffleHog 等のツールで、 削除されたコミット に埋もれた シークレット情報 を検出
    • 例:APIキー、パスワード、証明書など高額バグバウンティ対象
  • GitHub API やWeb UI経由で、 コミットハッシュ さえ分かれば内容取得が可能
    • APIは レートリミット あり(登録ユーザー5,000回/時、未登録60回/時)
  • 自動化スクリプト やOSSツールで、 Zero-Commit PushEvent を効率的に横断検索・検出
    • Truffle Security との共同開発で、 組織単位の全削除コミットスキャンツール をOSS公開

事例と影響

  • $25,000相当 のバグバウンティシークレットを 削除コミット から発見
  • サプライチェーンリスク情報漏洩 の早期発見・防止に直結
  • Force push 後も「完全消去」できないGitHubの特性を理解し、 運用・監査体制強化 が必須

推奨対策とまとめ

  • シークレット管理 の徹底(コミット前スキャンや自動検出ツールの活用)
  • Force push による履歴消去は 根本的な解決にならない 認識の徹底
  • 公開リポジトリ の運用者は GH ArchiveZero-Commit検出ツール の活用推奨
  • 定期的な監査自動化ツール による継続的な監視体制の構築
  • OSSツール によるセルフチェックで、 組織・個人のGitHubリスク低減 を実現

参考リンク・ツール


まとめ

  • GitHub でコミットを削除しても 履歴は消えない
  • Force push や履歴改変操作では 情報漏洩リスク は排除できない
  • GH ArchiveZero-Commit検出ツール過去の削除コミット も監査・調査可能
  • シークレット検出の自動化継続的な運用管理 が安全な開発運用の鍵

Hackerたちの意見

デフォルトの安全な削除オプションを、もっと簡単にできるイベントにするのはどうかな?イベントにチェックアウトして、クリーンな状態でコミットして、前のログ履歴を残しておいて、エリジョンの後にその状態を上書きして、Gitリポジトリを置き換える感じ。ログを保持して状態をエリジョンする必要があった時、RCSでこんなことをやってたんだ。日付や時間の情報を正しく取得するのが難しかったけどね。

秘密を公開してしまったら、それは漏洩したと考えるべきだよ。GitHubでは、監視されていないリポジトリだと5分、監視されているリポジトリだと30秒以内に取り消さないと、第三者にクローンされてアーカイブされちゃう。相手が悪意のある人かどうかに関わらず、Gitの履歴を書き換えても秘密が漏れた事実は変わらないよ。ほとんどのプロバイダーでは、Gitの履歴を再構築して、もうツリーに含まれないコミットをガーベジコレクトすることはできるからね。

もし何かがインターネットに出てしまったら、もう取り戻せないよ。秘密を公開してしまったら、リポジトリの履歴を書き換える意味はほとんどない。できるだけ早く秘密を変更するのが一番だね。

プッシュしたものは漏洩したと考えるべきだよ。コミットを残して、秘密を無効にしてしまった方がいいかもね。

もしかしたら見逃したかもしれないけど、この記事にはもっと簡単にこれを見る方法、アクティビティタブのことが書いてないね。そこにはすべてがあるんだ。醜いプロトタイプコードを隠すための強制プッシュは永遠に残るから、ちょっとイライラする。そこから削除できればいいのに、サポートにメールするしかないみたい?ここにテストリポジトリのアクティビティがあるよ https://github.com/SharonBrizinov/test-oops-commit/activity

それはどこからリンクされてるの?何年もGitHubを使ってるけど、このページのことは聞いたことないな。

面白いことに、過去に私たちのデプロイメントの一つで似たような問題があったよ。パスワードをバッシュの履歴にうっかり漏らすのに似てる。もっと起こるべきじゃないのにね。

完全に削除するのは、リポジトリ全体を削除して再アップロードすることで可能だと思う。フォークがなければね。

すべての開発者は、ローカルシステムのすべてのリポジトリに対してオープンソースのtrufflehogをプリコミットフックとして実行すべきだと思う。完璧な解決策ではないけど、セットアップに少し時間を投資するだけで、秘密をうっかりコミットしないという合理的な保証が得られる。これがもっと広く標準的な実践として考えられていない理由がわからないな。

プリコミットフックはクライアントサイドだけで、オプトインなんだ。私はずっとプリコミットフックの大ファンで、問題を早く見つけるほど修正が安く済むと思ってるけど、時間が経つにつれて、例えばユニットテストを実行するプリコミットフックはどんどん時間がかかるようになってきて、急いでコミットしたい人もいるんだよね。

正直言うと、これが職場でどれくらい起こっているのか分からないし、もし起こっても世界の終わりってわけじゃないよ。ただ、そのコミットを消しちゃえばいいんだ。私の中では、秘密をうっかり共有しちゃう人って、プリコミットでトリュフホッグを設定できない人でもあると思ってる。

私がずっと理解できなかったのは、これがプライベートリポジトリでの問題になるのはどうしてなのかってこと。オープンソースプロジェクトを除けば、うっかりやってしまうことに問題はないと思うんだけど、匂いはするけどね。

Hacker Newsで議論の続きを見る