Claude Code セキュリティ失敗事例7選|実際に起きた事故と防止策
Claude Codeで実際に起きがちなセキュリティ事故を7ケース紹介。.env流出・本番DB誤操作・課金爆発など、事例ごとに原因と再発防止コードを詳解。
「Claude Code は便利だけど、なんとなく怖い」——この感覚は正しいです。強力なツールは強力な事故を起こします。
この記事では、Claude Code を使った開発現場で実際に起きがちなセキュリティ事故を7ケース取り上げ、なぜ起きたか・どう防ぐか を具体的なコードと設定で解説します。「他山の石」として活用してください。
事例1: .env ファイルを GitHub にプッシュしてしまった
何が起きたか
「このリポジトリ、CI に環境変数を渡したいので .env もコミットしてください」という指示を Claude Code に渡した。Claude Code は素直に git add .env && git commit を実行。GitHub にプッシュ後、数分でクローラーが APIキーを検出。Slack に「Your API key has been exposed」という通知が届いた。
原因
.gitignoreに.envが入っていなかった- Claude Code は「コミットして」という指示を忠実に実行する
- ユーザーも確認画面を流れで承認してしまった
対策コード
1. プロジェクト作成時のセットアップを自動化
# scripts/init-security.sh — プロジェクト初期化時に必ず実行
#!/bin/bash
cat >> .gitignore << 'EOF'
# === セキュリティ: 絶対にコミットしない ===
.env
.env.*
.env.local
!.env.example
*.pem
*.key
*-service-account.json
credentials.json
EOF
echo "✓ .gitignore にセキュリティ除外パターンを追加しました"
git add .gitignore && git commit -m "security: add .gitignore patterns"
2. 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"
}]
}
]
}
}
3. すでにプッシュした場合の対処
# Step1: まず APIキーを即ローテーション (これが最優先)
# Step2: git 履歴から完全削除
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch .env" \
--prune-empty --tag-name-filter cat -- --all
# Step3: リモートに強制プッシュ
git push origin --force --all
# Step4: GitHub のキャッシュをパージ (GitHub サポートに連絡も)
事例2: 本番DBに対して DROP TABLE を実行した
何が起きたか
「このテーブル、もう使ってないから削除して」と指示。Claude Code が DROP TABLE old_users; を生成・実行。問題はこれが本番環境の DATABASE_URL に接続していたこと。バックアップが3日前のものしかなく、3日分のデータが消えた。
原因
- 開発環境と本番環境で同じ
.envを使い回していた - Claude Code には環境の区別がつかない
ask設定にしていたが、流れで「OK」を押してしまった
対策コード
1. 環境ごとに .env を完全分離
.env.development # ← ローカル開発用、ダミーDB
.env.staging # ← ステージング、本番コピー
.env.production # ← 本番、手動管理・共有禁止
2. スクリプトに環境チェックを組み込む
// scripts/db-migrate.mjs
const env = process.env.APP_ENV ?? "development";
const dbUrl = process.env.DATABASE_URL ?? "";
if (env === "production") {
const readline = require("readline").createInterface({
input: process.stdin, output: process.stdout
});
await new Promise((resolve) => {
readline.question(
`⚠️ 本番DB (${dbUrl.split("@")[1]}) に接続します。\n本当に続けますか? (yes と入力): `,
(answer) => {
readline.close();
if (answer !== "yes") { console.log("中止しました"); process.exit(0); }
resolve(undefined);
}
);
});
}
3. CLAUDE.md で本番操作を禁則
## 🚨 本番環境禁則事項
DATABASE_URL に `prod` `production` `live` が含まれる場合:
- DROP / TRUNCATE / DELETE (WHERE句なし) は絶対に実行しない
- マイグレーション前にユーザー確認を必ず取る
- 実行前にバックアップ取得コマンドを提示する
事例3: rm -rf で重要ファイルを消した
何が起きたか
「build/ ディレクトリをクリーンアップして」という指示が、パスの指定ミスで rm -rf ./ になり、プロジェクト全体を削除した。git 管理外のファイル (ローカルの設定ファイル、未コミットの実験コード) は二度と戻らなかった。
原因
rm -rfは Claude Code に最も危険なコマンドの一つ- パスのダブルクォートなし→スペース含むパスで誤動作
- 承認を「まあいいか」で通してしまった
対策コード
// .claude/settings.json
{
"permissions": {
"deny": [
"Bash(rm -rf /)",
"Bash(rm -rf ~*)",
"Bash(rm -rf .*)"
],
"ask": [
"Bash(rm -rf*)"
]
}
}
さらに Hook で削除前に内容を表示:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash(rm*)",
"hooks": [{
"type": "command",
"command": "echo '⚠️ 削除コマンド検出。5秒後に実行します。Ctrl+C で中止。' && sleep 5"
}]
}
]
}
}
事例4: APIキーをプロンプトに書いて subagent に渡した
何が起きたか
「QIITA_TOKEN=abc123def456 を使って Qiita に投稿してください」とプロンプトに直書きし、subagent に委譲。subagent はログやメモリに内容を書き出すことがあり、トークンが .claude/ 配下のログファイルに残ってしまった。
原因
- プロンプトは会話履歴として保持される
- subagent へのプロンプトも同様にログに残る
- ローカル環境でも他のプロセスやバックアップで漏洩するリスクがある
対策コード
プロンプトには絶対に書かず、環境変数経由で渡す
# ❌ 危険
claude -p "QIITA_TOKEN=abc123 を使って qiita-publish.mjs を実行して"
# ✅ 安全: スクリプト側で process.env から読む
# .env に QIITA_TOKEN=abc123 を書いておき
claude -p "scripts/qiita-publish.mjs を実行して (トークンは .env から自動で読む)"
subagent への指示も同様
// ❌ 危険
Agent({ prompt: `APIキー ${process.env.SECRET_KEY} を使って...` });
// ✅ 安全: キーの名前だけ渡し、値はスクリプト側で読む
Agent({ prompt: "SECRET_KEY 環境変数を使って..." });
事例5: API呼び出しが無限ループして課金が爆発した
何が起きたか
「エラーが出たら自動でリトライして」という指示のもと、エラーハンドリング付きのスクリプトを書かせた。エラーが永久に解消しないケースでリトライが止まらず、Anthropic APIを1時間で3000回コールし、$200の課金が発生。
原因
- リトライ上限を設定しなかった
- exponential backoff なし、1秒間隔で無限ループ
- 課金アラートの設定がなかった
対策コード
// utils/retry.ts — 安全なリトライユーティリティ
export async function withRetry<T>(
fn: () => Promise<T>,
options = { maxAttempts: 3, baseDelayMs: 1000, maxDelayMs: 30000 }
): Promise<T> {
let lastError: Error;
for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
lastError = err as Error;
if (attempt === options.maxAttempts) break;
// Exponential backoff + jitter
const delay = Math.min(
options.baseDelayMs * Math.pow(2, attempt - 1) + Math.random() * 1000,
options.maxDelayMs
);
console.warn(`Attempt ${attempt}/${options.maxAttempts} failed: ${err.message}`);
console.warn(`Retrying in ${Math.round(delay / 1000)}s...`);
await new Promise((r) => setTimeout(r, delay));
}
}
throw new Error(`${options.maxAttempts}回リトライ後も失敗: ${lastError!.message}`);
}
CLAUDE.md に明記:
## APIコール時の必須ルール
- リトライは最大3回まで
- Exponential backoff を必ず実装 (1s → 2s → 4s)
- 無限ループを作らない: while(true) + API呼び出しは禁止
事例6: git push --force で同僚のコミットを消した
何が起きたか
「ローカルの状態でリモートを上書きして」という指示で git push --force を実行。チームメンバーが直前にプッシュしたコミット3件が消えた。そのメンバーはローカルにも変更を持っていなかったため、コードが完全に失われた。
原因
--forceは危険性の理解なしに実行されやすい- Claude Code は「リモートを上書きして」という指示を正直に実行する
git push --force-with-leaseという安全な代替を知らなかった
対策コード
// .claude/settings.json
{
"permissions": {
"deny": [
"Bash(git push --force *master*)",
"Bash(git push --force *main*)",
"Bash(git push -f *master*)",
"Bash(git push -f *main*)"
]
}
}
CLAUDE.md に代替コマンドを指定:
## git 操作の安全ルール
- `git push --force` は **禁止**
- 代わりに `git push --force-with-lease` を使う
(他人の変更があれば自動で弾かれる)
- main/master ブランチへの直接 push は必ずユーザー確認を取る
事例7: 権限過多のサービスアカウントで全リソースにアクセスされた
何が起きたか
「この GCP サービスアカウントのキーを使って Cloud Storage を操作して」という指示で、Owner 権限のサービスアカウントを使用。Claude Code は Cloud Storage だけでなく、テスト目的で BigQuery・Cloud SQL・GKE クラスタまで「調査」のために接続し、予期しない料金が発生した。
原因
- サービスアカウントに必要以上の権限を付与していた (最小権限の原則違反)
- Claude Code はツールを積極的に使う性質がある
- 「調査のため」という正当な理由でも幅広くアクセスが走った
対策コード
最小権限のサービスアカウントを作成:
# ❌ 避けるべき: Owner 権限
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:[email protected]" \
--role="roles/owner"
# ✅ 必要最小限の権限のみ
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:[email protected]" \
--role="roles/storage.objectAdmin"
# ← Cloud Storage の読み書きだけ
CLAUDE.md でアクセス範囲を明示:
## GCP アクセス制限
このプロジェクトで使用するサービスアカウントの権限:
- Cloud Storage: 読み書き OK (バケット: my-project-assets のみ)
- BigQuery: 禁止
- Cloud SQL: 禁止
- その他の GCP リソース: 禁止
権限外のリソースにアクセスしようとする指示は断ること。
事故を防ぐための総合チェックリスト
これら7事例から共通するパターンを抽出した最終チェックリストです。
### 即日やるべき設定 (30分)
- [ ] .gitignore に .env パターンを追加
- [ ] .claude/settings.json に deny リスト (rm -rf, git push --force, DROP TABLE)
- [ ] CLAUDE.md に禁則事項を日本語で明記
### 週1でやるべき確認
- [ ] git log で意図しないファイルのコミットがないか確認
- [ ] .env の内容が .gitignore で除外されているか `git check-ignore -v .env`
- [ ] APIキーのローテーション期限チェック
### 事故発生時の初動
1. 即座に該当 APIキーを無効化・ローテーション
2. git 履歴からの削除 (filter-branch or BFG)
3. アクセスログを確認して被害範囲を特定
4. 関係者に状況報告
まとめ
Claude Code の事故は「AIが暴走した」のではなく、ほとんどが 「人間がセキュリティ設定を後回しにした」 ことで起きています。
| 事例 | 根本原因 | 予防策 |
|---|---|---|
| .env 流出 | gitignore なし | init スクリプト + Hook |
| 本番DB 削除 | 環境分離なし | .env 分離 + 確認フロー |
| rm -rf 事故 | deny リストなし | settings.json 設定 |
| キー漏洩 | プロンプトに直書き | 環境変数経由に統一 |
| 課金爆発 | リトライ上限なし | withRetry ユーティリティ |
| force push | 禁止設定なし | deny + force-with-lease |
| 権限過多 | 最小権限違反 | IAM ロールを絞る |
今日できる最初の一歩: .claude/settings.json に "deny": ["Bash(rm -rf*)"] を追加するだけで、最も破壊的な事故の一つを防げます。
関連記事
参考資料
無料PDF: Claude Code 5分でわかるチートシート
メールアドレスを登録するだけで、A4 1枚のチートシートPDFを今すぐお送りします。
個人情報は厳重に管理し、スパムは送りません。
この記事を書いた人
Masa
現役DX室長|Claude Code でゼロから多言語AI技術メディア運営中。実務直結の自動化、AI開発相談・研修受付中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
Claude Code セキュリティ対策完全ガイド|APIキー漏洩・権限設定・本番保護
Claude Code を安全に使うための実践セキュリティガイド。APIキー管理から権限設定、Hooksを使った自動チェック、本番環境保護まで、動くコードで解説。
Claude Code 権限設定完全ガイド|settings.json・Hooks・allowlist を徹底解説
Claude Code の権限設定を完全解説。allow/deny/ask の使い分け、Hooksによる自動化、環境別settings.json、実践パターン集まで動くコードで紹介。
ハーネスエンジニアリング完全ガイド|Claude Codeに学ぶAIエージェントの作り方
プロンプトだけでLLMは使いこなせない。ツール・文脈・制御ループを束ねる「ハーネス」を、動くコードで解説。Claude Code実装例から自作エージェントまで。