5/13 ローンチ予定!
PiloTube

PiloTube 開発日誌

← 「ひとり社長のAI開発記」一覧へ

gitの履歴には一度入ったら消えない——878ファイルの顧客情報削除作戦

約7分で読めます
セキュリティgit失敗談

gitリポジトリから顧客情報878ファイルを完全消去した話

878ファイル。それが、自分のローカルgitリポジトリに静かに眠り続けていた「消すべきもの」の数だった。顧客名、単価、担当者名。全部入ってた。


気づいたのは、ふとした瞬間だった

PiloTube(パイロチューブ)の開発作業中、ふとリポジトリの中身をざっと見返していた。そのときに目に入ったのが、明らかに「コードじゃないもの」が混じっているファイル群だった。

CSVファイル。クライアント名。月額単価。担当者のフルネーム。

「……あ、これ入ってる」

正直、最初は現実逃避したくなった。「たぶん大丈夫だろう」と思いたかった。でも冷静に考えると、全然大丈夫じゃない。gitというのはコミット履歴(過去の変更記録)を全部持っている。つまり、今そのファイルを削除しても、過去のコミットを遡れば普通に復元できる。ファイルを消すだけでは意味がない。履歴ごと消さないといけない。


問題の構造を整理する

まず状況を整理した。

  • ローカルのgitリポジトリに顧客情報が含まれたファイルが混入している
  • ファイルはすでに複数のコミットにまたがって記録されている
  • リモートリポジトリ(GitHubなど)への push は確認した限りしていない
  • ただしローカルに履歴が残っている以上、漏洩リスクはゼロではない

幸いだったのは、リモートへのpushがなかったこと。これが逆だったら、もっと深刻な対処が必要になっていた。ただ、安心している場合でもない。ローカルとはいえ、チームで共有するようなことがあれば即アウトだ。


git filter-branch を使って履歴ごと消す

調べた結果、使うべきコマンドは git filter-branch だった。

git filter-branch とは、gitの全コミット履歴を書き換えるコマンドのこと。特定のファイルを「最初からなかったことにする」ような処理ができる。強力な分、使い方を間違えると履歴が壊れる。慎重にやる必要があった。

実行したコマンドはこういう形だ:

git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch path/to/customer-data/' \
  --prune-empty --tag-name-filter cat -- --all

--index-filter でファイルを履歴から除外し、--prune-empty で中身が空になったコミットを削除、--all で全ブランチ・全タグに適用する。

ここまでは教科書通り。問題はこの後だった。


VS Codeが暴走した

コマンドを実行して、処理が走り始めた。

878ファイルを全コミット履歴から削除していくわけだから、当然それなりに時間がかかる。ターミナルで処理が流れているのを眺めていたら、突然VS Codeが激重になった。

ファイルエクスプローラーがぐるぐると更新を繰り返し始めた。gitの履歴が高速で書き換えられていくのを、VS CodeのGit拡張機能がリアルタイムで検知しようとして、処理が追いつかなくなっていた。

CPUファンがうなり始めた。マウスカーソルが止まった。

「あ、これまずいやつだ」

VS Codeを強制終了しようとしたが、それすら重くて反応しない。結局、しばらくそのまま放置するしかなかった。5分ほど待ったら、filter-branch の処理が完了してVS Codeも落ち着いた。ただ、この間はひたすら「壊れないでくれ」と念じていた。


処理完了後にやったこと

filter-branch が終わったあと、残作業がある。

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now --aggressive

これをやらないと、filter-branch が書き換えた元の履歴が .git/refs/original/ にバックアップとして残ってしまう。つまり、消したつもりのデータがまだ残っているという状態になる。git gc(ガベージコレクション、不要なデータを掃除するコマンド)まで実行して、ようやく完全消去が完了する。

最後に git log で履歴を確認し、問題のファイルが全コミットから消えていることを確認した。

878ファイル、全消去。


なぜこんなことが起きたのか

正直に振り返ると、原因はシンプルだった。

.gitignore の設定が甘かった。

.gitignore とは、gitの管理対象から除外するファイルやフォルダを指定するファイルのこと。CSVファイルや特定のディレクトリを最初から除外しておけば、こんな事態にはならなかった。

開発初期の「とりあえず動かす」フェーズで、.gitignore の整備を後回しにしたツケが回ってきた形だ。PiloTubeは顧客データを扱う業務的な側面もあるサービスなので、この手の管理は最初から厳しくやっておくべきだった。


読者への教訓とヒント

この経験から持ち帰ってほしいことを整理する。

1. gitに入れてはいけないものを最初に決める

プロジェクト開始時に .gitignore を整備する。CSVファイル、環境変数ファイル(.env)、認証情報が入ったファイルは、最初から除外設定しておく。テンプレートとして gitignore.io を使うと、言語・フレームワーク別の雛形が手に入る。

2. 「ファイルを消す」と「履歴から消す」は別の話

git管理下のファイルは、削除しても履歴に残る。機密情報が混入してしまった場合は、git filter-branch か、より新しいツールである git-filter-repo を使って履歴ごと消す必要がある。git-filter-repo の方が速くて安全なので、これから対処する人はそちらを使うことを勧める。

3. filter-branch 実行中はVS Codeを閉じておく

これは今回身をもって学んだ。大量のコミットを書き換える処理中にVS CodeのGit拡張機能が動いていると、PCがかなり重くなる。実行前にVS Codeを閉じるか、少なくともGit拡張機能を無効にしておくと安全だ。

4. リモートへのpushがなかったことを必ず確認する

もしGitHubなどに push 済みだった場合、git push --force で上書きする必要がある上に、GitHubのキャッシュにデータが残る可能性もある。その場合はGitHubのサポートに連絡して、キャッシュの削除を依頼する必要がある。ローカルだけで済んでいたのは、今回の唯一の救いだった。


878ファイルの消去作業自体は1時間かからなかった。ただ、「なんで自分はこんな初歩的なミスをしたんだ」という自己嫌悪は、もう少し長く続いた。

PiloTubeは顧客情報を扱うサービスだからこそ、こういう管理の甘さは絶対に繰り返せない。今回の件を機に、全プロジェクトの .gitignore を見直した。地味な作業だけど、これが一番の再発防止策だと思っている。

チャプター生成AI

URL貼るだけ。AIがチャプターを自動生成。

1

YouTubeのURLをコピーして貼る

2

「生成する」を押す

3

概要欄にコピペして完了

無料でチャプターを生成する →

月3回まで無料 · クレジットカード不要