出版日: 2026年3月24日

Claude Codeを使い始めて約1年になる。日本の熊出没を追跡する Kumamapをはじめ、複数のプロジェクトで活用してきた。ある日、データパイプラインを実行中にOpusが暴走した。既存データに新しいエントリをマージするのではなく、データ配列全体を置き換えてしまった。ローカルデータは消えた。

幸い、被害は限定的だった。防御システムが本番環境を守り、ローカルのデータ消去は復旧可能だった。この記事はその防御システムについて書いている。Claude Codeから最大の成果を引き出しつつ、付きっきりで監視しなくて済む方法だ。
私は4つのレイヤーに頼っている。どれも単独では完璧ではないが、組み合わせることで何か問題が起きたときに被害を封じ込められる。
プロジェクトに組み込む行動指示
会話中にその都度与えるガードレール
モデルが上書きできないツールレベルの権限制御
本番認証情報の隔離、バックアップ、使い捨てのローカルDB
CLAUDE.mdは、エージェントがセッション開始時に読み込むファイルだ。プロジェクト内でどう振る舞ってほしいかをエージェントに伝える役割を持つ。以下は私のKumamapプロジェクトのProduction Safetyセクションだ。
## Production Safety
ほとんどの場合うまく機能する。エージェントはこれらのルールを読み、破壊的な操作の前に確認してくれる。しかしCLAUDE.mdは提案であって壁ではない。モデルはそれを無視することもできる。配列を置き換えるのが「正しい」アプローチだと判断したら、CLAUDE.mdを読み返してはくれない。
作業中にエージェントに与える会話内の指示だ。
"don't touch the production database"
"only read, don't write anything"
"run the migration on local first, show me the result before touching prod"
"don't delete any files, just show me what you'd remove"
"check the logs before making any changes" 目の前のタスクには有効だが、持続性がない。エージェントはセッション途中で忘れることがあるし、新しい会話には引き継がれない。プロンプティングだけでは信頼できる防御にはならない。
ここからが本番だ。
.claude/settings.jsonは、エージェントが確認なしに使えるツールを制御する。CLAUDE.mdやプロンプティングとは違い、モデルが上書きできないハードリミットだ。
3つのティアがある。 allow(自動で実行)、 ask(確認が必要)、 deny(完全にブロック)。 どれにも該当しない操作はデフォルトで確認が求められる。
ルールは1つ。可逆な操作はallowに、不可逆な操作はaskにする。
rmは破壊的。Ask。以下が私のプロジェクト全体で使っている完全な設定ファイルだ。
{
"permissions": {
"deny": [],
"allow": [
// Reading & searching
"Read",
"Grep",
"Glob",
"WebSearch",
"WebFetch",
// Git — safe read/write operations
"Bash(git status *)",
"Bash(git log *)",
"Bash(git diff *)",
"Bash(git branch *)",
"Bash(git show *)",
"Bash(git stash *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git fetch *)",
"Bash(git pull *)",
"Bash(git merge *)",
"Bash(git rebase *)",
"Bash(git cherry-pick *)",
"Bash(git tag *)",
// GitHub CLI
"Bash(gh *)",
// ⚠️ Runtimes — can do anything
"Bash(node *)",
"Bash(python3 *)",
// npm — read-only & dev tools
"Bash(npm run *)",
"Bash(npm list *)",
"Bash(npm ls *)",
"Bash(npm show *)",
"Bash(npm view *)",
"Bash(npm outdated *)",
"Bash(npx prettier *)",
"Bash(npx eslint *)",
"Bash(npx tsc *)",
"Bash(npx vitest *)",
"Bash(npx svelte-check *)",
// pip — read-only
"Bash(pip show *)",
"Bash(pip list *)",
// Filesystem — read & navigate
"Bash(ls *)",
"Bash(cat *)",
"Bash(head *)",
"Bash(tail *)",
"Bash(wc *)",
"Bash(du *)",
"Bash(find *)",
"Bash(which *)",
"Bash(file *)",
"Bash(echo *)",
"Bash(date *)",
"Bash(pwd *)",
"Bash(diff *)",
// Text processing
"Bash(sort *)",
"Bash(uniq *)",
"Bash(jq *)",
"Bash(grep *)",
"Bash(rg *)",
"Bash(sed *)",
"Bash(awk *)",
"Bash(tr *)",
"Bash(cut *)",
"Bash(base64 *)",
// Filesystem — write (git tracked, so revertible)
"Bash(mkdir *)",
"Bash(touch *)",
"Bash(cp *)",
"Bash(mv *)",
// Misc tools
"Bash(open *)",
"Bash(cwebp *)",
"Bash(sips *)",
"Bash(gunzip *)",
"Bash(curl *)",
// Version/help — always safe
"Bash(* --version)",
"Bash(* --help)"
],
"ask": [
// Deployment — ask before shipping
"Bash(wrangler *)",
"Bash(npx wrangler *)",
// Git — destructive or irreversible operations
"Bash(git push *)",
"Bash(git reset --hard *)",
"Bash(git clean *)",
"Bash(git checkout .)",
"Bash(git checkout -- .)",
"Bash(git checkout * -- .)",
"Bash(git restore .)",
// Package management
"Bash(npm install *)",
"Bash(npm publish *)",
"Bash(pip install *)",
// Destructive
"Bash(rm *)",
"Bash(sudo *)",
"Bash(gh repo delete *)",
// Secrets — never read without asking
"Read(.dev.vars)",
"Read(.env)",
"Bash(cat *.env)",
"Bash(cat .env)"
]
}
} // ⚠️ Runtimes — can do anything: file writes,
// network requests, db operations.
// They bypass other restrictions.
"Bash(node *)",
"Bash(python3 *)" nodeと
python3は何でもできる。ファイル書き込み、ネットワークリクエスト、データベース操作。設定ファイルの他のすべての制限をバイパスしてしまう。
askに移すほうが安全だが、ワークフローが崩壊する。エージェントが実行したいスクリプトを一つ一つ承認する羽目になる。時間が経つにつれ、新しいモデルが賢くなるにつれ、
allowのままにすることに抵抗がなくなってきた。これは個人のリスク許容度の問題だ。
// Secrets — never read without asking
"Read(.dev.vars)",
"Read(.env)",
"Bash(cat *.env)",
"Bash(cat .env)" 見落としやすいポイントだ。
wranglerのデプロイをブロックしていても、エージェントは
.envや
.dev.varsを読み取り、curlやnode fetchで直接HTTPリクエストを送ることができる。APIキーを取得して、デプロイ制限を完全に迂回し、自力でサービスを呼び出せてしまう。
だからこそ環境変数は常に
askにしておく。エージェントがシークレットに触れるときは、必ず自分が把握している状態でなければならない。
上の3つのレイヤーがすべて失敗しても、アーキテクチャで被害を封じ込めるべきだ。
ここは難しいところだ。状況を把握するために本番データを読む必要がある場面がほとんどなので、エージェントにSELECTアクセスを与えるのは理にかなっている。
理想的なのは本番の完全なローカルコピーを用意し、エージェントが本物ではなくそちらを操作する構成だ。しかし同期を維持するのは常に現実的とは限らない。
どちらを選ぶにしても、本番データベースのバックアップは必ず有効にしておくこと。エージェントが不可逆なダメージを与えてしまった場合、即座に復旧できる状態にしておきたい。
正直に言うと、防げなかった。データ消去は
python3経由で起きたが、これは
allowに入っていた。どんな権限ファイルでもキャッチできなかっただろう。
ただし被害はローカルにとどまった。本番はレイヤー4で守られていた。本番データベースの認証情報が開発マシンになかったため、エージェントがいくら試みても本番には到達できなかった。
だから4つのレイヤーすべてが必要なのだ。どれ一つとして単独では十分ではない。
まずは厳しめに設定し、信頼を築きながら緩めていくのがいい。上の設定ファイルをコピーしよう。慎重にいくなら
nodeと
python3を
askに移す。本番環境の安全ルールを記したCLAUDE.mdを書く。本番の認証情報は開発マシンから外す。バックアップを有効にする。
目標は制限でエージェントを使い物にならなくすることではない。安全な場所では素早く動き、重要な場所では慎重に動けるようにすることだ。
このブログは英語からLLMによって翻訳されました。不明な点がある場合は、お問い合わせページからご連絡ください。
コメントを残す
コメント
その他のブログ
2025/07/07
Q学習:インタラクティブ強化学習の基礎
2025/07/06
最適化アルゴリズム:SGD、モメンタム、Adam
2025/07/05
文字から単語へ:日本語BPEトークナイザーの構築
2024/06/19
SvelteとJavaScriptを使用してシンプルで動的なツールチップを作成する
2024/06/17
JavaScriptを用いて東京都のインタラクティブな地図を作成する
2024/06/14
Matplotlibで日本語文字化けを解決できる簡単な方法
2024/06/13
書評 | トーキング・トゥ・ストレンジャーズ 「よく知らない人」について私たちが知っておくべきこと by マルコム・グラッドウェル
2024/06/07
日本語で最もよく使われる3000字の漢字
2024/06/07
VSCodeでRegexを使用してReplaceする方法
2024/06/06
SvelteではReadable Storeを使用するな
2024/06/05
GzipとPakoでデータを圧縮してWebサイトのローディング速度を上げる方法
2024/05/31
JavaScriptを使用してWebページ上でマウスが指している単語を特定する
2024/05/29
SvelteとSVGを用いてインタラクティブな地図を作成する
2024/05/28
書評 | Originals 誰もが「人と違うこと」ができる時代 by アダム・グラント & シェリル・サンドバーグ
2024/05/27
Javascriptを使用して数独を解く方法
2024/05/26
ウェブサイトへのトラフィックを1か月で10倍に増やした方法
2024/05/24
人生はサイクリングに似ている
2024/05/19
JavaScriptでバックトラッキング・アルゴリズムを用いて完全な数独グリッドを生成する
2024/05/16
Tailwindが素晴らしい理由とWeb開発をいかに楽にするか
2024/05/15
PythonとGitフックを使用してサイトマップを自動的に生成する
2024/05/14
書評 | Range (レンジ) 知識の「幅」が最強の武器になる by デイビッド・エプスタイン
2024/05/13
SvelteとSvelteKitはなんですか?
2024/05/12
SvelteKitで国際化(多言語化)
2024/05/11
SvelteでCachingを用いてDeploy時間を短縮する方法
2024/05/10
SvelteとIntersection Oberverによるレイジーローディング
2024/05/10
遺伝的アルゴリズムで最適な株式ポートフォリオを作る方法
2024/05/09
Pythonを用いてShapeFileをSVGに変換できる方法
2024/05/08
Svelteの反応性:変数、バインディング、およびキー関数
2024/05/07
書評 | 孫子の兵法
2024/05/06
スペシャリストは終了。ゼネラリスト万歳!
2024/05/03
トルコ人の有権者の投票行動をPythonでの分析
2024/05/01
Seleniumを用いてトルコ投票データベースを作る方法
2024/04/30
SvelteとTailwindを使用してInfinite Scrollできる方法
2024/04/29
1年間以内で日本語を駆使できるようになるための方法
2024/04/25
SvelteとTailwindを用いたWebサイトテンプレート
2024/01/29
怠惰なエンジニアとひどいデザイン
2024/01/28
偉大さについて
2024/01/28
MacBook で PDF を PNG に変換する
2023/12/31
2023年振り返り:24冊の読んだ本のまとめ
2023/12/30
Python PILを使用して写真コラージュを作成する方法
2024/01/09
ウェブサイトの訪問者のデバイスとブラウザを検出する方法
2024/01/19
ChatGPT回答の解析