Claude Codeで実際に起きたセキュリティ事故7選と防止策
「Claude Code は便利だけど、なんとなく怖い」——この感覚は正しいです。強力なツールは強力な事故を起こします。実際に開発現場で起きがちなケースを7つ取り上げ、原因と防止策を解説します。
事例1: .env ファイルをGitHubにプッシュ
「CI に環境変数を渡したいので .env もコミットして」という指示で、Claude Code が素直に git add .env && git commit を実行。数分でクローラーが APIキーを検出。
防止策: .gitignore に追加 + Hookでコミット前チェック
// .claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash(git add*)",
"hooks": [{
"type": "command",
"command": "git diff --cached --name-only | grep -E '^\\.env' && echo '🚨 .env をステージしようとしています!' && exit 1 || exit 0"
}]
}
]
}
}
事例2: 本番DBで DROP TABLE を実行
「このテーブル、もう使ってないから削除して」→ 本番 DATABASE_URL に接続した状態で実行。3日分のデータが消えた。
防止策: 本番環境にはインタラクティブ確認を強制
// scripts/db-migrate.mjs
if (process.env.APP_ENV === "production") {
const answer = await question("本番DBに接続します。続けますか? (yes): ");
if (answer !== "yes") process.exit(0);
}
事例3: rm -rf でプロジェクト全体を削除
「build/ をクリーンアップして」という指示がパスミスで rm -rf ./ に。git 管理外ファイルは全滅。
防止策: settings.json で deny + 5秒の猶予
{
"permissions": {
"deny": ["Bash(rm -rf ~*)", "Bash(rm -rf .*)"],
"ask": ["Bash(rm -rf*)"]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash(rm*)",
"hooks": [{ "type": "command", "command": "echo '⚠️ 削除コマンド検出。5秒後に実行。Ctrl+C で中止。' && sleep 5" }]
}
]
}
}
事例4: APIキーをプロンプトに直書きしてログに残った
claude -p "QIITA_TOKEN=abc123 を使って投稿して" と書いたら、subagent のログファイルにトークンが記録された。
防止策: プロンプトには書かず、スクリプト側で process.env から読む
# ❌ 危険
claude -p "TOKEN=abc123 を使って..."
# ✅ 安全
# .env に TOKEN=abc123 を書いておき
claude -p "scripts/publish.mjs を実行して (トークンは .env から読む)"
事例5: API呼び出し無限ループで $200 課金
「エラーが出たら自動でリトライして」→ リトライ上限なし、1時間で3000回コール。
防止策: Exponential backoff + 上限を必ず設定
async function withRetry<T>(fn: () => Promise<T>, maxAttempts = 3): Promise<T> {
for (let i = 1; i <= maxAttempts; i++) {
try {
return await fn();
} catch (err) {
if (i === maxAttempts) throw err;
const delay = 1000 * Math.pow(2, i - 1);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error("unreachable");
}
事例6: git push --force で同僚のコミットを消した
「ローカルの状態でリモートを上書きして」→ チームメンバーのコミット3件が消滅。ローカルにもなく完全消失。
防止策: force push を deny、force-with-lease を推奨
{
"permissions": {
"deny": [
"Bash(git push --force *main*)",
"Bash(git push -f *main*)"
]
}
}
<!-- CLAUDE.md -->
## git ルール
- `git push --force` は禁止
- 代わりに `git push --force-with-lease` を使う
事例7: Owner権限サービスアカウントで意図外リソースにアクセス
「Cloud Storage を操作して」という指示で、Owner権限のサービスアカウントを使用。BigQuery・Cloud SQL まで「調査」で接続され、予期しない課金が発生。
防止策: 最小権限の原則を徹底
# ❌ 避ける: Owner権限
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:claude@PROJECT.iam.gserviceaccount.com" \
--role="roles/owner"
# ✅ 必要なリソースだけ
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:claude@PROJECT.iam.gserviceaccount.com" \
--role="roles/storage.objectAdmin"
事故を防ぐチェックリスト
即日対応 (30分)
- [ ] .gitignore に .env パターン追加
- [ ] settings.json に deny リスト (rm -rf, git push --force, DROP TABLE)
- [ ] CLAUDE.md に禁則事項を記載
週1確認
- [ ] git log で意図しないコミットがないか確認
- [ ] git check-ignore -v .env で除外確認
- [ ] APIキーのローテーション期限チェック
まとめ
| 事例 | 根本原因 | 予防策 |
|---|---|---|
| .env 流出 | gitignore なし | init スクリプト + Hook |
| 本番DB 削除 | 環境分離なし | .env 分離 + 確認フロー |
| rm -rf 事故 | deny リストなし | settings.json |
| キー漏洩 | プロンプトに直書き | 環境変数経由 |
| 課金爆発 | リトライ上限なし | withRetry ユーティリティ |
| force push | 禁止設定なし | deny + force-with-lease |
| 権限過多 | 最小権限違反 | IAM ロールを絞る |
Claude Code の事故は「AIが暴走した」のではなく、「セキュリティ設定を後回しにした結果」 がほとんど。今日30分の設定で将来の大きな事故を防げます。
Claude Code セキュリティ対策完全ガイド も合わせてどうぞ。