【GitHub・コードリポジトリ偵察編】コミットに眠るシークレットを掘る|CTF思考フレームワーク R07
こんにちは、アンペンです!偵察ルートR07はGitHub偵察編。公開リポジトリのコミット履歴に眠るAPIキー・パスワード・内部URLを掘り出す技を扱います。ここはシャドーIT(R06)の宝庫で、「消したつもりのコミット」も履歴に残るのが最大のポイントです。
GitHubは、世界中の開発者がコードを公開し合う便利な場所。でもその“公開”の裏で、本来は隠すべき秘密——APIキーやパスワード——が、毎日のように意図せず流出しています。しかも怖いのは、それが高度なハッキングの結果ではなく、開発者の“ついうっかり”で起きること。今日は、その“うっかり”がどう漏れ、なぜ消しても消えないのか、そしてどう防ぐのかを見ていきましょう。

パスワードをコミットしても、次のコミットで消せば大丈夫でしょ?

それが一番危ない誤解。gitは全履歴を保持するから、git logを遡れば消したはずの秘密が丸ごと復活するんだ。だから漏れたら『削除』じゃなく『鍵の無効化(ローテーション)』が正解だよ。
GitHub偵察とは、公開リポジトリからシークレット(APIキー・パスワード・秘密鍵・内部URL)を見つける受動偵察。核心は「gitは全履歴を保持する」——後で削除したコミットもgit logで復元できる。探索は(1)GitHubコード検索(GitHub Dork)、(2)trufflehog/gitleaksで履歴全走査、(3)有効性の確認の3段。フォーク・Gist・CIログ・Dockerイメージにも漏れる。守る側の鉄則は『コードに秘密を書かない+.gitignore+pre-commit検出+Secret Scanning、漏れたら削除でなくローテーション』。一度ネットに出た鍵は「漏れた鍵」として扱う。
この記事で分かること
- 公開リポジトリに何が漏れるか(シークレットの種類)
- 「消したコミット」が残る理由(git履歴の仕組み)
- trufflehog/gitleaks/GitHub Dorkの使い分け
- 漏えい時に「削除」でなく「ローテーション」が必須な理由
- 守る側の水際対策(gitignore/pre-commit/Secret Scanning)
📖 はじめてのWebセキュリティR07|GitHub偵察編
コミット履歴に眠るシークレットを掘り出す受動偵察。 シリーズ一覧を見る →
⚠️ 大事なお約束
他者のリポジトリから見つけた認証情報を実際に使えば不正アクセスです。発見しても使わない・通報する(責任ある開示)。本記事の走査は自分のリポジトリ・CTF・許可された対象でのみ実施してください。
公開リポジトリに漏れるもの
ソースコードは「動かすための情報」を含むため、開発者がうっかり認証情報を混入させがちです。代表的な漏えい物を並べます。
- クラウドのAPIキー:AWS(
AKIA...)・GCP・Azureの鍵。課金乗っ取りや内部侵入の起点 - SaaSトークン:Slack・Stripe・GitHub PAT・SendGrid等。連携先を芋づる式に侵害
- DB接続文字列:
postgres://user:pass@host/dbがそのまま直書き - 秘密鍵:
-----BEGIN RSA PRIVATE KEY-----、.pem、SSH鍵 - 内部情報:社内ホスト名・管理画面URL・テスト用アカウント・コメントに残る注意書き
なぜコードに秘密が紛れ込むのか。理由はシンプルで、プログラムを“動かす”には、これらの情報が要るからです。クラウドに繋ぐにはAPIキー、DBに繋ぐにはパスワード——手元では確かに必要。問題は、それを“コードに直接書いたまま”公開リポジトリに上げてしまうこと。本人は『あとで消せばいい』と思いがちですが、その油断こそが、次に説明する最大の落とし穴につながります。

ここで覚える用語:シークレットとハードコーディング
意味:シークレットとはAPIキー・パスワード・トークン・秘密鍵など、漏れると即被害につながる認証情報。ハードコーディングはそれをソースコードに直書きすることです。
例:AWS_SECRET_KEY = "wJalrXUtn..." をスクリプトに直書きしてコミットしてしまう。
使いどころ:鉄則は「シークレットはコードと分離」。環境変数やSecrets Manager/Vaultから読み込み、リポジトリには絶対に置かない。これだけで漏えいの大半は防げます。
なぜ「消したコミット」が残るのか
gitは「スナップショットの連鎖」で、過去のコミットを丸ごと保持します。.envを一度コミットして次のコミットで削除しても、削除という変更が新たに記録されるだけで、過去の中身は履歴に残ったまま。git log -pやgit show 過去ハッシュで誰でも復元できます。さらに、その時点でフォークされたりクローンされていれば、あなたが履歴を消しても他人の手元のコピーには残り続けます。
ここ、知らない人が聞くと一番ギョッとするところです。『コミットして、次のコミットで消したから大丈夫』——これは完全な誤解。gitは“変更の履歴を全部とっておく”仕組みなので、消したつもりの秘密は、過去のページとしてそっくり残っています。git logを遡れば誰でも読める。さらに、誰かがその時点でコピー(フォーク)していたら、あなたが履歴を消しても、相手の手元には永遠に残る。だから『消す』は、解決にならないんです。
鉛筆で書いて消しゴムで消しても、強い光に当てれば筆圧の跡(=git履歴)が残っていて読めてしまいます。しかも、その日記をうっかりコピーして友人に渡していたら(=publicリポジトリやフォーク)、自分の手元で消しても友人の手元のコピーには全部残っている。だから対策は二つしかありません。「最初から秘密を日記に書かない」、書いてしまったら「その秘密の鍵自体を使えなくする(=ローテーション)」。消すこと自体は解決にならないのです。

