Agent SDK安全なデプロイ — サンドボックスと資格情報管理
Agent SDKとClaude Codeは動的に行動を生成するため、プロンプトインジェクションが前提になります。分離技術と資格情報のプロキシパターンをまとめます。
プロンプトインジェクションを前提にした脅威モデル
Claude CodeとAgent SDKは、コードを実行しファイルにアクセスし外部サービスとやり取りします。あらかじめ決まったコードパスを辿る従来のソフトウェアと違い、これらのツールは文脈と目標をもとに行動をその場で生成します。この柔軟さが実用性の源ですが、処理する内容(ファイル・Webページ・ユーザー入力)に行動が引きずられる余地も生みます。
リポジトリのREADMEに不審な指示が紛れていれば、Claude Codeがそれを自分の行動に取り込むことがあります。これがプロンプトインジェクションです。モデル側もこうした誘導に耐えるよう設計されていますが、多層防御(Defense in depth)は依然として有効です。悪意あるファイルが「顧客データを外部サーバーへ送れ」と指示しても、ネットワーク制御があればそのリクエスト自体を遮断できます。
どこまで固めるかは、デプロイの性質で変わります。ノートPCでClaude Codeを動かす個人開発者と、マルチテナント環境で顧客データを処理する企業では、要求水準がまったく違います。
組み込みの防御で足りるのはどこまでか
Claude Codeには、一般的な懸念に対応する機能が標準で入っています。
- 権限システム: すべてのツールとBashコマンドについて、許可・拒否・ユーザー確認のいずれかを設定できます。「npmコマンドはすべて許可」「sudoを含むコマンドはすべて拒否」のようなglobパターンのルールを組め、組織はユーザー横断のポリシーも設定できます。
- コマンド解析: Bashコマンドの実行前に、ASTに解析して権限ルールと突き合わせます。きれいに解析できないコマンドや許可ルールに一致しないコマンドは、明示的な承認が必要です。
evalのような一部の構文は、許可ルールにかかわらず常に承認が必要になります。 - Web検索の要約: 生のコンテンツを文脈に渡さず要約を渡すので、悪意あるWebコンテンツ由来のインジェクションのリスクが下がります。
コマンド解析は権限のゲートであってサンドボックスではありません。rm / rmdir の重要パス確認や保護パスの一覧といった組み込みの安全チェックを除けば、コマンドの対象パスや効果から危険性を推測する仕組みではないからです。
サンドボックスは既定でオフで、囲うのはシェルコマンドだけ
ファイルシステムとネットワークを制限するサンドボックスモードは、既定ではオフです。/sandbox を実行するか、設定ファイルで sandbox.enabled を true にすると有効になります。有効にした場合の既定の挙動は次のとおりです。
| アクセス | 既定の挙動 |
|---|---|
| 書き込み | 既定の挙動作業ディレクトリ・ユーザー別の一時ディレクトリ・追加したディレクトリ。保護パスは書き込み不可 |
| 読み取り | 既定の挙動マシンのほぼ全体。~/.ssh や ~/.aws/credentials のような認証情報ファイルも読める |
| ネットワーク | 既定の挙動直接の経路なし。自分のマシン上のプロキシが許可ドメインと照合する。許可ドメインは空から始まる |
| 環境変数 | 既定の挙動Claude Codeから継承する。環境にあるシークレットもそのまま入る |
読み取りが既定でマシン全体に及ぶ点は見落としやすく、sandbox.credentials の deny や mask で個別に塞ぎます。もう1つの見落としが、サンドボックスの囲う範囲です。
サンドボックスの内側と外側
囲われる
シェルコマンドと、そこから起動されるプロセスです。
囲われない
Read・Edit・Write・WebFetch・WebSearchは権限ルールに従います。denyRead はReadツールを止めず、allowedDomains はWebFetchを制限しません。フック・ローカルのMCPサーバー・LSPサーバー・ステータスラインのコマンド・apiKeyHelper は、フルアクセスで動きます。
もう1点、依存パッケージの欠落や未対応プラットフォームでサンドボックスを起動できない場合、既定ではサンドボックスなしでコマンドを実行します。起動時に終了させたいなら sandbox.failIfUnavailable を true にします。サンドボックスをセキュリティ上の関門にするデプロイでは、この設定の有無が分かれ目になります。
動作確認は、Claudeに次のコマンドを実行させます。! プロンプトに自分で打ち込んだコマンドは、通常サンドボックスの外で動くので確認になりません。
| コマンド | サンドボックス内での結果 |
|---|---|
touch ~/sandbox-probe | サンドボックス内での結果macOSでは Operation not permitted、LinuxとWSL2では Read-only file system で失敗 |
curl --noproxy '*' https://example.com | サンドボックス内での結果サンドボックスのプロキシを迂回する経路がなく、Could not resolve host で失敗 |
仕組みの掘り下げはClaude Codeのサンドボックス設計にあります。Claude Codeのプロセス全体を1つの境界に入れたい場合は、コンテナ・VM・sandbox runtimeのいずれかで動かします。以降の節が、その選び方です。
境界・最小権限・多層防御の3原則
既定の機能を超える堅牢化が必要なとき、選択肢を導くのは次の3原則です。
堅牢化の3原則
セキュリティ境界
信頼レベルの異なるコンポーネントを分け、資格情報のような機微なリソースをエージェントの環境の外に置きます。エージェントにAPIキーを渡す代わりに、環境の外でプロキシを動かしてリクエストへ鍵を注入します。エージェントはAPI呼び出しはできても、資格情報そのものを一度も目にしません。マルチテナントや、信頼できないコンテンツを処理する場面で有効です。
最小権限
タスクに必要な能力だけに絞ります。ファイルシステムは必要なディレクトリだけをマウントして読み取り専用を優先し、ネットワークはプロキシ経由で特定エンドポイントだけにします。コンテナでは不要なLinux capabilityを落とします。
多層防御
コンテナ分離・ネットワーク制限・ファイルシステム制御・プロキシでのリクエスト検証を重ねます。どの組み合わせが適切かは、脅威モデルと運用要件で決まります。
Managed Agentsをセルフホストに切り替える場合も、Anthropicと自社のどちらがどこを守るかという同種の境界設計が必要になります。
個人開発やCI/CDならsandbox-runtimeで足りるか
分離技術は、強度・性能オーバーヘッド・運用の複雑さのトレードオフが違います。いずれの構成でも、Claude Code(またはAgent SDKアプリケーション)自体は分離境界の内側で動き、以下の制御はその内側からアクセスできる範囲を絞ります。
| 技術 | 分離強度 | 性能オーバーヘッド | 複雑さ |
|---|---|---|---|
| サンドボックスランタイム | 分離強度良好(安全な既定設定) | 性能オーバーヘッド非常に低い | 複雑さ低い |
| コンテナ(Docker) | 分離強度設定次第 | 性能オーバーヘッド低い | 複雑さ中程度 |
| gVisor | 分離強度優秀(正しく設定すれば) | 性能オーバーヘッド中〜高 | 複雑さ中程度 |
| VM(Firecracker・QEMU) | 分離強度優秀(正しく設定すれば) | 性能オーバーヘッド高い | 複雑さ中〜高 |
コンテナなしの軽量な分離なら、sandbox-runtime がOSレベルでファイルシステムとネットワークを制限します。Claude Code自身のサンドボックスも、この同じパッケージの上に作られています。Dockerの設定やコンテナイメージ、ネットワーク構築が要らない手軽さが利点です。Linuxでは bubblewrap、macOSでは sandbox-exec で、読み書きできるパスを制限します。ネットワークはLinuxではネットワーク名前空間を外し、macOSではSeatbeltプロファイルで組み込みのプロキシへ経路を寄せます。設定は、ドメインとファイルシステムパスのJSON形式の許可リストです。
npm install @anthropic-ai/sandbox-runtime注意点は2つあります。
- サンドボックス化したプロセスはホストと同じカーネルを共有するため、カーネルの脆弱性が理論上は脱出経路になりえます。カーネルレベルの分離が必要ならgVisorか別立てのVMを使います。
- プロキシは、クライアントの申告したホスト名だけでドメインを許可リスト判定し、暗号化トラフィックの終端や検査をしません。ドメインフロンティングのような手法で許可リスト外のホストへ抜けられる可能性があるため、強い保証が必要ならTLS終端プロキシを別途構成します。
github.comのような広いドメインを許可すると、データ持ち出しの経路にもなりえます。
許可リストやコマンド制限をTypeScript/Pythonのコードから設定する方法は、Agent SDK sandbox設定をコードから制御するにあります。単一開発者やCI/CDの用途では、この手軽さで基準を大きく引き上げられます。
信頼できない入力を扱うならコンテナを堅牢化する
コンテナはLinux名前空間で分離し、ファイルシステム・プロセスツリー・ネットワークスタックの見え方をそれぞれ独立させます。ホストカーネルは共有します。堅牢化したDocker設定の例です。
docker run \
--cap-drop ALL \
--security-opt no-new-privileges \
--security-opt seccomp=/path/to/seccomp-profile.json \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=100m \
--tmpfs /home/agent:rw,noexec,nosuid,size=500m \
--network none \
--memory 2g \
--cpus 2 \
--pids-limit 100 \
--user 1000:1000 \
-v /path/to/code:/workspace:ro \
-v /var/run/proxy.sock:/var/run/proxy.sock:ro \
agent-image--cap-drop ALL は NET_ADMIN や SYS_ADMIN のような、権限昇格につながるLinux capabilityを外します。--security-opt no-new-privileges はsetuidバイナリ経由の権限取得を防ぎます。--read-only はルートファイルシステムを不変にし、エージェントが変更を永続化できないようにします。--pids-limit 100 はフォーク爆弾を防ぎ、--user 1000:1000 は非rootで実行します。
seccompは、使えるシステムコールを絞ります。Dockerの既定プロファイルは約44個を塞ぎ、独自プロファイルならさらに増やせます。追加の堅牢化には、コンテナのrootを権限のないホストユーザーに対応づける --userns-remap(デーモン側の設定が必要)と、コンテナ間攻撃を防ぐ --ipc private があります。
claude --help(v2.1.287)は、--dangerously-skip-permissions を「Recommended only for sandboxes with no internet access」と案内しています。権限確認を丸ごと外すフラグの前提が、インターネットに出られない箱だということです。--network none のコンテナは、この前提を満たす形です。
外へ出る道をUnixソケット1本にする
--network none を指定したコンテナには、ネットワークインターフェースが一切ありません。
コンテナの通信経路
- 1
エージェント(コンテナ内)
ネットワークインターフェースを持たず、直接は外へ出られません。
- 2
マウント済みのUnixソケット
コンテナの外へ出る唯一の経路です。
- 3
ホスト上のプロキシ
ドメインの許可リストを適用し、資格情報を注入し、すべての通信を記録します。
- 4
外部のAPI・サービス
プロキシが許可したドメインにだけ届きます。
この構造はsandbox-runtimeと同じです。プロンプトインジェクションでエージェントが侵害されても、任意のサーバーへデータを持ち出すことはできません。
この構成で見落としやすいのが、データの置き場所です。セッションのトランスクリプトは既定で ~/.claude/projects/ に書かれ、CLAUDE_CONFIG_DIR を設定していればその下の projects/ に書かれます。ホームを tmpfs にしたこの例では、コンテナの停止でそれも消えます。コンテナの再起動やスケールダウンをまたいで残したいセッションは、SessionStore へ書き出すか、永続ボリュームを別に用意します。
コンテナの中でClaude Codeのサンドボックスを動かすとき
権限のないコンテナでは、bubblewrapが新しい /proc をマウントできません。サンドボックス化したコマンドは、Can't mount proc on /newroot/proc: Operation not permitted のような bwrap エラーで失敗します。enableWeakerNestedSandbox を true にすると、コンテナ既存の /proc をバインドマウントして動きますが、サンドボックスにプロセス情報が見えるようになり、セキュリティは大きく弱まります。外側のコンテナが必要な分離をすでに提供しているときだけ使う設定です。
サンドボックス側の allowUnixSockets にも注意が要ります。/var/run/docker.sock のようなソケットを許可すると、Dockerソケット経由でホストへ到達できます。プロキシ用に開けるソケットは、そのプロキシだけに絞ります。
カーネルを共有したくないならgVisorかVM
標準的なコンテナはホストカーネルを共有します。コンテナ内のコードがシステムコールを発行すると、ホストを動かす同じカーネルに直接届きます。gVisorは、システムコールをホストカーネルに届く前にユーザー空間で横取りし、独自の互換レイヤーで処理します。プロンプトインジェクション由来の悪意あるコードがカーネルエクスプロイトを試みても、まずgVisorのユーザー空間実装を突破する必要があり、実カーネルへのアクセスは限られます。
DockerでgVisorを使うには、runsc ランタイムを導入してデーモンを設定します。
{
"runtimes": {
"runsc": {
"path": "/usr/local/bin/runsc"
}
}
}docker run --runtime=runsc agent-image代償は性能です。システムコールを横取りするので、ワークロードの性質で影響が大きく分かれます。
gVisorのオーバーヘッド
CPU中心の計算
約0%
システムコールの横取りが発生しない
単純なシステムコール
約2倍
遅くなる
ファイルI/O集中
最大10〜200倍
開閉が多い処理で遅くなる
VMはCPUの仮想化拡張によるハードウェアレベルの分離を提供し、各VMが自分のカーネルを持ちます。ゲストカーネルの脆弱性が、直接ホストを侵害することはありません。ただしVMがgVisorのような代替より自動的に安全なわけではなく、実際の安全性はハイパーバイザーとデバイスエミュレーションの実装に大きく依存します。
Firecrackerは軽量なmicroVM向けに設計され、不要なデバイスエミュレーションを削って攻撃対象領域を減らしています。125ミリ秒未満・メモリーオーバーヘッド5MiB未満でVMを起動できます。この方式ではエージェントVMが外部ネットワークインターフェースを持たず、vsock(仮想ソケット)経由の通信だけがホスト上のプロキシへ届きます。許可リストの適用と資格情報の注入は、そのプロキシで行います。
クラウド上では、これらの分離技術をクラウドネイティブなネットワーク制御と組み合わせます。
- プライベートサブネットに、インターネットゲートウェイなしでコンテナを置く
- クラウドのファイアウォール規則(AWSのセキュリティグループ、GCPのVPCファイアウォール)で、プロキシ以外への外向き通信をすべて遮断する
credential_injectorフィルターを持つEnvoyのようなプロキシが、リクエストを検証・許可リスト適用・資格情報注入したうえで転送する- エージェントのサービスアカウントには最小限のIAM権限だけを割り当て、機微なアクセスは可能な限りプロキシ経由にする
- すべての通信をプロキシで監査ログに残す
資格情報はエージェントに渡さずプロキシで注入する
エージェントはAPI呼び出し・リポジトリアクセス・クラウドサービス連携のために資格情報を必要とします。課題は、資格情報そのものを露出させずにそのアクセスを提供することです。
推奨は、エージェントのセキュリティ境界の外でプロキシを動かし、送信リクエストへ資格情報を注入する構成です。エージェントは資格情報なしでリクエストを送り、プロキシがそれを付与して転送先へ届けます。プロキシは許可エンドポイントの一覧を強制でき、すべてのリクエストを監査用に記録でき、資格情報を各エージェントに分散させず1か所で管理できます。
プロキシは自作しても既存のものを使ってもかまいません。候補は、credential_injector フィルターで認証ヘッダーを足せるEnvoy、HTTPSを終端して中身を検査・変更できるmitmproxy、アクセス制御リストを持つキャッシュプロキシのSquid、資格情報の注入とレート制限を備えたLLMゲートウェイのLiteLLMです。
Claude Codeがサンプリングリクエストをプロキシ経由にする方法は2通りあります。
プロキシへの向け方
ANTHROPIC_BASE_URL
Claude CodeとAgent SDKのサンプリングリクエストだけをプロキシへ向けます。プロキシは平文のHTTPリクエストを受け取り、内容を検査・変更(資格情報の注入を含む)して実際のAPIへ転送します。
HTTP_PROXY / HTTPS_PROXY
標準の環境変数に従い、すべてのHTTPトラフィックをプロキシ経由にします。HTTPSでは暗号化されたCONNECTトンネルが張られるため、TLSインターセプトなしではプロキシがリクエスト内容を見ることも書き換えることもできません。
export ANTHROPIC_BASE_URL="http://localhost:8080"
export HTTP_PROXY="http://localhost:8080"
export HTTPS_PROXY="http://localhost:8080"上の3行は別々の方式の設定例で、併用の例ではありません。サンプリングだけで足りるなら ANTHROPIC_BASE_URL で済みます。他のHTTPSサービスも扱うなら、HTTP_PROXY / HTTPS_PROXY に加えてTLS終端プロキシと証明書のインストールが要ります。
サンプリングAPI以外の資格情報
gitリポジトリ・データベース・社内APIの認証には、2つのアプローチがあります。
1つはカスタムツールです。MCPサーバーやカスタムツールが、エージェントの境界の外にあるサービスへリクエストを転送し、実際の認証済みリクエストは外側で行われます。たとえばgitのMCPサーバーがエージェントからのコマンドを受け取ってホスト上のgitプロキシへ転送し、そこで認証を付与してリモートリポジトリへ届けます。TLSの傍受が要らず、資格情報は外側に残ります。ただしローカルのMCPサーバーはサンドボックスの外でフルアクセスで動くため、資格情報を持たせる先は、エージェントの境界の外にあるサービスにします。
もう1つはトラフィックフォワーディングです。GitHubやnpmレジストリのようにエンドツーエンドで暗号化されたHTTPSサービスには、TLS終端プロキシがトラフィックを復号・検査・変更してから再暗号化して転送します。プロキシをエージェントのコンテナの外で動かし、プロキシの認証局証明書をエージェントの信頼ストアに入れ、HTTP_PROXY / HTTPS_PROXY で経路を向けます。証明書管理の複雑さが増しますが、ツールを書かずに任意のHTTP系サービスを扱えます。
すべてのプログラムが環境変数に従うわけではありません。curl・pip・npm・gitはほとんど従いますが、迂回するツールもあります。Node.jsの fetch() は既定でこれらの変数を無視し、Node 24以降では NODE_USE_ENV_PROXY=1 で対応を有効にできます。網羅的に扱うなら、proxychains でネットワーク呼び出しを横取りするか、iptablesでアウトバウンド通信を透過プロキシへリダイレクトします。透過プロキシはネットワーク層で通信を横取りするので、クライアント側にプロキシの設定が要りません。
Claude Code自身のサンドボックスにある注入機能
Claude Codeのサンドボックスは、この注入をクライアント側の設定として持っています。sandbox.credentials で mask を指定すると、サンドボックス内のコマンドには実際の値でなくセッションごとのプレースホルダーが見え、サンドボックスのプロキシが許可ホストへの送信時に本物へ差し替えます。
{
"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" }
]
}
}
}差し替えはリクエストの中身を見る必要があるので、network.tlsTerminate でプロキシにTLSを終端させます。設定しないと、プレースホルダーがそのままサーバーに届いて認証が通りません。injectHosts を省いた NPM_TOKEN は、allowedDomains の全ホストへの送信で差し替えが働きます。
境界の考え方が表れているのが、設定ファイルの信頼範囲です。mask の項目と tlsTerminate は、ユーザー設定・管理設定・--settings フラグからだけ有効になり、リポジトリの .claude/settings.json に書いても無視されます。リポジトリ側からは、実際の資格情報の送り先を指定できない形です。sandbox.credentials が効くのは、サンドボックス化したBashコマンドだけです。ファイルの mask にはv2.1.221以降が要ります。ファイルの mask の具体例はmask設定の解説にあります。
自前のプロキシでサンドボックスの通信を見たいときは、sandbox.network.httpProxyPort と socksProxyPort を設定します。ポートを設定すると、その通信には allowedDomains や strictAllowlist などClaude Code側のネットワーク制御が働かなくなり、フィルタリングはすべて自分のプロキシの責任になります。
読み取り専用マウントでも漏れるもの
エージェントがコードを解析するだけで変更しないなら、ディレクトリは読み取り専用でマウントします。
docker run -v /path/to/code:/workspace:ro agent-imageただし、読み取り専用でも資格情報は漏れます。マウント前に除外するか、サニタイズしておくファイルは次のとおりです。
| ファイル | 含まれうるもの |
|---|---|
.env / .env.local | 含まれうるものAPIキー・データベースパスワード |
~/.git-credentials | 含まれうるもの平文のgitトークン |
~/.aws/credentials | 含まれうるものAWSアクセスキー |
~/.config/gcloud/application_default_credentials.json | 含まれうるものGoogle CloudのADCトークン |
~/.azure/ | 含まれうるものAzure CLIの資格情報 |
~/.docker/config.json | 含まれうるものDockerレジストリの認証トークン |
~/.kube/config | 含まれうるものKubernetesクラスターの資格情報 |
.npmrc / .pypirc | 含まれうるものパッケージレジストリのトークン |
*-service-account.json | 含まれうるものGCPのサービスアカウントキー |
*.pem / *.key | 含まれうるもの秘密鍵 |
エージェントが書き込みを必要とするなら、永続化するかどうかで選択肢が変わります。エフェメラルな作業領域には、メモリー上にしか存在せずコンテナ停止で消える tmpfs マウントを使います。
docker run \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=100m \
--tmpfs /workspace:rw,noexec,size=500m \
agent-image変更を永続化する前にレビューしたいなら、オーバーレイファイルシステムを使います。エージェントは元のファイルを変更せずに書き込め、変更は別レイヤーに閉じ込められるので、検査・適用・破棄を選べます。完全に永続化したい出力は専用ボリュームにマウントし、機微なディレクトリとは分離しておきます。
自分のワークロードにはどの組み合わせが要るか
選択肢を全部使う必要はありません。脅威モデルの重さと、性能オーバーヘッドの許容度から段階を選びます。
ワークロード別の出発点
- 1
ノートPCの個人開発・単発のCI/CD
sandbox-runtimeなら、設定はドメインとパスのJSON許可リストだけで済み、Dockerやコンテナイメージが要りません。 - 2
信頼できない入力を処理する
外部から受け取ったリポジトリ・ファイル・Webページを扱うなら、堅牢化したコンテナに、プロキシ経由の資格情報注入を組み合わせます。
- 3
複数テナントのデータが同じインフラに乗る
gVisorかVMまで進みます。マルチテナントや信頼できない入力を扱う環境では、追加の分離が性能の代償に見合うことが多くなります。
まとめ
Agent SDKとClaude Codeのセキュリティは、プロンプトインジェクションを前提に設計します。デプロイごとに要る分離は違うので、守りたいものを先に決めてから分離技術を選びます。自社ホスティングのインフラ構成はAgent SDK本番ホスティングで扱っています。