投稿

force-push しても GitHub に残る dangling commit と特定リスク

force-push しても GitHub に残る dangling commit と特定リスク

まとめ

  • git push --force で書き換えた旧 commit は GitHub 上に約 90 日残る(dangling commit)。SHA を直接指定すれば誰でも閲覧可能
  • private repo であってもリポジトリ所有者にアクセス権がある人なら旧 commit URL から内容を読める
  • 完全に消したいなら (1) GitHub のガベージコレクション待ち (2) サポート申請 (3) repo を delete → 再作成 のいずれか
  • 外部攻撃者からの SHA 特定は実質不可能。SHA 漏洩経路(CI ログ・shell history・webhook 等)の方が現実的なリスク

何が起きるか

リポジトリに会社固有のドメインを誤って含めて push してしまい、後から修正コミットを重ねた後、最終的に git checkout --orphan で履歴を 1 commit に潰して force-push した。

ローカルでは:

git log --all -p -S "jccapital" | wc -l
# → 0

完全にクリーン。だが GitHub 側では旧 commit SHA を直接指定すれば commit 内容が見える状態が残る:

https://github.com/<owner>/<repo>/commit/<old-sha>

これが dangling commit。どの branch・tag・PR からも到達不能なのに、SHA 経由で実体が残っている状態。

なぜ GitHub では消えないか

ローカル git では git gc を走らせれば dangling object は削除される。デフォルトでは reflog の保持期間(90 日)が経過するまでは保持されるが、git reflog expire --expire=now --all && git gc --prune=now で即時削除できる。

しかし GitHub は force-push 後も独自に旧 object を保持する。理由はおそらく:

  • 誤って force-push したケースのリカバリ
  • PR・issue・notification 等で参照されている SHA への解決保証
  • Forks(フォーク先で旧 commit が参照されている場合)

実体は GitHub のガベージコレクションが約 90 日後に走るまで残り続ける。

特定リスクの分解

旧 SHA を完全に知らない第三者が dangling commit にたどり着く現実的な経路を整理する。

SHA エントロピーで防げる範囲

項目
完全 SHA 40 hex 文字 = 160 bit
GitHub URL で受理される最短 4 文字(衝突しない範囲で)
短縮 7 文字の総当たり 約 2.7 億通り

private repo は GitHub 検索インデックスに載らないので、SHA を知らない外部攻撃者にとって特定は事実上不可能。

実際にあり得る漏洩経路

経路 大きさ
CI/CD ログ(GitHub Actions の各 step 出力に SHA が混入)
Webhook 連携先(Slack / Datadog / 監査基盤 等)の保管ログ
ローカルの shell history (~/.zsh_history)
他端末からの clone(.git/objects に旧 commit が残っている) 状況依存
アカウント侵害経由の git reflog 参照

逆に言うと、外部に SHA を流していない・他端末 clone がない・侵害がない限り、外部からの特定はゼロに近い。

確実に消す方法

方法 即時性 手間
GitHub の自動 GC を待つ 〜90 日 0
GitHub Support に明示申請して GC を早める 数日 サポート問い合わせ
repo を delete → 同名で再作成 → push 即時 clone 状況の確認のみ

private repo で他に clone した人が確実にいなければ、delete → 再作成が最速かつ最も確実。force-push の数十秒後にすべて消える。

教訓

  • force-push は「ローカル履歴」を書き換えるだけで、「リモートに到達した過去の commit」は別物として残る
  • 漏れたら困る情報は最初から commit しない(言うは易し)
  • やってしまった時の判断軸は「private か」「外部経路で SHA が流れたか」「許容できる秘匿性か」の 3 つ
  • 一律「force-push で消える」と思い込まないこと

GitHub の dangling commit はバグではなく仕様。書き換え後の commit が誰かの notification に残っている可能性を救うための設計だと思えば筋は通っている。が、秘密を消す目的で force-push を使うと期待が外れるので、用途に応じて消去方法を選ぶ必要がある。

トレンドのタグ