Claude Code Sandboxの設定—~/.sshまで読める初期状態の塞ぎ方

Claude Code Sandboxの設定—~/.sshまで読める初期状態の塞ぎ方 | mohablog

Claude Code 2.1.150/sandbox を有効にすると、Bash コマンドの承認プロンプトがほぼ消えます。OS が作業ディレクトリの外への書き込みを止めてくれるから。ただ読み取りは初期状態でほとんど素通しで、~/.ssh の秘密鍵まで見えます。

目次

Sandboxを有効にするとBash承認がどう変わるか

サンドボックスは Claude Code に組み込み済み。macOS / Linux / WSL2 で動きます。コマンドごとに承認する代わりに、触れてよいファイルとネットワークドメインを先に決め、その境界を OS が全 Bash コマンドとその子プロセスに強制する仕組み。Anthropic の社内利用では、サンドボックスで承認プロンプトが 84% 減ったと報告されています(出典: engineering ブログ “Making Claude Code more secure and autonomous”)。

/sandboxパネルの3つのタブ

セッション中に /sandbox を打つとパネルが開きます。タブは3つ。

  • Mode: サンドボックス内のコマンドをどう承認するか選ぶ
  • Overrides: 失敗したコマンドを非サンドボックスで再実行させるか(allowUnsandboxedCommands
  • Config: 解決済みのサンドボックス設定を確認する

依存パッケージが足りない環境では Dependencies タブだけが出ます。不足分を入れて Claude Code を再起動すれば、残りのタブが現れる。

Auto-allowとRegular permissionsの違い

Mode タブで選べるのは2つ。Auto-allow はサンドボックス内のコマンドを承認なしで走らせます。Regular permissions はサンドボックス内でも通常の承認フローを通す。公式ドキュメントの “Sandbox modes” セクションはこう書いています。

In both modes, the sandbox enforces the same filesystem and network restrictions. The difference is only in whether sandboxed commands are auto-approved or require explicit permission.

隔離の強さはどちらも同じ。違いは承認を自動化するかどうかだけ。

settings.jsonで全プロジェクトに効かせる

パネルでの選択はプロジェクトの .claude/settings.local.json に書かれます。git には載らない。全プロジェクトで有効にするなら、ユーザー設定 ~/.claude/settings.json に書きます。

{
  "sandbox": {
    "enabled": true
  }
}

この1行で、以降のセッションは Bash コマンドを自動でサンドボックスに入れます。作業ディレクトリの外へ書こうとすると、OS の段階で止まる。

macOSはSeatbelt、Linux/WSL2はbubblewrap+socat

隔離は OS のセキュリティ機構に乗ります。実装はプラットフォームで分かれる。

プラットフォーム隔離機構追加インストール
macOSSeatbelt不要
Linuxbubblewrapbubblewrap + socat
WSL2bubblewrap同上
ネイティブ Windows非対応WSL2 上で動かす

LinuxとWSL2で要る2つのパッケージ

Linux と WSL2 では2つ要ります。bubblewrap がファイル隔離を、socat がネットワークをプロキシへ中継する役。

sudo apt-get install bubblewrap socat
Setting up bubblewrap (0.8.0-2) ...
Setting up socat (1.7.4.4-2) ...

入れた後、/sandbox の Dependencies タブで ripgrepbubblewrapsocat、seccomp フィルタの有無を確認できます。seccomp フィルタは Unix ドメインソケットの遮断に使う任意の部品。欠けていれば npm install -g @anthropic-ai/sandbox-runtime で入る。依存チェックは起動時に走るので、インストール後は Claude Code の再起動が要ります。

ネイティブWindowsは動かない

ネイティブ Windows は非対応。bubblewrap が WSL2 のカーネル機能を要求するためで、WSL1 でも動きません。Windows では WSL2 ディストリビューション内で Claude Code を起動する。

書き込みは作業ディレクトリだけ、でも読み取りは初期状態で全開放

多くの解説は「有効化すれば安全」で止まります。実際は、書き込みと読み取りで初期ポリシーが大きく違う。書き込みは作業ディレクトリ配下だけ。一方の読み取りは、ほぼコンピュータ全体に通ります。公式ドキュメントの “Filesystem isolation” セクションが、わざわざ注記しているくらいです。

read access to the entire computer, except certain denied directories. Note that this default still allows reading credential files such as ~/.aws/credentials and ~/.ssh/. Add them to denyRead to block them.

有効化した直後、試しに cat ~/.ssh/id_ed25519 を走らせたら、秘密鍵の中身がそのまま返ってきました。

cat ~/.ssh/id_ed25519
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAA...
-----END OPENSSH PRIVATE KEY-----

書き込みは止まるのに、読み取りは通る。プロンプトインジェクションで踏まされたコマンドが秘密鍵や ~/.aws/credentials を読み、許可済みのドメインへ送れば、それで漏れます。ネットワーク隔離と組み合わせて初めて意味を持つ理由がここ。

denyReadで資格情報ディレクトリを塞ぐ

初期状態で読める資格情報は、denyRead で明示的に閉じます。

{
  "sandbox": {
    "enabled": true,
    "filesystem": {
      "denyRead": ["~/.ssh", "~/.aws", "~/.config/gcloud"]
    }
  }
}

この設定なら、さっきの cat ~/.ssh/id_ed25519 は OS の段階で弾かれます。

cat: /Users/you/.ssh/id_ed25519: Operation not permitted

プロジェクトだけ読めれば十分なとき

資格情報を1つずつ列挙する代わりに、ホーム全体を閉じてプロジェクトだけ開ける手もあります。

{
  "sandbox": {
    "enabled": true,
    "filesystem": {
      "denyRead": ["~/"],
      "allowRead": ["."]
    }
  }
}

ただし allowRead. は、この設定をプロジェクトの .claude/settings.json に置いたときだけプロジェクトルートに解決されます。同じ設定を ~/.claude/settings.json に置くと .~/.claude を指し、プロジェクトファイルは denyRead 側に残って読めなくなる。置き場所で意味が変わる点に気をつけてください。

allowWriteでkubectlやterraformに書かせる

kubectlterraformnpm のように作業ディレクトリの外へ書くツールは、allowWrite で書き込み先を足します。

{
  "sandbox": {
    "enabled": true,
    "filesystem": {
      "allowWrite": ["~/.kube", "/tmp/build"]
    }
  }
}

パスは OS レベルで効くので、Claude のファイルツールだけでなく、コマンドが生む子プロセス全部が従います。ツールごと excludedCommands でサンドボックスから外すより、書き込み先を1つ足すほうが隔離を保てる。なお linked worktree で作業しているときは、git commit が ref を更新できるよう、メインリポジトリの共有 .git への書き込みも許可されます。ただし hooks/config への書き込みは閉じたまま。

ネットワーク隔離はプロキシ経由、ドメインは初回に承認

ネットワークは、サンドボックスの外で動くプロキシ経由でだけ通ります。初期状態で許可済みのドメインはゼロ。公式ドキュメントの “Network isolation” セクションは no domains are pre-allowed と明記しています。新しいドメインに接続しようとするたび、初回だけ承認を求められる。

allowedDomainsで初回プロンプトを省く

毎回聞かれたくないドメインは allowedDomains に先回りで登録します。

{
  "sandbox": {
    "enabled": true,
    "network": {
      "allowedDomains": ["registry.npmjs.org", "pypi.org", "github.com"]
    }
  }
}

npm installpip install が、承認を挟まずにレジストリへ届くようになります。

github.com全許可が穴になる理由

広いドメインを開けると、それ自体が抜け道になります。組み込みプロキシは、クライアントが申告したホスト名で許可を判断するだけ。TLS は終端も検査もしません。”Security limitations” セクションが警告しているのはこの点です。

Allowing broad domains such as github.com can create paths for data exfiltration. … code running inside the sandbox can potentially use domain fronting or similar techniques to reach hosts outside the allowlist.

github.com を丸ごと許可するだけで、ドメインフロンティングで許可リスト外のホストへ到達される余地が残ります。TLS を検査したいなら、終端する独自プロキシを立てて CA 証明書をサンドボックス内に入れます。deniedDomains は、広いワイルドカード許可の中から特定ドメインだけを個別に閉じる用途。

permissionsやpermission modesとの違いを整理する

サンドボックス、permission ルール、permission mode は別レイヤー。何を制御し、プロンプトの代わりに何が効くかで切り分けると整理しやすい。

仕組み制御するものプロンプトの代わりになるもの
/sandboxBash コマンドが実行後に触れられる範囲OS が強制する境界そのもの
Auto mode各ツール呼び出しを実行するか行動を審査する分類器
--dangerously-skip-permissions各ツール呼び出しを実行するかなし(保護パスのチェックも外れる)

permission ルールはコマンドが走る前に評価され、全ツールに効きます。サンドボックスは走った後のプロセスに OS が境界を強制するので、モデルが何を実行したかに関係なく効く。サンドボックスの Auto-allow と Auto mode も別物です。前者は境界で封じ込めるから承認を省く、後者は分類器が行動を審査する。permission ルールの組み立て方はClaude Code permissions—allow/denyの落とし穴と評価順序で扱っています。

docker・jest・ghがSandboxで動かないとき

サンドボックスの中で素直に動かないコマンドがあります。多くは隔離との相性問題で、外へ逃がせば解決する。

  • docker: サンドボックス非対応。docker *excludedCommands に入れて外で走らせる
  • jest: watchman が非対応。jest --no-watchman で回す
  • gh / gcloud / terraform: Go 製 CLI は macOS の Seatbelt 下で TLS 検証に失敗することがある。excludedCommands
{
  "sandbox": {
    "enabled": true,
    "excludedCommands": ["docker *", "gh *", "gcloud *"]
  }
}

個別に外すほかに、逃げ道もあります。コマンドがサンドボックス制約で落ちると、Claude が失敗を分析し、dangerouslyDisableSandbox を付けて外で再実行することがある。これは通常の承認フローを通るので、実行前にこちらの許可が要ります。承認なしの自動実行を絶対に許したくない環境では、allowUnsandboxedCommandsfalse にして逃げ道ごと塞ぐ。Overrides タブで Strict sandbox mode と表示される状態です。

組織でSandboxを強制するmanaged settings

開発者ごとの設定に任せず、組織で必須化するなら managed settings に sandbox キーを置きます。boolean のキーは managed の値が優先され、ローカルの上書きは無視される。

{
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "allowUnsandboxedCommands": false
  }
}

