動的で、アイデンティティ認識型の、セキュアなSandbox認証
2026-04-13 Mike Nomitch Gabi Villalonga Simón 7分で読める
AI大規模言語モデルとOpenCodeやClaude Codeなどのハーネスがますます高性能になるにつれ、チャットメッセージ、Kanbanの更新、vibe coding UI、ターミナルセッション、GitHubコメントなどに応答してサンドボックス化されたエージェントを起動するユーザーが増えています。
サンドボックスは単純なコンテナを超えた重要なステップです。なぜなら、以下のような機能を提供するからです:
- セキュリティ:信頼できないエンドユーザー(または悪意のあるLLM)がサンドボックス内で実行されても、ホストマシンや並行して実行されている他のサンドボックスを危険にさらすことがありません。これは従来(ただし常にではありませんが)microVMで実現されています。
- 速度:エンドユーザーは新しいサンドボックスを素早く取得し、以前使用したものから状態を素早く復元できる必要があります。
- 制御:信頼できるプラットフォームは、サンドボックスの信頼できないドメイン内でアクションを実行できる必要があります。これには、サンドボックス内でのファイルのマウント、アクセス要求の制御、特定のコマンドの実行などが含まれます。
本日、私たちはSandboxesとすべてのContainersに制御の重要なコンポーネントを追加することを発表します:outbound Workersです。これらは、サンドボックスを実行するユーザーが異なるサービスに簡単に接続し、観測可能性を追加し、エージェントにとって重要な柔軟で安全な認証を追加できるプログラマティックなegress proxyです。
仕組み
outbound Workerを使用してヘッダーにシークレットキーを追加する簡単な例を見てみましょう:
class OpenCodeInABox extends Sandbox {
static outboundByHost = {
"github.com": (request, env, ctx) => {
const headersWithAuth = new Headers(request.headers);
headersWithAuth.set("x-auth-token", env.SECRET);
return fetch(request, { headers: headersWithAuth });
}
}
}
サンドボックス内で実行されるコードが"github.com"にリクエストを行うたびに、そのリクエストはハンドラーを通じてプロキシされます。これにより、ログ記録、変更、キャンセルなど、各リクエストに対して任意の処理を実行できます。この場合、シークレットを安全に注入しています(詳細は後述)。
プロキシはサンドボックスと同じマシン上で実行され、分散状態にアクセスでき、シンプルなJavaScriptで簡単に変更できます。特にエージェントの認証に関して、これがSandboxesに追加するすべての可能性について私たちは興奮しています。
詳細に入る前に、従来の認証形式を簡単に見て、なぜより良いものがあると考えるのかを説明しましょう。
エージェントワークロードの一般的な認証
エージェント認証の核となる問題は、ワークロードを完全に信頼できないことです。私たちのLLMは悪意があるわけではありませんが(少なくともまだ)、データを不適切に使用したり、すべきでないアクションを実行したりしないよう保護を適用できる必要があります。
エージェントに認証を提供するいくつかの一般的な方法が存在し、それぞれに欠点があります:
標準APIトークン
最も基本的な認証方法で、通常は環境変数やマウントされたシークレットファイルを介してアプリケーションに注入されます。これは最もシンプルな方法ですが、最も安全性が低いです。サンドボックスが何らかの形で侵害されたり、リクエスト中に誤ってトークンを流出させたりしないことを信頼する必要があります。エージェントを完全に信頼できないため、トークンの有効期限とローテーションを設定する必要があり、これは面倒です。
ワークロードアイデンティティトークン
OIDCトークンなどのワークロードアイデンティティトークンは、この問題の一部を解決できます。エージェントに一般的な権限を持つトークンを付与する代わりに、そのアイデンティティを証明するトークンを付与できます。エージェントがトークンでサービスに直接アクセスする代わりに、アイデンティティトークンを非常に短期間のアクセストークンと交換できます。OIDCトークンは特定のエージェントのワークフローが完了した後に無効化でき、有効期限の管理が容易になります。
ワークロードアイデンティティトークンの最大の欠点の1つは、統合の潜在的な柔軟性の欠如です。多くのサービスはOIDCの第一級サポートを持たないため、上流サービスとの動作する統合を得るために、プラットフォームは独自のトークン交換サービスを構築する必要があります。これにより採用が困難になります。
カスタムプロキシ
最大の柔軟性を提供し、ワークロードアイデンティティトークンと組み合わせることができます。サンドボックスのegressの一部またはすべてを信頼できるコードに通すことができれば、必要なルールを何でも挿入できます。エージェントが通信している上流サービスのRBACが不十分で、細かい権限を提供できない場合でも、問題ありません。制御と権限を自分で書けばよいのです!
これは、細かい制御でロックダウンする必要があるエージェントにとって優れた選択肢です。しかし、サンドボックスのすべてのトラフィックをどのように傍受するのでしょうか?動的で簡単にプログラム可能なプロキシをどのように設定するのでしょうか?トラフィックを効率的にプロキシするにはどうすればよいでしょうか?これらは解決が容易ではない問題です。
これらの不完全な方法を念頭に置いて、理想的な認証メカニズムはどのようなものでしょうか?
理想的には、以下のような特徴があります:
- ゼロトラスト:信頼できないユーザーにトークンが付与されることは一切ありません。
- シンプル:作成が容易。トークンの発行、ローテーション、復号化の複雑なシステムを含みません。
- 柔軟:必要な細かいアクセスを提供するために上流システムに依存しません。任意のルールを適用できます。
- アイデンティティ認識:呼び出しを行うサンドボックスを識別し、それに特定のルールを適用できます。
- 観測可能:どのような呼び出しが行われているかについて簡単に情報を収集できます。
- 高性能:集中化された、または遅い信頼できる情報源への往復を行いません。
- 透明:サンドボックス化されたワークロードがそれについて知る必要がありません。物事は単に動作します。
- 動的:ルールをその場で変更できます。
私たちは、SandboxesのoutboundWorkersがこれらすべてに適合すると信じています。どのようにかを見てみましょう。
実践でのoutbound Workers
基本:制限と観測可能性
まず、非常に基本的な例を見てみましょう:リクエストのログ記録と特定のアクションの拒否です。この場合、サンドボックスからのすべての発信HTTPリクエストを傍受するoutbound関数を使用します。数行のJavaScriptで、GETのみが行われることを簡単に保証し、許可されていないメソッドをログに記録してから拒否できます。
class MySandboxedApp extends Sandbox {
static outbound = (req, env, ctx) => {
if (req.method !== 'GET') {
console.log(`Container making ${req.method} request to: ${req.url}`);
return new Response('Not Allowed', { status: 405, statusText: 'Method Not Allowed'});
}
return fetch(req);
};
}
このプロキシはWorkers上で実行され、サンドボックス化されたVMと同じマシン上で実行されます。Workersは高速な応答時間のために構築されており、多くの場合キャッシュされたCDNトラフィックの前に配置されているため、追加のレイテンシは極めて最小限です。
これはWorkers上で実行されているため、観測可能性をすぐに利用できます。Workersダッシュボードでログとoutboundリクエストを表示したり、選択したアプリケーションパフォーマンス監視ツールにエクスポートしたりできます。
ゼロトラスト認証情報注入
これを使用してエージェントのゼロトラスト環境を強制するにはどうすればよいでしょうか?プライベートなGitHubインスタンスにリクエストを行いたいが、LLMにプライベートトークンにアクセスさせたくない場合を想像してみましょう。
outboundByHostを使用して、特定のドメインやIPの関数を定義できます。この場合、ドメインが"my-internal-vcs.dev"の場合に保護された認証情報を注入します。サンドボックス化されたエージェントはこれらの認証情報にアクセスすることはありません。
class OpenCodeInABox extends Sandbox {
static outboundByHost = {
"my-internal-vcs.dev": (request, env, ctx) => {
const headersWithAuth = new Headers(request.headers);
headersWithAuth.set("x-auth-token", env.SECRET);
return fetch(request, { headers: headersWithAuth });
}
}
}
コンテナのアイデンティティに基づいて応答を条件付けることも簡単です。すべてのサンドボックスインスタンスに同じトークンを注入する必要はありません。
static outboundByHost = {
"my-internal-vcs.dev": (request, env, ctx) => {
const authKey = await env.KEYS.get(ctx.containerId);
const requestWithAuth = new Request(request);
requestWithAuth.headers.set("x-auth-token", authKey);
return fetch(requestWithAuth);
}
}
Cloudflare Developer Platformの使用
最後の例でお気づきかもしれませんが、outbound Workersを使用するもう1つの大きな利点は、Workersエコシステムへの統合が容易になることです。以前は、ユーザーがR2にアクセスしたい場合、R2認証情報を注入してから、コンテナからパブリックR2 APIに呼び出しを行う必要がありました。KV、Agents、他のContainers、他のWorkerサービスなども同様でした。
今では、outbound Workersから任意のバインディングを呼び出すだけです。
class MySandboxedApp extends Sandbox {
static outboundByHost = {
"my.kv": async (req, env, ctx) => {
const key = keyFromReq(req);
const myResult = await env.KV.get(key);
return new Response(myResult);
},
"objects.cf": async (req, env, ctx) => {
const prefix = ctx.containerId
const path = pathFromRequest(req);
const object = await env.R2.get(`${prefix}/${path}`);
const myResult = await env.KV.get(key);
return new Response(myResult);
},
};
}
トークンの解析とポリシーの設定の代わりに、コードと任意のロジックでアクセスを簡単に条件付けできます。R2の例では、サンドボックスのIDを使用してアクセスを簡単にさらにスコープできました。
制御を動的にする
ネットワーク制御も動的であるべきです。多くのプラットフォームでは、ContainerとVMネットワーキングの設定は静的で、次のようになっています:
{
"defaultEgress": "block",
"allowedDomains": ["github.com", "npmjs.org"]
}
これは何もないよりは良いですが、もっと良くできます。多くのサンドボックスでは、開始時にポリシーを適用し、特定の操作が実行された後に別のポリシーでオーバーライドしたい場合があります。
例えば、サンドボックスを起動し、NPMとGithub経由で依存関係を取得してから、その後egressをロックダウンできます。これにより、ネットワークを可能な限り短時間だけ開放することが保証されます。
これを実現するために、outboundHandlersを使用できます。これにより、setOutboundHandlerメソッドを使用してプログラマティックに適用できる任意のoutboundハンドラーを定義できます。これらはそれぞれparamsも受け取り、コードから動作をカスタマイズできます。
この場合、カスタムの"allowHosts"ポリシーでいくつかのホスト名を許可してから、HTTPをオフにします。
class MySandboxedApp extends Sandbox {
static outboundHandlers = {
async allowHosts(req, env, { params }) {
const url = new URL(request.url);
const allowedHostname = params.allowedHostnames.includes(url.hostname);
if (allowedHostname) {
return await fetch(newRequest);
} else {
return new Response(null, { status: 403, statusText: "Forbidden" });
}
}
async noHttp(req) {
return new Response(null, { status: 403, statusText: "Forbidden" });
}
}
}
async setUpSandboxes(req, env) {
const sandbox = await env.SANDBOX.getByName(userId);
await sandbox.setOutboundHandler("allowHosts", {
allowedHostnames: ["github.com", "npmjs.org"]
});
await sandbox.gitClone(userRepoURL)
await sandbox.exec("npm install")
await sandbox.setOutboundHandler("noHttp");
}
これはさらに拡張できます。エージェントは、その時点で必要なツールに基づいて、"cloudflare.comへのPOSTリクエストを許可しますか?"のような質問をエンドユーザーに尋ねるかもしれません。動的なoutbound Workersを使用すると、このレベルの制御を提供するためにサンドボックスルールをその場で簡単に変更できます。
MITM ProxyingによるTLSサポート
リクエストを許可または拒否する以外に有用なことを行うには、コンテンツにアクセスする必要があります。これは、HTTPSリクエストを行う場合、Workersプロキシによって復号化される必要があることを意味します。
これを実現するために、各Sandboxインスタンスに対して一意の一時的な証明書機関(CA)と秘密鍵が作成され、CAがサンドボックスに配置されます。デフォルトでは、サンドボックスインスタンスはこのCAを信頼し、標準のコンテナインスタンスは、例えばsudo update-ca-certificatesを呼び出すことで、それを信頼することを選択できます。
export class MyContainer extends Container {
interceptHttps = true;
}
MyContainer.outbound = (req, env, ctx) => {