ここで覚える用語:git履歴とローテーション
意味:gitは全変更履歴を保持するため、一度コミットした秘密は後から消しても復元可能。だから漏えい時は削除でなく鍵の無効化(ローテーション)が必須です。
例:trufflehogが「3ヶ月前に消したはずのStripeキー」を履歴から発見。そのキーがまだ有効なら、今この瞬間も悪用できる状態です。
使いどころ:漏れた瞬間に「そのキーは死んだ」前提で即ローテーション。履歴のクリーンアップ(git filter-repo)はあくまで補助で、本命は鍵の無効化です。
探索ツールの使い分け
- GitHubコード検索(GitHub Dork):
org:会社名 "password"filename:.envextension:pemでブラウザから露出確認 - trufflehog:エントロピー+正規表現で履歴を全走査。削除済みコミットも検出し、キーの有効性まで検証
- gitleaks:正規表現ベースで高速。CIに組み込みやすく、pre-commitにも使える
- 監視範囲を広げる:GitLab・Bitbucket・Gist・公開Dockerイメージ・npm・CI/CDログも漏えい源
GitHub Dorkは、いわば“社内検索を外部から叩く”ようなもの。org:会社名 "password" のように会社名と危ないキーワードを組み合わせれば、その組織のリポジトリから怪しい箇所を一気に拾えます。trufflehogやgitleaksは、それをさらに自動化して“過去の全履歴”まで掘ってくれる道具。攻撃者はこれを使って待ち構えているので、守る側は『同じ検索を、先に自分でやる』のが鉄則になります。
CTFでやってみよう:自分のリポジトリを走査
「自分のコミット履歴に秘密が残っていないか」点検する
攻撃者がやる走査を、自分のリポジトリに先回りで実行します。各ステップに「なぜやるか」を添えました。
- 自分のpublicリポジトリに
trufflehog git <repo>を履歴全走査でかける → なぜ:過去コミットの漏れを掘り起こすため gitleaks detectも併用する → なぜ:ツールで検出傾向が違い取りこぼしを減らせるから- GitHubで
org:自分 "password"やfilename:.envを検索 → なぜ:GitHub Dorkでの露出を確認するため - 見つかったシークレットがまだ有効かを確認 → なぜ:有効なら今まさに悪用可能だから
- 有効な鍵は削除より先に即ローテーション → なぜ:履歴に残るので無効化が本質だから
.gitignoreに.env/*.pem/credentials.jsonを追加 → なぜ:再発防止の第一歩だから- pre-commit hookに
gitleaksを仕込む → なぜ:コミット前に自動で止めるため - GitHubのSecret Scanning+Push Protectionを有効化 → なぜ:プラットフォーム側の防壁を足すため
では守り方。結論を先に言うと、いちばん強いのは『最初から書かない』です。漏れてから消す・無効化するのは、あくまで火事になってからの消火。それより、そもそも燃えるものを置かない——秘密はコードと分け、環境変数や金庫(Secrets Manager)から読む。次のリストは、その“入口で止める”を軸に並べています。
守る側:書かない・止める・無効化する
- コードに秘密を書かない:環境変数/Secrets Manager/Vaultから読む
- .gitignoreで確実に除外:
.env/*.pem/credentials.json等 - pre-commit hookで水際遮断:
gitleaks/git-secretsでコミット前に検出 - Secret Scanning+Push Protection:GitHub側でpushを自動ブロック
- 漏れたら削除より先にローテーション:履歴に残る前提で鍵を無効化
- リポジトリ可視性の点検:public/privateの誤設定を定期確認
- 監視範囲を広げる:フォーク・Gist・CI/CDログ・Dockerイメージも対象
- 最小権限+短命トークン:漏れても被害を最小化できる設計に


「最初から書かない」が一番強い対策なんだね。

そう、入口で止めるのが最強。次はR08、メタデータ・人物偵察。画像のExifや文書のプロパティ、SNSから人物像を立ち上げるOSINTの作法を扱うよ。
まとめ:『履歴は消えない』前提で守る
- 公開リポジトリはAPIキー・DB接続・秘密鍵の宝庫
- gitは全履歴を保持=消したコミットも復元できる
- 探索はGitHub Dork+trufflehog/gitleaksの履歴全走査
- 漏えい時は削除でなくローテーションが必須
- 守りは書かない・gitignore・pre-commit・Secret Scanning
今日の持ち帰りは『git履歴は消えない、だから“漏れた鍵は死んだ鍵”』。一度ネットに出た認証情報は、削除しても出回った前提で、すぐ無効化する。そして何より、攻撃者と同じツールで自分のリポジトリを一度走査してみること。『3年前に消したはずのキーが、まだ生きていた』——そんな発見が、被害を未然に防ぎます。
次はR08、メタデータ・人物偵察編。コードの次は「ファイルと人」に残る痕跡を読み解きます。
