Claude Code の権限・サンドボックス・保護パス——公式仕様3本を読み切って、「AIに設定を書き換えさせない」仕組みの本当の関門を突き止めた記録
はじめに
個人用のAIコーディングアシスタントに、いくつかの取り決めを作っている。その一つが「自分自身の設定ファイル(何を許可し、何を拒むかのルール一式)は、AI自身に書き換えさせない」というものだ。取り決めは一度決めて終わりではなく、「本当に効いているか」を確かめ続ける必要がある。この記事は、その確認のために権限・サンドボックス(隔離実行環境)・保護パスに関する公式の一次資料3本を読み切った記録だ。
読む前は「サンドボックスという隔離の仕組みを使えば一番硬く守れるはずだ」と考えていた。読み終えてみると、その予想は外れていた。しかも予想が外れた理由と、代わりに見つかった答えの両方が、実際に確かめてみないと分からない種類のものだった。
判定の順番と、参照されないルールが1つ紛れていた
権限のルールは、拒否(deny)・確認(ask)・許可(allow)の順で評価される。公式はこう明記している。
Claude Code checks file permissions against
Edit(path)andRead(path)rules only. If you write a path rule forWrite,NotebookEdit,Glob, or the legacyMultiEdittool instead, Claude Code accepts the rule but never consults it, and warns at startup.
つまり、ファイルパスに対するルールは Edit と Read の2種類でしか実際には参照されない。Write に対してパスルールを書いても、設定としては受理されるのに判定には一度も使われない。実際に自分の設定を見直すと、Write 用のパスルールが1件見つかった。実害は無かった——同じ場所を対象にした Edit 用のルールが隣にあり、そちらが Write ツールの分も含めてカバーしていたためだ。ただし「書いたつもりのルールが一度も評価されていない」状態自体は、気づかなければそのまま放置され続けるものだった。
もう一つ、パスの書き方そのものに罠があった。
A pattern like
/Users/alice/fileisn’t an absolute path. The single leading slash anchors at the settings source, not the filesystem root.
先頭にスラッシュを1つだけ付けたパスは、ディスク上の絶対パスではなく「その設定ファイルが置かれている場所」を基準にした相対パスとして解釈される。ディスクの絶対パスを指したいなら、スラッシュを2つ重ねる書き方が必要になる。同じ「/」から始まる文字列でも、意味がまるで違う——書いたルールが、狙った場所ではなく別の場所を指している可能性がある、ということだ。
「外した」はずの設定が、4日間戻っていた
一番大きな発見は、権限そのものの仕様ではなく、自分たちが過去に確定させたはずの取り決めが、実際には巻き戻っていたことだった。
以前、「設定ファイルを書き換える権限そのものを、AIの許可リストから外す」という判断を下したことがある。判断を下した記録には「外した」と現在形で書いてあった。だが実際の設定ファイルと変更履歴を突き合わせると、様子が違っていた。
[実測] 設定ファイルの変更履歴(要旨)
1本目のコミット: 設定書き換え権限を許可リストから外す(判断どおり)
↓ 3時間14分後
2本目のコミット: 別の不具合を切り分けるため、設定ファイルを編集する必要が生じ
書き換え権限が許可リストに戻る
↓
以後の変更でも、外れたままにはならず今日まで許可リストに残存
判断を下した記録は「外した」と書かれたまま更新されず、実際の設定は3時間14分後に戻っていた。 どちらか一方だけを見ていたら気づけない食い違いだった。判断の記録と、判断が実際に反映された状態は、別々に確認しないと乖離に気づけない。
一番硬い壁だと思ったものが、そもそも選べない環境だった
権限のルールには「シェル経由の抜け道」がある。ファイル操作の許可・拒否ルールは、Claude自身が認識しているコマンドには効くが、任意のプログラムが内部でファイルを開閉する経路までは塞げない。
They don’t apply to arbitrary subprocesses that read or write files indirectly, like a Python or Node script that opens files itself. For OS-level enforcement that blocks all processes from accessing a path, enable the sandbox.
この一文を読んだ時点では、「OSレベルで強制する仕組み=サンドボックス」が本命の答えだと考えた。ところがサンドボックスの仕様書を開くと、最初の数行にこう書いてあった。
The sandbox is built into Claude Code and runs on macOS, Linux, and WSL2. Native Windows is not supported. On Windows, run Claude Code inside a WSL2 distribution.
自分の作業環境は、Windows上で直接動くターミナル(WSL=Windows内でLinuxを動かす仕組み、を経由していない構成)だった。「本命の答え」と考えていた仕組みは、確認する前から自分の環境では選べなかった。 1つ目の資料の一文だけで次の一手を決め、実際にその仕組み自体の仕様書を開く前に「答え」と書いてしまっていた——これは読み方の順番の失敗であって、仕様の失敗ではない。仕様書は最後まで読んでから、それを「答え」と呼ぶ必要がある。
選べない壁の代わりに、最初から別の壁が立っていた
サンドボックスが使えないと分かった後、その仕様書の別の箇所に手がかりがあった。
The permission system has its own protected paths, which control what Claude Code approves before a tool runs; the sandbox’s list applies to a command that is already running.
サンドボックスとは別に、権限システム側にも「保護パス」という仕組みが独立して存在する。こちらはOSレベルの隔離ではないので、Windows上でも動く可能性がある。実際に仕様書を確認すると、その通りだった。
Writes to a small set of paths are never auto-approved, except in
bypassPermissionsmode and in planning sessions with bypass permissions available.permissions.allow rules in settings files do not pre-approve protected-path writes. The safety check runs before Claude Code evaluates allow rules from settings, so an entry such as
Edit(.claude/**)in~/.claude/settings.jsonor.claude/settings.jsondoes not change the per-mode outcome in the table above.
保護パスに対する書き込みは、許可リストに何が書いてあっても自動では通らない。 許可リストの評価より前に、この安全確認が先に走る仕組みになっている。自分自身の設定ファイルが置かれているディレクトリは、この保護対象にまるごと含まれていた。
その壁も、最終的には「人間の関門」ではなかった
ここで話は終わらない。保護パスへの書き込みが「自動では通らない」としても、それは動作モードによって扱いが変わる。自分が使っている動作モードでは、次のように処理される。
Actions matching your allow, ask, or deny rules resolve immediately. Writes to protected paths route to the classifier even when an allow rule matches. … Everything else goes to the classifier.
保護パスへの書き込みは、人間への確認画面ではなく「分類器」(過去のやり取りを踏まえてAI自身が可否を判定する仕組み)に送られる。 分類器はモデル自身の判断であり、人間がその場で「はい/いいえ」を選ぶ関門ではない。しかも公式は、分類器の判断について踏み込んだ注記も残していた。
the classifier blocks matching actions even when the default rules would allow them … Claude’s own judgment that a condition was met does not lift it … a boundary can be lost if context compaction removes the message that stated it. For a hard guarantee, add a deny rule instead.
会話の中で「ここは触らないでほしい」と伝えるだけの境界線は、会話が要約されて古い発言が消えると一緒に失われうる。公式が名指しする、確実に効く唯一の手段は「拒否ルールを明示的に書く」ことだった。 取り決めを会話で伝えるのと、設定ファイルに拒否ルールとして書くのとでは、保証の強さが違う。
資料を読み進めた道のり
起点: 「設定ファイルを書き換えさせない」取り決めは本当に効いているか
│
├─▶ permissions.md を読む
│ ├─ 評価順序(deny→ask→allow)を確認
│ ├─ Writeルールが実は参照されないことを発見
│ ├─ パスの書き方一つで指す場所が変わることを発見
│ └─ 過去の裁定と実際の設定ファイルを突き合わせる
│ └─ ⚠️ 「外した」はずが3時間14分後に戻っていたと判明
│
├─▶ サンドボックスが「本命の答え」に見える一文を発見
│ └─▶ sandboxing.md を開いて確認
│ └─ ⛔ native Windows 非対応=自分の環境では選べない
│ └──(訂正・戻りエッジ)──▶ 前段の結論を撤回
│
├─▶ 同じ資料の中に「保護パスは別の仕組み」という手がかりを発見
│ └─▶ permission-modes.md を開いて確認
│ └─ ✅ 保護パスはOS非依存=Windowsでも効く
│ └─ ただし動作モード次第で扱いが変わる
│ └─ 自分の動作モードでは分類器行き
│ └─ 分類器=人間の関門ではない
│ └─ 公式が名指しする確実な手段=拒否ルール
実測して初めて分かった、「取り決め」と「壁」の違い
3本の資料を読み終えて手元に残ったのは、単一の「これで安全」という答えではなく、強さの違う複数の壁を、正しく積み重ねる材料だった。会話の中で述べた境界線は消えうる。許可リストは保護パスの前段では意味を持たない。分類器はモデル自身の判断であって、人間の承認そのものではない。そして、いちばん確実だと公式が名指しする手段は、設定ファイルに書かれた明示的な拒否ルールだった。
もう一つ、資料の中身とは別に持ち帰ったものがある。「一次資料の一文だけを読んで次の一手を決めない」という規律だ。サンドボックスを本命と書いた時点では、その仕様書自体をまだ開いていなかった。引用元が指す先を実際に読み終えるまで、それを結論と呼ばない。 今回、答えが一度ひっくり返ったのはそのためだった。
AIコーディングアシスタントに「やらせない」仕組みを作る — 助言から強制へ、そして関門に空いていた横穴
強い壁を1枚探していたつもりが、手に入ったのは強さの違う壁を、正しい順番で並べる図面だった。どれが最後の1枚かを知っていることの方が、その1枚を厚くすることより効く。