資格情報プロキシ — 技術仕様
プロキシの仕組み。
保護できるものと保護できないもの。
このページは、セキュリティレビュアー、ペネトレーションテスター、およびプロキシの脅威モデルを評価するエンジニアを対象としています。プロトコルレベルでのプロキシの動作、メモリ内での資格情報の存在場所、および残存する攻撃対象領域について説明します。
アーキテクチャ
このプロキシは、CONNECTベースのHTTPS MITMプロキシです。AIエージェントはHTTPS_PROXYを設定してこのプロキシを指すようにします。エージェントがHTTPSリクエストを行うと、プロキシはTLS接続をインターセプトし、リクエストヘッダー内の資格情報参照を検査してClavitorボールトに対して解決し、資格情報を注入したリクエストを上流のAPIに転送します。
プロキシはデフォルトで127.0.0.1:1983をリッスンします。これは、プロキシとエージェントが同じホストを共有するサイドカーパターンです。共有デプロイメント(プライベートネットワーク上で複数のエージェントにサービスを提供する1つのプロキシ、複数のワークロードに対するコンテナサイドカー、専用プロキシホスト)の場合、リッスンインターフェースはCLAVITOR_PROXY_LISTENで設定できます。
プロキシはスタンドアロンのGoバイナリです。CGOは使用しません。Clavitorプロトコルのすべての暗号化処理は、WebAssemblyにコンパイルされ、起動時にwazero経由で読み込まれる正規のRust実装を通じてルーティングされます。
TLSの処理
プロキシは初回実行時に自己署名ECDSA P-256ルートCAを生成し、バイナリのディレクトリにモード0600で永続化します。各上流ホストに対して、オンデマンドでリーフ証明書が生成され、このCAによって署名され、制限付きエビクション(1,000ホスト)を備えたメモリにキャッシュされます。リーフ証明書の有効期間は24時間で、セッション中の有効期限切れを防ぐため、23時間経過時に透過的に再生成されます。
エージェントはプロキシのCA証明書を信頼する必要があります。clavitor-proxy caでエクスポートします。
上流への接続では、HTTP/2およびHTTP/1.1のALPNネゴシエーションを伴うTLS 1.3以上を使用します。上流の検証にはシステム証明書プールが使用されます。証明書ピンニングは行わず、プロキシはOSが信頼するものをそのまま信頼します。
資格情報はキャッシュされず、ディスクに書き込まれず、1つのHTTPリクエストを超えて保持されることもありません。
| フェーズ | 資格情報の存在場所 | 期間 |
|---|---|---|
| ボールト内での保存時 | ボールトデータベース内のAES-GCM暗号文 | 削除されるまで |
| プロキシへの転送中 | ボールトAPIからのTLS暗号化されたJSONレスポンス | 1回のHTTPラウンドトリップ |
| プロキシ内での復号時 | プロセスメモリ(ヒープ上のGo文字列) | 1回のHTTPリクエスト |
| 上流リクエストへの注入時 | 上流への回線上のTLS暗号化されたバイト列 | 1回のHTTPリクエスト |
プロキシは、実行期間中ずっとエージェントの資格情報復号鍵(16バイト)をメモリに保持します。この鍵は起動時に暗号化されたサイドカー設定(CLV1形式)から読み込まれ、グレースフルシャットダウン時にクリアされます。鍵がプロセス外に出ることはありません。
サイドカー設定は、静的シードから導出された決定論的鍵を使用してAES-128-GCMおよびHMAC-SHA256で暗号化されます。これは難読化であり、機密性ではありません。セキュリティ境界はファイル権限(0600)とファイルの所有権です。CLV1形式は、プロキシ、CLI、およびブラウザ拡張機能間で共有されます。
解決モード
モード1 — 明示的プレースホルダー
エージェントはリクエストヘッダーにclavitor://Entry/field参照を含めます。プロキシは名前でエントリをボールトから検索し、それを取得して指定されたフィールドを復号し、プレースホルダーを実際の値に置換します。
検索結果が0件または2件以上の場合、プロキシは安定したエラーコードと共に502を返します。プレースホルダーが削除され、そのまま転送されることは決してありません。
モード2 — URL一致
プレースホルダーが存在しない場合、プロキシはURLフィールドが上流ホストと一致するエントリをボールトに要求します。認識されたフィールド形状を持つ一致がちょうど1つ存在する場合、プロキシは資格情報を自動的に注入します。
一致なし → パススルー(資格情報の期待なし)。複数の一致 → 曖昧さ解消のガイダンスを含む502。不明なフィールド形状 → 502。
決定木は決定論的です:プレースホルダーあり → 解決または失敗。プレースホルダーなし → URL一致またはパススルー。解決の失敗により資格情報なしでリクエストが上流に送信されるという、サイレントなフォールバックパスは存在しません。
デフォルトでは、ボールトはすべてのリクエストでプロキシ自身のエージェントIDを確認します。レート制限、アクセス範囲の限定チェック、および監査ログはプロキシに帰属します。
複数のエージェントが1つのプロキシインスタンスを共有する場合、プレースホルダーにエージェントIDを含めることができます:clavitor://agentid@Entry/field。プロキシはこのエージェントIDをボールトに送信し、ボールトはそのエージェントのアクセス範囲の限定とレート制限を適用し、そのアクセスをログに記録します。エージェントIDは、ボールトUIのエージェント詳細ページに表示される32文字の16進数値です。
# Without agent ID — attributed to the proxy Authorization: Bearer clavitor://OpenAI/key # With agent ID — attributed to agent 0102030405060708090a0b0c0d0e0f10 Authorization: Bearer clavitor://0102030405060708090a0b0c0d0e0f10@OpenAI/key
| デプロイメント | アイデンティティモデル | 分離 |
|---|---|---|
| エージェントごとに1つのプロキシ | プロキシID = エージェントID(デフォルト) | 完全 — 個別のバイナリ、設定、アクセス範囲の限定、レート制限 |
| 共有プロキシ、URLにエージェントIDなし | すべてのエージェントがプロキシのIDを共有 | 共有されたアクセス範囲の限定とレート制限 |
共有プロキシ + URLにagentid@ | エージェントごとのアイデンティティ | エージェントごとのアクセス範囲の限定、レート制限、および監査ログ |
URL内のエージェントIDは認証メカニズムではありません。接続の認証はプロキシのCVTトークンによって行われます。エージェントIDは帰属を決定します。すなわち、誰のアクセス範囲の限定が適用されるか、誰のレート制限がカウントされるか、誰の監査証跡にアクセスが記録されるかです。ボールトは不明なエージェントIDを明確なエラーで拒否します。
ネットワークセキュリティ
SSRF対策
デフォルトでは、プロキシはプライベートネットワーク(RFC 1918)、クラウドインスタンスメタデータ(169.254.169.254)、ループバック、リンクローカル、およびキャリアグレードNAT範囲への上流接続をブロックします。DNSが最初に解決され、TCP接続が確立される前に返されたすべてのIPが検証され、DNSリバインディングのTOCTOUウィンドウを閉じます。
正当にプライベートAPIにアクセスするエージェントの場合は、CLAVITOR_PROXY_ALLOW_PRIVATE=trueでオーバーライドします。
ターゲットピンニング
CONNECTターゲットホストはトンネル確立時にキャプチャされ、トンネルの存続期間全体で使用されます。トンネル内の後続のリクエストは、Hostヘッダーを操作して別のホストにリダイレクトすることはできません。不一致の場合は502になります。
これにより、エージェントがapi.openai.comへのトンネルを確立した後、internal-service.corpにリクエストを送信することを防ぎます。
ヘッダーの処理
RFC 7230 §6.1に従い、ホップバイホップヘッダーはリクエストとレスポンスの両方から削除されます:Connection、Keep-Alive、Proxy-Authenticate、Proxy-Authorization、Proxy-Connection、TE、Trailers、Transfer-Encoding、Upgrade。
上流がエージェントのHTTPクライアントにCookieを仕込むのを防ぐため、上流からのレスポンスからSet-Cookieが削除されます。
リクエストとレスポンスのボディはバッファリングなしでストリーミングされます。リクエストボディはデフォルトで64 MBに制限されています(CLAVITOR_PROXY_MAX_BODY_MB)。レスポンスボディはハードキャップなしでストリーミングされますが、Content-Lengthが100 MBを超えるとログに警告が出力されます。
URL一致モードでは、プロキシはボールトのフィールドラベルをHTTPヘッダーにマッピングします:
| フィールドラベル | 注入されるヘッダー |
|---|---|
key、apikey、api_key、token、secret、bearer、access_token | Authorization: Bearer <value> |
x-api-key、api-key | X-API-Key: <value> |
username + password(ペア) | Authorization: Basic base64(user:pass) |
| その他すべて | 拒否 — ERR-PROXY-052 |
プレースホルダーモードでは、どのフィールドが解決され、どこに配置されるかをエージェントが制御します。上記のマッピングはURL一致モードにのみ適用されます。
すべての失敗は安定したERR-PROXY-NNNコードを生成します。これらのコードはプロキシのパブリックインターフェースの一部であり、エージェントとオペレーターはアラートとデバッグのためにこれらに一致させることができます。
| 範囲 | カテゴリ |
|---|---|
001–019 | セットアップ(設定、初期化、CA生成、WASM) |
020–029 | デーモンのライフサイクル |
030–049 | プレースホルダーの解決(clavitor:// URI) |
050–069 | URL一致の注入 |
070–089 | 上流 / TLS |
アイデンティティ暗号化で保護されたデータにはアクセスできません
ボールトエントリは3つの暗号化レベルをサポートします。ボールト暗号化フィールドはプレーンテキストのメタデータです。資格情報暗号化フィールドはエージェントの鍵で復号されます。アイデンティティ暗号化で保護されたデータは、サーバーとプロキシが認識したことのない鍵で暗号化されます。ボールトの所有者のみが、自身のハードウェアキーを介してそれらを復号できます。
プレースホルダーがアイデンティティ暗号化で保護されたデータを参照している場合、プロキシはERR-PROXY-035を返します。フォールバックも部分的な結果もありません。そのデータはアーキテクチャ上、プロキシからアクセスできません。
プロキシが保護できないもの
プロキシの脅威モデルは、認証されたエージェントに資格情報を収集させる、侵害されたスキルまたはプロンプトインジェクションです。ボールトのエージェントごとのレート制限、一意エントリクォータ、およびツーストライクロックダウンが主要な防御策です。プロキシは、資格情報がリクエストごとに解決され、エージェントによって保持されることがないネットワーク層の強制ポイントを追加します。
プロキシはエージェントと同じマシン上で実行されます。ルートアクセス権を持つ攻撃者は、プロセスメモリを読み取ったり、デバッガーをアタッチしたり、ループバックトラフィックをインターセプトしたりできます。プロキシは資格情報注入レイヤーであり、ハードウェアセキュリティ境界ではありません。
上流のAPIがレスポンスで資格情報をエコーバックする場合(「whoami」エンドポイントなど)、エージェントはそれを確認できます。プロキシはレスポンスではなくリクエストに資格情報を注入します。返ってくるものをフィルタリングすることはありません。
ログ記録
プロキシは、受け入れられたCONNECTごとに1行のログを記録し、失敗時にはエラー行を出力します。以下のものがログに記録されることは決してありません:
- 復号された資格情報の値
- 完全なリクエストURL(クエリ文字列にシークレットが含まれる可能性があるため、スキーム + ホスト + パスのみがログに記録されます)
- リクエストまたはレスポンスのボディ
- 資格情報復号鍵またはサイドカー設定の内容
プロキシは、上流からの400レスポンスに認証関連のキーワード(unauthorized、invalid tokenなど)が含まれていることを検出すると、注入された資格情報が古くなっている可能性を示唆する診断ヒントをログに記録します。レスポンスは変更されずに転送されます。
暗号化
Clavitorプロトコルのすべての暗号化処理(AES-GCMフィールド復号、HKDF鍵導出、base62エンコーディング、CVTトークン発行、CLV1設定のパック/アンパック)は、純粋なGo WASMランタイムであるwazeroを介して読み込まれる単一のWebAssemblyモジュール(clavis_crypto.wasm)内で実行されます。CGOは使用しません。ClavitorプリミティブのGoによる再実装もありません。
WASMモジュールは、ブラウザ、CLI、およびブラウザ拡張機能で使用されるのと同じRustクレート(clavis-crypto)からコンパイルされます。単一の信頼できるソース、単一のバイナリ、単一の監査対象領域です。
プロキシはTLSワイヤにGoのcrypto/tlsを、MITM証明書生成にcrypto/ecdsaを使用します。これらはトランスポートの懸念事項であり、Clavitorプロトコルの操作ではありません。
シークレット(資格情報復号鍵、エージェントID、デバイスID、ボールトURL)は、clavitor-proxy init中に一度だけ書き込まれる暗号化されたCLV1サイドカー設定に存在します。運用上の設定は環境変数に存在します:
| 変数 | デフォルト | 目的 |
|---|---|---|
CLAVITOR_PROXY_LISTEN | 127.0.0.1 | リッスンインターフェース。共有デプロイメントの場合は0.0.0.0に、特定のインターフェースIPの場合はそのIPに設定します。 |
CLAVITOR_PROXY_PORT | 1983 | リッスンポート |
CLAVITOR_PROXY_ALLOW_PRIVATE | false | RFC-1918 / プライベートネットワークへの接続を許可 |
CLAVITOR_PROXY_MAX_BODY_MB | 64 | リクエストボディサイズの上限 |
CLAVITOR_PROXY_WRITE_TIMEOUT | 300 | レスポンス書き込みタイムアウト(秒) |
CLAVITOR_CONFIG | (exe dir) | サイドカー設定パスのオーバーライド |
運用上の設定はシークレットではありません。暗号化された設定に含めるものではありません。デプロイメントツールがすでに管理している場所、すなわち環境変数に含めるものです。
ご自身でご確認ください。
暗号化は単一の監査可能なWASMアーティファクトです。脅威モデルは文書化されています。私たちが見過ごしているものを発見した場合は、ぜひお知らせください。