failIfUnavailable は、bubblewrap 欠如などでサンドボックスが起動できないとき、警告で素通しせず Claude Code の起動自体を止めます。あわせて資格情報ディレクトリの denyRead を managed 側に入れておくと、初期状態の読み取り全開放を組織全体で塞げる。allowManagedReadPathsOnlytrue にすれば、開発者が allowRead で読み取り範囲を広げるのも防げます。

まとめ

Claude Code 2.1.150 のサンドボックスは、Bash の承認を OS 境界に肩代わりさせる仕組み。導入時に押さえる点はこのあたり。

  • 有効化は sandbox.enabledtrue にするだけ。macOS は追加インストール不要、Linux/WSL2 は bubblewrapsocat が要る
  • 書き込みは作業ディレクトリ配下のみ。一方で読み取りは初期状態でほぼ全開放、~/.ssh~/.aws/credentials も読める
  • 資格情報は denyRead で塞ぐ。ホーム全体を閉じてプロジェクトだけ allowRead で開ける手もある
  • ネットワークは初期状態で全ドメイン未許可。allowedDomains で先回り登録、広域許可は TLS 非検査ゆえの抜け道に注意
  • docker や Go 製 CLI は excludedCommands で外へ逃がす

有効化しただけでは、読み取りとネットワーク広域許可の2つに穴が残ります。denyReadallowedDomains の絞り込みまでやって、ようやく隔離が噛み合う。

よくある質問

Q. サブエージェントの Bash コマンドもサンドボックスされますか?
サブエージェントは親セッションと同じプロセスで動き、サンドボックス設定も共有します。親で有効なら、サブエージェント内の Bash も隔離対象。

Q. サンドボックス内のコマンドに渡る環境変数の資格情報を消したい
サンドボックス化された Bash は親プロセスの環境変数を引き継ぎます。Anthropic やクラウドプロバイダの資格情報を子プロセスから外すには、CLAUDE_CODE_SUBPROCESS_ENV_SCRUB を設定します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次