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

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をプリコミットフックとして実行すべきだと思う。完璧な解決策ではないけど、セットアップに少し時間を投資するだけで、秘密をうっかりコミットしないという合理的な保証が得られる。これがもっと広く標準的な実践として考えられていない理由がわからないな。

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

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

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

プライベートって呼ばれているけど、実際には自分がコントロールできない大企業と共有されているし、彼らがコントロールしていないインフラで動いている可能性が高い。CLOUD法の影響で、アメリカ政府とも共有されているんだ。

それは良くないアイデアだよ... - 現在プライベートなリポジトリに秘密をコミットして - 3年後にそれを共有/公開して - コミット履歴に秘密が残ってるのを忘れてしまって、まだ有効だし、(それに、長期間の秘密はあまり安全じゃない)確かに君にはそんなことが起こらないかもしれないけど、秘密をコミットする習慣をつけると、リスクがかなり高くなるよ。

後でリポジトリがプライベートでなくなるようなこと(意図的な公開、ハッキング(リポジトリだけでなく、接続できるもの全般)など)があったら、その秘密はオープンになっちゃう。ソース管理にコミットした後は、必ず資格情報をローテーションしてね。すぐにやらないと忘れちゃうから。たとえリポジトリが絶対に公開されることがないと100%確信していても、そういう習慣をつけるのは良いことだよ。

大学を卒業して最初の仕事の時、何年も前に、うっかり内部のGitリポジトリにプライベートキーをコミットしちゃったんだ。それを削除したけど、将来的にそのリポジトリが顧客や世界に公開される可能性を完全には排除できなかったからね。フィルターレポを使って、どこからでもキーを取り出したと思う。

会社の中で、従業員によって権限が違うんだよね。アクセス権が多い従業員が秘密をコミットしちゃうと、そんなにアクセス権がない従業員がその秘密を持ち出して使っちゃう可能性がある。

GitHubは、私たちが知る限り、これらのダングリングコミットを永遠に保持する。カスタマーサポートに連絡してリポジトリをガーベジコレクトしてもらうと、そうでもないよ。私がうっかり公開したくないものをプッシュしたときにやることはこんな感じ: - 強制プッシュ; - 秘密鍵みたいなものであればすぐにローテーション; - カスタマーサポートに連絡してリポジトリをgcしてもらう(その後、コミットが消えているか確認する)。 (もちろん、プッシュした瞬間にダメージを考慮すべきだよ。上記のステップは、さらなるダメージを最小限に抑えるためのものだからね。)

秘密をローテーションしたなら、他に何かする必要ある?さらなるダメージはないと思うよ( reputationalなものを除いて)。

Gitはその孤立したリファレンスをクローンしないよね?

これ、私もやっちゃったことある。プレゼンの30分前に、ホスティングサービスからキーが取得できない理由がわからなくて、結局キーをハードコーディングしちゃった。プレゼンの後にキーはローテーションしたけど、リポジトリにはあまり良い印象を与えないよね。

残念ながら、Googleアカウントを持っていない私たちには、言及されているSQLiteデータベース(force_push_commits.sqlite3)をダウンロードするのに必要みたいだね。

心配だな。面白い研究だけど、Truffle Securityはこのメールアドレスをリードジェンやマーケティング目的で使うのかな?XSS Hunterのフォークからユーザーのピンバックを統計用に掘り出したみたいに。 https://portswigger.net/daily-swig/new-xss-hunter-host-truff...

こういうファイルにアクセスするのはよくあるハードルだよね。LeadsAppをチェックしてみるといいかも。Googleログインなしでデータを整理する手助けをしてくれるよ。似たような問題を整理するのが楽になった。

で、質問なんだけど、コミットを孤立させた後、どうやって本当にGitHub上で見えなくするの?カスタマーサポートに連絡してGCを頼む以外に方法はないの?リポジトリの「危険ゾーン」エリアにボタンがあってもいいんじゃないかな。

できないと思っておいた方がいいよ。サポートに連絡しても、何かが行われる頃には誰かがアーカイブしてるだろうし。インターネットデータストレージのマーフィーの法則が適用されるからね。何かをインターネットに投稿したら、それは永遠に公開されるし、一度見たことがある情報を再度探そうとしても、リンクが腐ってて永遠に失われてるよ。

時間が経つにつれて「おっと」と言うのに飽きて、環境変数を使いまくるようになったよ。もし10秒で設定するだけの規律があれば、魔法の文字列がソース管理に吸い込まれる心配はなくなる。環境変数のもう一つの利点は、プロジェクトを跨いで使えること。名前を覚えておけば、設定して忘れられるしね。OpenAIやAWS、GHなどのトークンを取得するのは、もう私のマシンでは解決済みの問題だよ。ただ、多くの開発者がこれをやらない理由も分かる。特にWindowsでは、これらの管理画面にアクセスするのに結構面倒なクリックが必要だからね。秘密をコードに貼り付ける方が(相対的に)ずっと早いし。この手のちょっとした怠惰が、気をつけないと本当に積み重なっていくよ。

私は、秘密の文字列をマスターパスワードで暗号化してる。マスターパスワードはTPMモジュールか、絶対にGitリポジトリに追加しない「MASTER_SECRET」という名前のファイルに保存してる。新しいプロジェクトの標準スクリプトでは、このファイルを.gitignoreに追加して、誤ってコミットされないようにプリコミットフックを使ってるよ。

使いまくる? 環境変数はまさにそのためにあると思ってたけど。

長い間、そして今でも、Google AppEngineはYAMLに秘密を保存することを推奨してたんだ。これがうっかりgitコミットされるのが簡単なんだよね。Herokuなどとは違って、サービスに秘密を渡す簡単な方法がないから、環境変数に入れるのはいつも一つのコマンドで済むんだ。前回試したときは、デフォルトの提案がCloud KMS(そうだね)だったけど、今は新しい秘密管理者も出てきて、これも面倒そうだよね: https://stackoverflow.com/questions/58371905/how-to-handle-s...

それと、略奪的な価格モデルについて話せる? AWSでは、ある秘密サービスが秘密を月0.4ドルで提供してるんだ。初めて見たときは驚愕したよ。12バイトのデータを保存するのに年間5ドルも取るの?