個人開発で秘密鍵をgit pushしかけた話 — 二度と事故を起こさない仕組みの作り方
git pushした後に冷や汗をかいたことはありませんか?
結論から言います。.gitignoreだけでは秘密情報の漏洩は防げません。 pre-commitフックを入れれば、うっかりミスを物理的に不可能にできます。設定は3分で終わります。
この記事では、筆者が実際にセッションCookieをpushしかけた体験と、その後に構築した「二度と事故が起きない仕組み」を全部公開します。
私の体験談
筆者はClaude Crewという11体のAIエージェントを運用しています。そのsns-automationリポジトリで、noteのセッションCookie(.note-auth/state.json)がgit statusに表示されているのを発見しました。pushする直前でした。
何が起きたか
AIエージェントの1つが、noteへの自動投稿のためにブラウザ認証の状態(セッションCookie)をJSONファイルに保存していました。
.note-auth/
state.json ← noteのセッションCookie・認証トークンが平文で入っている
このファイルが .gitignore に登録されておらず、git status で untracked file として表示されていたのです。
もしこのままpushしていたら:
- noteアカウントの乗っ取りリスク
- セッションCookieでログイン済みの操作が第三者に可能に
- GitHubのコミット履歴に残るため、force pushしても完全削除が困難
なぜ.gitignoreだけでは不十分なのか
.gitignore は「知っているファイル」しか除外できません。
| シナリオ | .gitignoreで防げるか |
|---|---|
.env ファイル | ○(事前に書いてあれば) |
| 新しく生成された認証state | x(パターンを知らない) |
| ダウンロードしたService AccountのJSONキー | x |
| ライブラリが勝手に生成した一時ファイル | x |
つまり、「まだ知らないファイル」に対しては無力です。
解決策①: .gitignoreを網羅的にする
まず .gitignore を「考えられる秘密ファイルを全パターン」カバーするように強化します。
# Environment
.env
.env.local
.env.*
# Auth state / credentials (NEVER commit)
.note-auth/
*.pem
*.key
*.p12
*.pfx
*-credentials.json
*-service-account.json
service-account*.json
client_secret*.json
oauth2*.json
token.json
credentials.json
# Cookies
*_cookies.json
# Google Cloud
gcloud/
.gcp/
ポイント: ワイルドカードを積極的に使う。service-account*.json のように、ファイル名のパターンで網をかけます。
解決策②: pre-commitフックで自動検出(本命)
これが本命です。コミット時に秘密情報パターンを自動検出し、コミット自体を止めます。
.git/hooks/pre-commit に以下を保存してください:
#!/bin/bash
# Pre-commit hook: 秘密情報を含むファイルのコミットをブロック
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
BLOCKED=0
FILES=$(git diff --cached --name-only --diff-filter=ACM)
if [ -z "$FILES" ]; then
exit 0
fi
# 1. 危険なファイル名パターン
DANGEROUS_PATTERNS=(
"\.pem$"
"\.key$"
"\.p12$"
"credentials\.json"
"service.account.*\.json"
"client_secret.*\.json"
"token\.json"
"state\.json"
"_cookies\.json"
"\.env$"
"\.env\."
)
for FILE in $FILES; do
for PATTERN in "${DANGEROUS_PATTERNS[@]}"; do
if echo "$FILE" | grep -qE "$PATTERN"; then
echo -e "${RED}BLOCKED${NC}: $FILE (dangerous filename: $PATTERN)"
BLOCKED=1
fi
done
done
# 2. ファイル内容に秘密情報パターンがないか
SECRET_PATTERNS=(
"PRIVATE KEY"
"BEGIN.*KEY"
"sk_live_"
"sk_test_"
"ghp_[a-zA-Z0-9]{36}"
"\"_note_session"
"\"type\":.*\"service_account\""
)
for FILE in $FILES; do
if [ ! -f "$FILE" ]; then continue; fi
if file "$FILE" 2>/dev/null | grep -q "binary"; then continue; fi
for PATTERN in "${SECRET_PATTERNS[@]}"; do
if git diff --cached -- "$FILE" | grep -qE "$PATTERN"; then
echo -e "${RED}BLOCKED${NC}: $FILE contains secret pattern: $PATTERN"
BLOCKED=1
break
fi
done
done
if [ $BLOCKED -ne 0 ]; then
echo ""
echo -e "${YELLOW}コミットをブロックしました。秘密情報が含まれている可能性があります。${NC}"
echo "本当にコミットする場合は: git commit --no-verify"
exit 1
fi
exit 0
保存したら実行権限を付けます:
chmod +x .git/hooks/pre-commit
動作確認
試しにStripeのテスト秘密鍵を含むファイルをコミットしてみます:
echo 'sk_live_fake1234567890abcdef' > test-leak.txt
git add test-leak.txt
git commit -m "test"
結果:
BLOCKED: test-leak.txt contains secret pattern: sk_live_
コミットをブロックしました。秘密情報が含まれている可能性があります。
本当にコミットする場合は: git commit --no-verify
コミットが止まりました。 事故防止成功です。
解決策③: GitHub Secret Scanning(補足)
GitHubにはpush時に秘密情報を検出する「Secret Scanning」機能があります。
- パブリックリポジトリ: 無料で自動有効
- プライベートリポジトリ: GitHub Advanced Security(有料)が必要
個人開発者はプライベートリポジトリが多いので、pre-commitフックの方が現実的です。
まとめ
| 対策 | 効果 | 設定時間 |
|---|---|---|
| .gitignore強化 | 既知のパターンをブロック | 1分 |
| pre-commitフック | 未知のパターンも検出・コミット拒否 | 2分 |
| GitHub Secret Scanning | push時の最終防衛(パブリックのみ無料) | 0分 |
.gitignore + pre-commitフックの2段構えが最強です。 3分で設定できるので、今すぐやりましょう。