Claude Code のサンドボックスは、Bash コマンドをファイルシステム・ネットワークの境界内で走らせる安全機構です。ですが見落とされがちな既定があります——サンドボックスの読み取りポリシーは「コンピュータ全体を読み取り可」で、~/.aws/credentials や ~/.ssh といった認証情報ファイルもエージェントから読めてしまう。さらにサンドボックス内の Bash コマンドは親プロセスの環境変数を継承するので、GITHUB_TOKEN などの秘密もそのまま渡ります。v2.1.187 で追加された sandbox.credentials は、この穴を塞ぐための設定です。本記事で、既定の露出範囲と守り方を整理します。関連する権限制御は auto mode の hard_deny も参照。
到達点
- サンドボックス既定では認証情報が読めてしまうことを理解する
sandbox.credentialsのfiles/envVarsとdenyの使い方denyとmaskの違い(ツールを壊すか、動かしたまま隠すか)- スコープと安全設計(deny は誰でも追加可・除去不可、mask は信頼スコープのみ)
- 組織での強制と、補助の 環境変数スクラブ
まず既定の露出を知る
サンドボックスの既定のファイル読み取りはこうです。
- 書き込み:作業ディレクトリとセッション一時ディレクトリのみ
- 読み取り:コンピュータ全体(一部の拒否ディレクトリを除く)
つまり書き込みは厳しく制限される一方、読み取りはほぼ全開。公式ドキュメントも明記していますが、この既定は ~/.aws/credentials や ~/.ssh/ の読み取りを許してしまいます。加えて、環境変数の面でも:
サンドボックス内の Bash コマンドは、既定で親プロセスの環境変数を継承する。そこに設定された認証情報を含む。
エージェントが実行するコマンド(やそのサブプロセス)が、あなたのクラウド認証情報・SSH 鍵・トークンを読める——これが出発点の問題です。組み込みの拒否リストは無いため、放置すると露出したままになります。
sandbox.credentials(v2.1.187+)
この設定で、守るべき認証情報ファイルと環境変数を宣言します。sandbox.enabled: true が前提です。
{
"sandbox": {
"enabled": true,
"credentials": {
"files": [
{ "path": "~/.aws/credentials", "mode": "deny" },
{ "path": "~/.ssh", "mode": "deny" }
],
"envVars": [
{ "name": "GITHUB_TOKEN", "mode": "deny" },
{ "name": "NPM_TOKEN", "mode": "deny" }
]
}
}
}
"mode": "deny" の挙動:
files:サンドボックス内でその読み取りを拒否(filesystem.denyReadと同じ制限)。ファイルはdenyのみ対応envVars:サンドボックス化された各コマンドの実行前に、その環境変数を unset(除去)する
credentials ブロックにまとめることで、一般的な filesystem ルールと認証情報ルールを分離して管理できます。ファイルパスは sandbox.filesystem.* と同じ prefix ルール(/=絶対、~/=ホーム、./=プロジェクト)に従います。
deny と mask の使い分け
deny には弱点があります——変数を完全に消すため、それを必要とするツール(gh や npm など)が壊れます。そこで v2.1.199 以降、環境変数には "mode": "mask" が使えます。
**mask は「認証情報を守りつつ、認証するツールは動かす」**ための仕組みです。
- サンドボックス内のコマンドには、実値の代わりにセッションごとのセンチネル値が見える
- リクエストが指定ホスト(
injectHosts)向けにサンドボックスを出るとき、プロキシがセンチネルを実値に置換する - コマンド本体やそのログには実値が残らないが、リクエストは正しく認証される
{
"sandbox": {
"enabled": true,
"network": {
"tlsTerminate": {},
"allowedDomains": ["*.github.com", "registry.npmjs.org"]
},
"credentials": {
"envVars": [
{ "name": "GH_TOKEN", "mode": "mask", "injectHosts": ["api.github.com"] },
{ "name": "NPM_TOKEN", "mode": "mask" }
]
}
}
}
上の例では、GH_TOKEN は api.github.com へのリクエスト時のみ、NPM_TOKEN は injectHosts 無しなので allowedDomains の全ホストで置換されます。
mask の必須条件と注意
network.tlsTerminateが必須:プロキシがリクエスト内容(≒ HTTPS)を見て置換するため、プロキシが TLS を終端する必要があります。無いと fail closed(センチネルのまま送られ認証失敗)。この設定ミスは起動時と/doctorで報告されますinjectHostsはallowedDomainsに含まれている必要がある- 信頼スコープからのみ有効:mask はプロキシに実値の送信を許可するため、user 設定・managed 設定・
--settingsフラグからのみhonored。リポジトリの.claude/settings.json等に書いたmask/tlsTerminateは無視されます(悪意あるリポジトリ設定で鍵を外部に送らせない安全設計) - 同じ変数が
denyでも指定されたらdenyが優先
| deny | mask | |
|---|---|---|
| 値の見え方 | 消える(unset) | センチネル(偽値) |
| ツール動作 | 壊れうる | 動く |
| 必要条件 | なし | tlsTerminate + 信頼スコープ |
| 対象 | files / envVars | envVars のみ(v2.1.199+) |
スコープと安全設計
denyは「狭める」だけ:どのスコープからでもdenyを追加できるが、あるスコープが追加したdenyを別スコープが除去することはできない。プロジェクトが緩めても、user/managed の deny は残るmaskは信頼スコープのみ:前述の通り、リポジトリ設定からは効かない- 設定ファイル自体は保護:サンドボックスは全スコープの
settings.jsonと managed 設定ディレクトリへの書き込みを自動拒否するので、サンドボックス内コマンドが自分のポリシーを書き換えられない
補助:全サブプロセスから認証情報を剥ぐ
sandbox.credentials はサンドボックス化された Bash コマンドにのみ効きます。サンドボックスの有無に関わらずすべてのサブプロセスから Anthropic・クラウドプロバイダの認証情報を剥ぎたいなら、環境変数 CLAUDE_CODE_SUBPROCESS_ENV_SCRUB を使います。用途に応じて併用してください。
実務レシピ
- サンドボックスを有効化:
sandbox.enabled: true(macOS / Linux / WSL2) - 鍵ファイルを deny:
~/.aws・~/.ssh・~/.config/gcloudなど、コマンドが読む必要のない認証情報ディレクトリをcredentials.filesにdeny - 不要な秘密の環境変数を deny:コマンドが使わないトークンは
denyで unset - 使うトークンは mask:
gh・npm等が必要とするものはdenyでなくmaskにし、tlsTerminate+allowedDomains+injectHostsを設定(user/managed 設定に置く) - 組織なら managed 設定で強制:
enabled/failIfUnavailableを managed で固定し、credentialsの deny をベースラインとして配布
注意点
- 組み込みの拒否リストは無い:列挙したものだけが守られます。設定しなければ既定のまま露出します。ここが最重要。
- mask は tlsTerminate 前提:忘れると認証が静かに壊れます(fail closed)。
/doctorで確認を - mask をリポジトリ設定に書いても効かない:必ず user / managed /
--settingsに置く - サンドボックス外は別管理:Read/Edit ツールやサブエージェントのファイルアクセスは権限システム側。サンドボックスは Bash サブプロセスの境界です
- ネットワークも併せて締める:読み取りを塞いでも、広い
allowedDomainsがあると持ち出し経路が残ります。ファイルとネットワークは両輪で締める
まとめ
Claude Code のサンドボックスは書き込みには厳しい一方、既定の読み取りはほぼ全開で、~/.aws/credentials や ~/.ssh、継承された GITHUB_TOKEN などがエージェントから読める状態です。sandbox.credentials(v2.1.187+)で、**ファイルの読み取り拒否と秘密の環境変数の除去(deny)**を宣言し、ツールを動かしたまま値を隠したいものは mask(v2.1.199+、tlsTerminate 必須・信頼スコープのみ)にする——これが基本形です。
勘所は 3 つ — 組み込みの拒否リストは無いので必ず自分で列挙する、使うトークンは deny でなく mask にしてツールを壊さない、mask は user/managed 設定に置いて tlsTerminate を忘れない。エージェントに自律的に Bash を任せるほど、「秘密を読ませない」この一手が効いてきます。まず ~/.aws と ~/.ssh の deny から始めるのがおすすめです。