第2回 Claude Code の権限設計 — どこまで許可し、何を必ず拒否するか
Claude Code の permissions を「書き方」ではなく「決め方」から整理します。deny に必ず入れる4カテゴリ、allow に入れてよいものを見分ける3条件、サンドボックスが守る範囲と守らない範囲、そしてチームで managed / project / user をどう分担するかを扱います。
この回のキーメッセージ
権限設計とは「便利さをどこまで諦めるか」ではなく、「取り返しのつかない操作だけを確実に閉じる」作業です。
Claude Code の permissions をどう書くかは、インストールと初期設定ガイドで扱っています。allow / deny / ask の記法、defaultMode、additionalDirectories の設定方法はそちらを参照してください。この回で扱うのは、その一段手前にある「何をどの枠に入れるか」の判断です。
現場で権限設定が形骸化する典型は2つあります。ひとつは、確認プロンプトが煩わしくて allow を膨らませ続け、気づけば実質的に何でも通る状態になっているケース。もうひとつは、逆に厳しくしすぎて毎回 --dangerously-skip-permissions で起動するようになり、設定ファイルが飾りになっているケースです。どちらも「便利さと安全性を1本のつまみで調整しようとした」結果です。
つまみは1本ではありません。取り返しがつく操作は広く許し、取り返しがつかない操作だけを狭く閉じるという二軸で考えると、日常の摩擦を増やさずに事故の上限を下げられます。以下では、その「取り返しがつかない側」の具体的なリストと、サンドボックスがどこまでを肩代わりしてくれるのかを整理します。
前提として、第1回で扱った責任分界の考え方を引き継ぎます。エージェントの権限は、開発者自身が持つ権限の部分集合でしかありません。開発マシンに本番の認証情報が置いてあれば、エージェントの権限設計だけでその到達を止めることはできない、という点が本記事の後半の論点になります。
1. 前提として押さえる2つの挙動
判断軸を組み立てる前に、設定が実際にどう効くかを2点だけ確認します。詳細な記法は既存記事に譲り、ここでは「設計に影響する挙動」だけを扱います。
評価順序は deny → ask → allow の固定
ルールは deny → ask → allow の順で評価され、最初にマッチしたものが採用されます。重要なのは、ルールの具体性がこの優先順位を変えないことです。
たとえば allow に細かく書いた具体的なパターンがあっても、deny に大雑把なパターンがマッチすれば拒否されます。CSS のセレクタや一部のファイアウォール製品のように「より具体的なルールが勝つ」わけではありません。
この性質は設計上、素直に使えます。deny は「例外を作らない禁止リスト」として書けるということです。「本番デプロイは禁止、ただしこの1コマンドだけは例外」という書き方は成立しないので、そもそも例外が必要な操作を deny に入れてはいけません。逆に言えば、deny に入れた時点で「この操作はエージェントには絶対にやらせない」という宣言になります。
スコープはマージされ、managed の deny は下位で緩められない
設定は managed(組織ポリシー) / project / user / local の各スコープに分かれており、それぞれの allow / ask / deny はすべてマージされて1つの実効ポリシーになります。上書きではなく合成です。
したがって、下位スコープで書けるのは「追加」だけだと考えるのが安全です。とくに managed の deny は、下位のどのスコープからも緩められません。組織として譲れない禁止事項をここに置けば、個人設定やプロジェクト個人設定でどう書かれても効き続けます。
各ファイルの配置場所と、個人設定 / プロジェクト設定の使い分けについてはインストールと初期設定ガイドの該当節を参照してください。
2. deny に必ず入れる4カテゴリ
deny に入れる基準は1つです。「実行された結果を、自分の手で元に戻せるか」。戻せないなら deny、戻せるなら ask か allow に回します。この基準で振り分けると、おおむね次の4カテゴリに収束します。
| カテゴリ | 対象の例 | なぜ戻せないか | 事故ったときの被害 |
|---|---|---|---|
| 認証情報の読み取り | 環境変数ファイル、秘密鍵、~/.aws ~/.ssh 配下、クラウドCLIの認証キャッシュ | 一度モデルの文脈に入った値は「読まなかったこと」にできない。ログ・トランスクリプト・要約に残る | 本番DBやクラウドアカウントへの永続アクセス。ローテーションするまで有効 |
| 本番環境に到達するコマンド | デプロイコマンド、マイグレーション適用、本番DBへの直接接続クライアント | 実行された副作用は外部システム側にある。ローカルの変更を戻しても消えない | サービス停止、データ破壊。マイグレーションは down がない場合が多い |
| 履歴を破壊する git 操作 | force push、git reset --hard、追跡外ファイルを消す git clean | リモートの履歴を上書きすると、他人のローカルにしか残らない | 他のメンバーの未マージ作業の消失。復旧はメンバー各自の reflog 頼み |
| パッケージの publish | npm / PyPI などレジストリへの公開 | 公開されたバージョンは取り消しても、既にインストールされた先には届かない | 秘密情報の混入した成果物が第三者に配布される。名前空間の汚染 |
4カテゴリを個別に補足します。
認証情報の読み取り
最優先はここです。他の3つが「壊す」被害であるのに対し、これだけが「漏れる」被害だからです。壊れたものはバックアップから戻せますが、漏れた値は取り消せません。
見落としがちなのは、読み取りを止めるだけでは足りない経路があることです。Read ツールを塞いでも、Bash 経由でファイルの中身を標準出力に出せば同じことが起きます。Read と Bash の両面を塞ぐ必要があります。
もうひとつ、環境変数ファイルそのものより先に、環境変数の一覧を出力するコマンドが盲点になります。プロセスの環境に秘密が載っている構成では、ファイルを一切読まずに同じ情報が取れてしまいます。
本番環境に到達するコマンド
「本番に到達しうるか」は、コマンド名ではなく認証情報がどこを向いているかで決まります。同じデプロイコマンドでも、プロファイルが staging を指していれば影響は staging に閉じます。したがってここでの deny は、コマンド名で機械的に止める粗いフィルタだと理解しておきます。本質的な対策は第5節で扱います。
マイグレーションの適用は特に注意が必要です。多くのフレームワークで up は自動生成されますが、down は書かれていないか、書かれていても列削除を伴う場合は元のデータを復元できません。
履歴を破壊する git 操作
これは自分だけの被害では済まない点が他と違います。force push はリモートの共有履歴を書き換えるため、他のメンバーが取得済みのコミットとの整合が崩れます。
git reset --hard と、追跡外ファイルを消す git clean も同じ枠に入れます。コミットしていない作業は Git のどこにも残らないため、消えたら終わりです。エージェントが「作業ツリーをきれいにしてから作業しましょう」と判断して実行する動機は普通にあるので、明示的に閉じておきます。
パッケージの publish
頻度は低いものの、被害の非可逆性は最大級です。レジストリからバージョンを削除しても、既にインストールした環境やキャッシュ、ミラーには残ります。公開作業は人間が明示的に行う工程として切り出し、エージェントの手の届く範囲から外します。
3. allow に入れてよいものの見分け方
deny が「戻せないもの」だとすると、allow はその対偶ではありません。「戻せる」だけでは不十分で、次の3条件をすべて満たすものだけを allow に置きます。
| 条件 | 意味 | 満たす例 | 満たさない例 |
|---|---|---|---|
| 読み取り専用、または巻き戻せる | 実行後の状態を元に戻す手順が確実に存在する | テスト実行、型チェック、git status / git diff、ビルド | マイグレーション適用、リモートへの push |
| 影響がリポジトリ内で閉じる | 副作用がワーキングツリーの外に出ない | ソースの編集、ローカルのフォーマッタ | グローバルインストール、ホームディレクトリ配下の設定書き換え |
| 失敗が自分で気づける | 壊れたことがテストや差分としてすぐ観測できる | 型チェックを伴う編集 | 外部へのリクエスト送信、非同期に効くジョブ登録 |
3条件のうち2つしか満たさないものは ask に置きます。ask は「危険度が中くらいのもの」を入れる場所ではなく、判断に文脈が必要なものを入れる場所だと考えると整理しやすくなります。docker コマンドや外部URL取得がここに来るのは、危険度が中間だからではなく、そのときの引数次第で影響範囲が変わるからです。
もうひとつの実務的な指針として、allow の肥大化を定期的に見直すことを勧めます。「今後このコマンドを確認しない」という承認は、プロジェクト個人設定に蓄積されていきます。放っておくと、誰も内容を把握していない許可リストができあがります。四半期に一度は中身を読み、条件を満たさなくなったものを落とすのが現実的です。
4. サンドボックスの守備範囲と限界
権限設定と並んで語られるのがサンドボックスです。ただし「サンドボックスに入れたから安心」という理解は正確ではありません。適用範囲がはっきり限定されているためです。
適用されるのは Bash ツールとその子プロセスだけ
Claude Code のサンドボックスは OS レベルの強制機構ですが、適用対象は Bash ツールとその子プロセスです。Read / Edit、MCP サーバー、hooks はサンドボックスの外側でホスト上のプロセスとして動きます。
この非対称性は設計に直接効きます。たとえばサンドボックスがワークツリー外への書き込みを禁止していても、それは Bash 経由の書き込みにしか効かず、Read ツールでの読み取りには無関係です。サンドボックスは permissions の代わりにはならず、permissions もサンドボックスの代わりにはなりません。 両方を書いて初めて意味を持ちます。
Hooks が外側で動く点も重要です。Hooks はガードレールを実装する場所であると同時に、それ自体がホスト権限で動く実行経路でもあります。この二面性については第4回で扱います。MCP サーバーが外側で動く点は第5回の主題です。
ファイルシステムとネットワークの扱い
サンドボックス内シェルの制約は次の通りです。
| 対象 | 挙動 |
|---|---|
| ファイル読み取り | 既定ではほぼ全域を読める。~/.ssh や ~/.aws/credentials も含まれる。塞ぐには sandbox.credentials または denyRead の明示設定が必要 |
| ファイル書き込み | ワーキングツリーの外へは書き込めない |
| ネットワーク | サンドボックス外のプロキシサーバー経由で制御される。既定では許可ドメインが1つもない。新しいドメインへのリクエストは許可プロンプトを出す |
ネットワークが既定で全閉なのは、実務上かなり効きます。データを持ち出す経路が最初から塞がっており、外に出したいドメインを1つずつ明示的に開けていく形になるためです。第1回で触れた「機密データ・非信頼コンテンツ・外部通信の3つが揃うと危険」という整理でいえば、3つ目の要素を既定で欠かせる仕組みです。
一方で、ファイル読み取りは「ほとんど読める」側に倒れています。ここは誤解しやすい点です。公式ドキュメントは、既定の読み取り挙動がクラウド認証情報や SSH 鍵の読み取りまで許すことを明記しており、sandbox.credentials を設定して初めてそれらが塞がれます。サンドボックスを有効にしただけでは認証情報は守られません。守るのは deny と sandbox.credentials の仕事です。
併用の設計
以上を踏まえると、責任分担は次のようになります。
- サンドボックス: Bash の副作用範囲を物理的に制限する。書き込み先とネットワークの上限を決める
- permissions: ツール横断で、意味的に危険な操作を止める。Read / Edit / MCP にも効く
- hooks: パターンでは書き分けられない条件付きの判断を実装する
3層のどれか1つだけでは穴が残ります。とくに「サンドボックスを有効にしたので permissions は緩めてよい」という判断は、Read ツールの経路を素通しにします。
5. パターンマッチの限界と、その先の解き方
permissions のパターンは文字列に対するマッチングです。シェルの表現力は文字列のバリエーションとして極めて広いため、「危険なコマンド」という概念をパターンで完全に覆うことはできません。同じ結果をもたらす書き方が無数にある以上、禁止リストは常に不完全です。
これは実装の欠陥ではなく、この種の仕組みが本質的に抱える性質です。したがって、設計上の結論はこうなります。
denyは「事故の頻度を下げる仕掛け」であって、「絶対に実行されない保証」ではありません。
保証が必要な操作は、パターンではなくそもそも到達できない状態で解きます。具体的には次の方向です。
| 解き方 | 内容 | 効果 |
|---|---|---|
| 認証情報を手元に置かない | 本番のクレデンシャルを開発マシンに配置しない。開発マシンから見えるのは開発・staging の認証情報だけにする | パターンをすり抜けても、到達先が本番でなくなる |
| 権限を絞った資格情報を使う | 開発用の資格情報に読み取り専用や限定スコープを与える | 実行できても影響範囲が小さい |
| 本番操作を別経路に寄せる | デプロイやマイグレーション適用を CI から実行し、その資格情報は CI 側だけが持つ | 開発マシンの権限設計とは独立に守られる |
| 組織ポリシーで下限を固定する | managed の deny に絶対禁止を置く | 個人設定の緩め方に依存しなくなる |
いちばん効くのは1行目です。開発マシンに本番の認証情報が存在しなければ、エージェントが何をどう実行しても本番には届きません。 権限設計に頼らずに済む状態を作ることが、権限設計の目的でもあります。
なお --dangerously-skip-permissions で起動した場合も deny のルールは効き続けます。ただしこのフラグは「確認をやめる」意思表示なので、隔離された環境以外で常用するものではありません。CI や使い捨てコンテナのように、壊れても捨てられる環境に限定します。
CI 側のガードレール設計については、CI/CDガードレールと第10回で扱います。
6. チームで運用するときの層の分け方
個人で使う分には設定ファイルは1つで足りますが、チームでは「誰が何を決めてよいか」を層で分けます。マージされる仕組みを踏まえると、次の分担が素直です。
| 層 | 誰が書くか | 何を書くか | 書かないもの |
|---|---|---|---|
| managed | 情報システム部門・セキュリティ担当 | 組織として絶対に譲らない禁止。認証情報の読み取り、本番到達コマンド、publish | プロジェクト固有の事情、個人の好み |
| project(プロジェクト共有設定) | リポジトリのメンテナ | そのリポジトリ固有の禁止と許可。使うパッケージマネージャ、テストコマンド、触ってはいけないディレクトリ | 他リポジトリにも共通する組織方針 |
| user(個人共通設定) | 各開発者 | 個人の快適さ。よく使う調査系コマンドの許可、defaultMode の好み | チームで合意すべき禁止事項 |
| local(プロジェクト個人設定) | 各開発者 | そのリポジトリでの個人的な追加許可。承認の蓄積先 | 共有したいルール |
運用上のポイントを3つ挙げます。
managed に書くのは禁止だけにします。 managed の allow を充実させると、各リポジトリの事情に合わなくなったときに、現場が回避策を探し始めます。禁止だけを最小限に置き、許可はプロジェクト層に委ねるほうが長持ちします。
project 層は Git 管理下に置き、レビュー対象にします。 権限設定の変更は、コードの変更と同じく差分レビューを通すべきものです。誰かが deny を1行消したことに気づけない状態は避けます。
local 層は Git 管理外にします。 「今後確認しない」という承認はここに蓄積されるため、共有すると他人の判断が黙って自分に適用されます。ここは個人のスコープに留めます。
新しくジョインしたメンバーが最初にやることは、project 層の設定を読むことです。禁止事項がなぜそこにあるのかをコメントやドキュメントで残しておくと、あとから「これ何でしたっけ」で消される事故を防げます。
7. 出発点にする設定例
以下は、Web アプリケーション開発のリポジトリを想定した project 層の共有設定の例です。そのままコピーして、プロジェクトの事情に合わせて足し引きする出発点として使えます。
{
"permissions": {
"defaultMode": "acceptEdits",
"deny": [
"Read(./.env*)",
"Read(./**/*.pem)",
"Read(./**/*.key)",
"Read(~/.aws/**)",
"Read(~/.ssh/**)",
"Bash(env)",
"Bash(printenv*)",
"Bash(git push --force*)",
"Bash(git push -f*)",
"Bash(git reset --hard*)",
"Bash(git clean*)",
"Bash(npm publish*)",
"Bash(npx wrangler deploy*)",
"Bash(npx supabase db push*)",
"Bash(psql*)"
],
"ask": [
"Bash(docker *)",
"Bash(npm install *)",
"WebFetch"
],
"allow": [
"Read",
"Edit",
"Bash(npm run test*)",
"Bash(npm run lint*)",
"Bash(npm run build)",
"Bash(npx tsc*)",
"Bash(git status)",
"Bash(git diff*)",
"Bash(git log*)",
"Bash(git add *)",
"Bash(git commit *)"
]
}
}
この例で意図している判断を補足します。
denyの前半は第2節の「認証情報の読み取り」に対応します。Readと Bash の両面から塞いでいます。秘密を含むファイルを Bash 経由で読み出すコマンド名は環境によって変わるため、自分の環境で実際に使われる読み出し系コマンドを追加しますgit addとgit commitはallowに置いています。コミットは巻き戻せる操作であり、影響がリポジトリ内で閉じるためです。一方 push 系はallowに入れていませんnpm installをaskに置いているのは、依存追加が第11回で扱うサプライチェーンの入口だからです。禁止するほどではないものの、人間が一度見る価値がありますallowにBash単体を書いていません。ここを開けるとdenyに書き漏らした全てのコマンドが素通しになります
組織で managed 層を用意できる場合は、deny のうち「認証情報の読み取り」「publish」「本番到達コマンド」を managed へ移し、project 層にはリポジトリ固有のものだけを残す形にします。
まとめ
denyの基準は「実行結果を自分の手で元に戻せるか」の一点です。認証情報の読み取り、本番到達コマンド、履歴破壊、publish の4カテゴリを最低限入れますallowには「巻き戻せる」「影響がリポジトリ内で閉じる」「失敗に気づける」の3条件をすべて満たすものだけを置きます。2つしか満たさないものはaskに回します- サンドボックスが適用されるのは Bash ツールとその子プロセスだけで、Read / Edit / MCP / hooks はホスト上で動きます。permissions との併用が前提です
- サンドボックスのネットワークは既定で許可ドメインなしですが、ファイル読み取りは広く許されています。プロジェクトの秘密ファイルを守るのは
denyの役割です - パターンマッチは万能ではありません。確実に止めたい操作は「本番の認証情報を開発マシンに置かない」という到達不能化で解きます
- チームでは managed に絶対禁止、project にリポジトリ固有、user と local に個人の快適さ、という層分けにします。project 層はレビュー対象にします
参考リンク
- Claude Code Permissions(公式ドキュメント) — allow / deny / ask の評価順序とスコープのマージ
- Claude Code Sandboxing(公式ドキュメント) — 適用範囲、ファイルシステムとネットワークの制約
- Claude Code インストールと初期設定ガイド — 本記事の前提となる設定記法
- 第1回 AIで作る時代の攻撃面 — 3層モデルと責任分界
- 第4回 Hooks でガードレールを組む — パターンで書けない判断の実装
- CI/CDガードレール — 本番操作を CI 側へ寄せる設計