Claude Codeのサンドボックスに追加された「mask」モードを使うと、APIキーを守りながらghやnpmをそのまま動かせるのでしょうか——答えはイエスで、サンドボックス内のコマンドには偽の値(センチネル)を見せ、許可したホストへの送信時だけプロキシが本物に差し替えることで両立します。
2026年8月4日リリースのClaude Code v2.1.221で、サンドボックスの認証情報保護に"mode": "mask"のファイル対応が加わりました。従来のdeny(読み取り禁止)は安全ですが、認証ファイルを読めなくなった時点でghのようなツール自体が動かなくなるという副作用がありました。maskモードはこのトレードオフを解消する仕組みです。検証環境(Ubuntu 24.04 + WSL2)で実際に設定を組んでみると、設計の割り切りがよく見える機能でした。この記事では公式ドキュメントで裏取りした仕様をもとに、仕組み・設定方法・macOSでの制約・既存のシークレット管理との使い分けを整理します。
Claude Codeのサンドボックスとは何か
前提から確認しておきましょう。Claude Codeのサンドボックス(sandboxed Bash tool)は、Bashコマンドとその子プロセスをOSレベルの隔離境界の中で実行する機能です。コマンドごとの許可プロンプトを減らしつつ、「どのファイルに触れるか」「どのドメインに通信できるか」をユーザー側が定義し、OSが強制します。
隔離は大きく2層に分かれます。
- ファイルシステム隔離:書き込みは作業ディレクトリとセッション用一時ディレクトリに限定。読み取りはデフォルトで広く許可されるため、
~/.aws/credentialsや~/.ssh/のような認証情報ファイルは初期状態では読めてしまう点に注意が必要です - ネットワーク隔離:サンドボックスの外で動くプロキシサーバーが通信を仲介し、許可ドメイン(
allowedDomains)以外への接続は承認プロンプトで制御します
対応プラットフォームはmacOS・Linux・WSL2で、Windowsネイティブは非対応です。macOSは組み込みのSeatbeltフレームワークを使うため追加インストール不要、LinuxとWSL2では専用パッケージのセットアップが必要になります。セッション内で/sandboxコマンドを実行すると、モード選択や不足依存の確認ができるパネルが開きます。
ポイントは「デフォルトの読み取り許可が広い」ことです。だからこそ、認証情報だけを狙って保護するsandbox.credentials設定(v2.1.187以降)が用意されており、その保護方法としてdenyとmaskの2つのモードがあります。
maskモードで何が変わるのか:denyとの決定的な違い
従来のdenyモードは単純明快です。指定したファイルはサンドボックス内から読めなくなり、指定した環境変数はコマンド実行前に削除されます。設定はこのような形です。
{
"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" }
]
}
}
}
動作環境:Claude Code v2.1.187以降(sandbox.credentialsブロック対応バージョン)
安全ですが、その認証情報で認証するツールも一緒に壊れます。GITHUB_TOKENをdenyすればgh pr listは失敗し、~/.config/gh/hosts.ymlをdenyすればghコマンド全般が認証エラーになります。
maskモードはここを解決します。動きは次の3段階です。
- 1. センチネルの提示:サンドボックス内のコマンドには、本物の値の代わりにセッションごとのプレースホルダー値(センチネル)が見えます。ファイルの場合は、秘密部分だけ置換された「センチネルコピー」を読むことになります
- 2. egress時の置換:コマンドが
injectHostsで許可したホストへ通信するとき、サンドボックスプロキシがリクエスト内のセンチネルを本物の値に差し替えます。置換はヘッダーとリクエストボディの両方が対象です - 3. ログにも残らない:コマンド自身とその出力・ログは一度も本物の値を保持しません。それでもAPIリクエストは正しく認証されます
つまり「認証情報を漏らさない」と「ツールを動かし続ける」を同時に満たす設計です。プロンプトインジェクションでエージェントが騙され、cat ~/.config/gh/hosts.ymlの結果をどこかに送信しようとしても、そこに写っているのはセンチネルであり本物のトークンではありません。しかも本物への置換はinjectHostsに列挙した正規のAPIホスト宛てにしか起きないため、攻撃者のサーバーには偽の値しか届かない、という多層防御になります。
バージョン要件は段階的に追加されてきました。環境変数のmaskはv2.1.199から、ファイルのmaskが今回のv2.1.221から、後述のdecode: "jwt"やAWS向けawsPairsはv2.1.224からです。
設定方法:mask・extract・injectHostsの書き方
実際の設定を見ていきましょう。maskモードには前提が1つあります。プロキシがリクエストの中身を書き換えるには通信内容が見えなければならないため、実験的設定のnetwork.tlsTerminate(プロキシ自身がTLSを終端する。v2.1.199以降)が必須です。これを忘れるとどうなるかは後述の失敗パターンで扱います。
環境変数をマスクする
まずは環境変数の例です。GH_TOKENはapi.github.com宛てのみ置換、NPM_TOKENはinjectHostsを省略しているためallowedDomains内の全ホストで置換されます。なおinjectHostsに書くホストは、必ずnetwork.allowedDomainsでもカバーされている必要があります。
以下は~/.claude/settings.json(ユーザー設定)に書く設定例です。
{
"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" }
]
}
}
}
動作環境:Claude Code v2.1.199以降(Linux / WSL2 / macOS。環境変数maskは全プラットフォーム対応)
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
ファイルをマスクする:extract正規表現で「一部だけ」隠す
次が本題のファイルmask(v2.1.221以降)です。設定ファイルの多くは、秘密の値と無害な設定が同居しています。ghのhosts.ymlならユーザー名やプロトコル設定の中にOAuthトークンが1行だけ埋まっている形です。ファイル全体を1つのセンチネルに置き換えてしまうと、ghがYAMLとしてパースできなくなり結局壊れます。
そこでextractフィールドに正規表現を指定します。Claude Codeはファイル全体にこのパターンを適用し、各マッチのキャプチャグループ1に捕捉されたテキストだけをプレースホルダーに置換します。.netrc・JSON・YAMLのような構造化ファイルでも、秘密の値以外はそのまま読める状態を保てるわけです。パターンには最低1つのキャプチャグループが必要です。
{
"sandbox": {
"enabled": true,
"network": {
"tlsTerminate": {},
"allowedDomains": ["*.github.com"]
},
"credentials": {
"files": [
{
"path": "~/.config/gh/hosts.yml",
"mode": "mask",
"extract": "oauth_token:\s*(\S+)",
"injectHosts": ["api.github.com"]
}
]
}
}
}
動作環境:Claude Code v2.1.221以降、Linux / WSL2のみ(macOSの挙動は次章)
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
逆に、ファイルがトークン1個だけを裸で保持している場合はextractを省略します。その場合はファイル内容全体が1つのセンチネル値に置き換わります。
マスクが効いているかの確認は簡単で、サンドボックス内でcat ~/.config/gh/hosts.ymlを実行させてみることです。LinuxとWSL2ではトークン部分がセンチネル値になった出力が返り、macOSでは読み取り自体が失敗します。
マッチしなかったときの挙動と補助フィールド
extractを使う場合、パターンが何もマッチしなかったときの扱いをonExtractNoMatchで選べます。
| 値 | 挙動 | 向いているケース |
|---|---|---|
warn(デフォルト) |
警告してエントリをスキップ。実ファイルがそのまま読める | その認証情報が正当に存在しないことがある場合 |
deny |
ファイルを読み取り不可にする | 秘密は存在するはずだがパターンが取りこぼす恐れがある場合 |
error |
設定を修正するまでサンドボックス起動を停止 | 設定ミスを確実に検知したい場合 |
デフォルトがwarnである点は要注意です。正規表現の書き間違いで秘密が丸見えのまま「警告だけ出て動いてしまう」可能性があるため、確実に守りたいファイルにはdenyかerrorを明示しておくのが安全側の選択です。
ほかにも補助フィールドがあります(v2.1.224以降)。JWTを保持するファイル・変数にはdecode: "jwt"を指定でき、構造的に有効な偽トークンへ置換されるため、サンドボックス内でトークンをデコードするコードも動き続けます。maskClaimsで特定のペイロードクレームだけ隠すことも可能です。またmaskDuplicatesを有効にすると、マッチ範囲の外に同じ値が逐語的に現れた場合も置換されます(短い値だと誤置換が広がるため、高エントロピーな長い秘密向け)。AWSのようにリクエスト署名(SigV4)で認証する場合は、プロキシが署名を再計算するawsPairsという専用の仕組みが用意されています。
1つのmaskエントリが守れるのは単一ファイルのみで、ディレクトリ・globパターン・8MiB超のファイル・UTF-8テキストでないファイルは安全にマスクできないためdenyにフォールバックします。ディレクトリ(~/.sshなど)は最初から明示的なdenyエントリとして書きましょう。
macOSではファイルマスク非対応:denyフォールバックの実際
ここは公式ドキュメントでも明確に区別されている点で、誤解すると「Macで設定したのにghが動かない」と混乱します。ファイルのmaskエントリはプラットフォームで挙動が分かれます。
- Linux / WSL2:センチネルコピーの読み取り+プロキシによるegress置換。フル機能
- macOS:対象ファイルは一切読めなくなります。センチネルコピーは作られず、egress置換も行われないため、そのファイルで認証するツールはサンドボックス内で動きません。実質
denyと同じ効果です
ただしmacOSのmaskエントリにはdenyと異なる点が1つあります。通常のdenyによるファイル保護はファイルシステム隔離レイヤーの一部なので、filesystem.disabledで隔離を切ると保護も消えます。一方maskエントリ由来の読み取りブロックは、ファイルシステム隔離を無効化しても維持されます。「隔離は緩めたいがこのファイルだけは絶対に見せない」という構成では、macOSでもmask指定に意味があるということです。
なお環境変数のmaskはmacOSでも機能します。非対応なのはあくまで「ファイルの」マスキングです。Macメインで開発しているチームなら、トークンを環境変数経由で渡す運用に寄せておくとmaskモードの恩恵をフルに受けられます。
既存のシークレット管理手法との違い・使い分け
maskモードは万能薬ではなく、既存の手法と守備範囲が異なります。整理してみましょう。
| 手法 | 守れるもの | ツールは動くか | 主な制約 |
|---|---|---|---|
credentialsのdeny |
指定ファイルの読み取り/指定環境変数 | 動かない(認証不可) | ファイル保護はfilesystem隔離が前提 |
credentialsのmask |
ファイル・環境変数の実値 | 動く(許可ホスト宛てのみ認証成立) | tlsTerminate必須。ファイルはLinux/WSL2のみ |
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB |
Anthropicやクラウド系の認証環境変数 | 動かない(変数が消える) | サンドボックス外のサブプロセスにも効くが対象は定義済みの変数群 |
| 外部シークレットマネージャー(Vault等) | 保存時の秘密全般 | 動く | 取得後にメモリ・環境変数へ展開された値は守れない |
使い分けの考え方はこうなります。
- サンドボックス内で使わない秘密(SSH秘密鍵、本番DBの資格情報など)→
denyで読み取り自体を遮断。迷ったらまずこちら - サンドボックス内のツールが必要とする秘密(
gh・npm・クラウドCLIのトークン)→mask+injectHostsで正規のAPIホストにだけ届くようにする - 秘密の保管と配布 → 従来どおりシークレットマネージャーの仕事。maskは「配布後、エージェントの実行環境で値が露出する最後の1マイル」を埋める補完関係にあります
もう1つ重要な制約が設定ソースの制限です。maskはプロキシに「本物の認証情報を指定ホストへ送ってよい」と授権する設定なので、ユーザー設定・管理者のmanaged settings・CLIの--settingsフラグからのみ有効です。リポジトリの.claude/settings.jsonや.claude/settings.local.jsonに書いても無視されます。クローンしてきたリポジトリの設定ファイルが勝手にトークンの送信先を書き換える、というサプライチェーン的な攻撃経路を塞ぐための制限で、同じ変数が別スコープでdeny指定されていればdenyが優先されます。この辺りの脅威モデルはプロンプトインジェクション対策の記事で扱った「ツール結果を信頼しない」原則の設定版と捉えると分かりやすいはずです。
また、gVisorやFirecrackerで実行環境ごと隔離するアプローチとの関係で言えば、maskモードは「隔離境界の内側にそもそも本物の秘密を持ち込まない」という一段深い対策です。強い隔離を敷いても、その中に本物のトークンがあれば許可済み経路から漏れ得ます。両者は排他ではなく重ねて使うものです(サンドボックス設計の全体像はgVisor/Firecrackerによるサンドボックス設計の記事を参照してください)。
【要注意】つまずきやすいポイントと対策
失敗1:tlsTerminateを設定せずに認証エラーだけが起きる
❌ credentialsにmaskエントリだけ書いてnetwork.tlsTerminateを忘れる
⭕ maskを使う設定には必ず"tlsTerminate": {}をセットで書く
なぜ重要か:TLS終端がないとプロキシはリクエストの中身を書き換えられません。このときの失敗モードは「秘密が漏れる」ではなく「センチネルがそのままサーバーに届いて認証が失敗する」であり、安全側に倒れる設計です。Claude Codeは起動時にこの設定不備を報告するので、警告を見落とさないことが対策になります。
失敗2:リポジトリのsettings.jsonに書いて「効かない」と悩む
❌ .claude/settings.jsonにmask設定をコミットしてチーム展開したつもりになる
⭕ 各メンバーのユーザー設定か、組織のmanaged settingsで配布する
なぜ重要か:前述のとおりmask・tlsTerminate・allowPlaintextInjectはリポジトリスコープでは無視されます。エラーにならず静かに無効になるため、「設定したのに動かない」の原因として最も踏みやすいポイントです。
失敗3:AWSのシークレットキーだけをマスクする
❌ AWS_SECRET_ACCESS_KEYのみmaskして、リクエストがAWSで署名エラーになる
⭕ AWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYを必ずペアでmaskする
なぜ重要か:AWSリクエストは認証情報そのものではなくSigV4署名を送るため、プロキシは署名の再計算(re-sign)が必要です。プロキシはアクセスキーのセンチネルを手がかりにSigV4リクエストを検知するので、シークレットだけをマスクすると検知できず、プレースホルダーで署名されたリクエストがそのままAWSに届いて失敗します。このケースはClaude Codeが起動時に警告します。
失敗4:extractの取りこぼしをwarnのまま放置する
❌ デフォルトのonExtractNoMatch: "warn"のまま、正規表現ミスで実値が素通しになる
⭕ 必ず存在するはずの秘密には"onExtractNoMatch": "deny"か"error"を指定し、導入時にサンドボックス内でcatさせてセンチネル化を目視確認する
なぜ重要か:warnは「認証情報が正当に無いこともある」ケース向けのデフォルトです。守りたい秘密が確実に存在するファイルでは、パターン不一致=設定ミスとして止まる方が安全です。
よくある質問
maskモードはどのバージョンから使えますか?
環境変数のmaskと前提となるnetwork.tlsTerminateはv2.1.199以降、ファイルのmaskはv2.1.221(2026年8月4日リリース)以降、decode: "jwt"・maskClaims・awsPairs・環境変数側のextractはv2.1.224以降です。sandbox.credentialsブロック自体はv2.1.187から存在します。
デフォルトで保護される認証情報はありますか?
ありません。組み込みの認証情報denyリストは存在せず、credentialsに列挙したファイル・変数だけが保護対象です。サンドボックスのデフォルト読み取り許可は広いため、守りたいものは明示的に書く必要があります。
Windowsでは使えますか?
Windowsネイティブはサンドボックス自体が非対応です。WSL2内でClaude Codeを動かせばLinux扱いとなり、ファイルmaskを含むフル機能が使えます。
センチネル値が外部に送信されたらどうなりますか?
injectHosts(未指定時はallowedDomains内の全ホスト)以外の宛先では置換が起きないため、届くのはプレースホルダーのみです。セッションごとに生成される値であり、本物の認証情報の復元には使えません。
要点の整理
Claude Code v2.1.221のサンドボックスmaskモードについて、押さえるべき点は次のとおりです。
- maskモードは、サンドボックス内のコマンドにセンチネル(プレースホルダー)を見せ、許可ホストへのegress時だけプロキシが実値に置換する仕組み。「秘密を見せない」と「ツールを動かす」を両立する
- ファイルのmaskはLinux/WSL2のみ。macOSはdeny相当(読み取り不可)にフォールバックするが、環境変数のmaskはmacOSでも使える
extract正規表現でキャプチャグループ1だけを置換でき、.netrc・JSON・YAMLなど構造化ファイルの「秘密の行だけ」をマスクできる- maskには
network.tlsTerminateが必須。設定はユーザー設定・managed settings・--settingsフラグからのみ有効で、リポジトリのsettings.jsonでは無視される - 使わない秘密はdeny、使う秘密はmask、保管・配布はシークレットマネージャー、という役割分担で重ねて使う
まずはユーザー設定にtlsTerminateとmaskエントリを1つ書き、サンドボックス内でcatさせてセンチネル化を確認するところから試してみましょう。設定フィールドの網羅的な仕様は公式のサンドボックスドキュメントで必ず最新版を確認してください。
あわせて読みたい:
- Claude Code v2.1.221のFocus view解説 — maskモードと同じリリースに収録されたUI刷新の詳細
- AIエージェントのシークレット管理・運用ガイド — シークレットマネージャー側の設計とmaskモードの補完関係を考える土台に
この記事を読んで導入イメージが固まってきた方へ
UravationではAIエージェント導入の研修・コンサルを行っています。
参考・出典
- Configure the sandboxed Bash tool — Claude Code Docs — Anthropic(参照日: 2026-08-10)
- Claude Code changelog — Anthropic(参照日: 2026-08-10)
- Release v2.1.221 — anthropics/claude-code — GitHub(参照日: 2026-08-10)
関連の続報(2026年8月11日追記):この記事で扱った権限プロンプトの厳格化に続き、Claude Codeは2026年8月14日から権限モデル自体を大きく変える。Claude Code Auto Modeが標準化|8月14日から何が変わるもあわせて確認してほしい。
