問題と背景
マルチテナントSaaS APIゲートウェイが、サインインしたユーザーの代理として課金サービスを呼び出します。IDプラットフォームはゲートウェイ向けの上流アクセストークンを発行しますが、課金サービスは自身専用のオーディエンスのみを受け入れます。ゲートウェイは、誰が誰の代理として行動したかの監査可能な記録を保持しながら、よりスコープを絞った下流トークンを必要とします。
交換エンドポイント、検証ルール、クレームマッピング、キャッシュ、エラーハンドリング、失効、モニタリングを設計してください。ゲートウェイは管理されたサーバーサイドクライアントであり、ブラウザが下流トークンを保持することは決してないと仮定します。課金サービスは、他のサービス向けのトークンを拒否しなければなりません。
設計は次の5つの不変条件を維持する必要があります:
- 交換サービスは、交換が許可されている検証済みのサブジェクトトークンのみを受け入れる。
- 新しいオーディエンス、スコープ、テナント、リソースアクセス範囲は、上流の認可よりも広くなることはない。
- サブジェクトとアクターは明確に区別され、追跡可能である。サービスがユーザーになりすますことはできない。
- ダウンストリームサービスは、自身向けの発行者、オーディエンス、署名鍵のみを信頼する。
- 失効、有効期限、鍵のローテーション、監査境界には測定可能な時間制限が存在する。
面接官が評価しているポイント
まず、トークン交換、トークン転送、なりすまし(impersonation)、委任(delegation)の違いを明確にすることから始めます。RFC 8693はHTTP/JSONセキュリティトークンサービス(STS)を定義しています。リクエストにはsubject_tokenを含めることができ、オプションでactor_tokenを含めることができます。レスポンスは通常のOAuthトークンエンドポイントレスポンスを拡張したものです。このプロトコル自体がより広い権限を付与することはありません。
次の評価ポイントは、オーディエンスの規律です。ゲートウェイは、自身宛てのベアラートークンを課金、検索、エクスポートサービスにそのまま転送してはなりません。各ダウンストリームトークンには単一のオーディエンスと、その呼び出しに必要なスコープのみが必要です。
3つ目のポイントはプロキシ関係です。交換は、ポリシーによってアクターがサブジェクトの代理として行動することが許可されている場合にのみ認められます。may_actやactなどのクレームには、信頼できる発行者またはローカルポリシーソースが必要です。クライアントから提供された任意のsubやtenant_idを新しいトークンにそのままコピーすることは、特権昇格につながります。
最後に障害モードについて議論します:古い認可キャッシュ、署名は検証するがオーディエンスを検証しないダウンストリーム、ログへのトークン出力、ゲートウェイに渡されたリフレッシュトークン、ダウンストリームのTTLが切れるまで影響が残る上流の失効遅延などです。
最初に確認すべき明確化のための質問
- サブジェクトは誰で、アクターは誰か? ここではサブジェクトはユーザーであり、アクターはAPIゲートウェイです。サービスアカウントの偽装には明示的なポリシーが必要です。
- 交換されたトークンを消費するのは誰か? 課金サービスのみがオーディエンスなのか、それとも複数のリソースが許可されるのか?オーディエンスが複数になると影響範囲が拡大します。
- スコープはどのように縮小されるか? アップストリームに
billing:read billing:writeが含まれている場合、このリクエストにはどれが必要か? - テナント境界はどこでチェックされるか? どのID、セッション、リソースストアが信頼できる情報源(authoritative)か?リクエストボディのテナントフィールドを決して信用してはなりません。
- 失効の目標値は何か? 例えば、5秒以内に新しい交換を停止し、既存のダウンストリームトークンを2分以内に制限するなどです。
- ゲートウェイにリフレッシュトークンは必要か? 委任された呼び出しには通常、短命なアクセストークンが必要です。長寿命のリフレッシュトークンには個別の脅威レビューが必要です。
30秒での回答
「ゲートウェイがユーザーのsubject_token、ゲートウェイのactor_token、固定された課金オーディエンス、最小限のスコープを指定して、標準のトークンエンドポイントを呼び出すように設計します。交換サービスは、発行者、署名、exp、nbf、クライアント認証、サブジェクトの委任ポリシー、テナントの状態を検証します。ポリシーテーブルが上流スコープを許可された下流スコープにマッピングします。新しいトークンには課金オーディエンス、短い有効期限、テナントコンテキスト、サブジェクトとアクターの関係のみが含まれ、未検証のクレームをコピーすることはありません。
課金サービスは自身の発行者、署名鍵、オーディエンスのみを信頼し、リソースレベルの認可を実行します。交換の決定は短時間キャッシュされる場合がありますが、失効イベントと短いTTLによって目標要件を満たす必要があります。トークン本文、交換リクエスト、アクターシークレットはマスクされます。エラーは汎用的なものを返し、監査ログにはリクエストID、サブジェクト、アクター、オーディエンス、スコープ、ポリシーバージョンを保持します。」
ステップごとの設計
ステップ1:トークンと信頼境界の定義
交換サービスは認可サーバーまたは信頼されたSTSであり、単なるゲートウェイのヘルパーではありません。許可された上流発行者、JWKS、クライアント認証方式、下流オーディエンス、ポリシーバージョンを設定します。ゲートウェイはmTLS、プライベートキーJWT、またはその他の承認された方法で認証します。静的文字列だけでは委任権限の証明にはなりません。
最小限のリクエストは次のようになります:
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
subject_token=...
subject_token_type=urn:ietf:params:oauth:token-type:access_token
actor_token=...
actor_token_type=urn:ietf:params:oauth:token-type:access_token
audience=https://billing.internal
scope=billing:readRFC 8693では、リクエスト元を交換におけるクライアントとして扱います。リソースサーバーはそのクライアントとして一時的に動作し、受信したトークンをバックエンドサービスに適したトークンに交換できます。この役割自体が新しい権限を付与することはありません。
ステップ2:ソースによるサブジェクトとアクターの検証
トークンタイプごとにバリデーターを選択し、アルゴリズムの許可リスト、発行者、exp、nbf、必須クライアント、トークンタイプ、失効状態を確認します。発行者ごとにJWKSをキャッシュし、kidによってローテーションします。未知のキーを受信した場合は制御されたリフレッシュをトリガーできますが、アルゴリズムのダウングレードは決して許可しません。
サブジェクトは表現されているユーザーまたはワークロードです。アクターは実際に交換を開始したゲートウェイです。ポリシーは、アクターが既知のサービスであるかだけでなく、このアクターがこのオーディエンスにおいてこのサブジェクトの代理として行動できるかを判断しなければなりません。RFC 8693のmay_actは認可されたアクターを表現でき、交換されたトークンはactで現在のアクターを表現できます。両方のクレームには、信頼できる発行者またはローカルポリシーが必要です。
リクエストボディのsub、tenant_id、rolesを同一性の事実として扱ってはなりません。検証済みトークンと信頼できるテナントディレクトリからコンテキストを再構築し、欠落または競合するサブジェクトデータは拒否します。
ステップ3:単調に狭められた認可の計算
認可を制約された積集合として表現します:
issued_scope = requested_scope
∩ subject_allowed_scope
∩ actor_allowed_scope
∩ audience_policy_scope
∩ tenant_state_scope空または過度に広い要求スコープは拒否します。より広いデフォルトを暗黙的に発行してはなりません。オーディエンスはサービスレジストリから取得し、ゲートウェイから提供された任意のURLを使用してはなりません。各オーディエンスは、許可されたクレーム、スコープ、TTL、リソースタイプをバインドします。
テナントの状態、無効化されたユーザー、請求アカウントの所有権、高リスクのアクションには、オンラインポリシーチェックが必要になる場合があります。トークンのtenant_idは検証済みコンテキストの一部にすぎません。リソースサービスは引き続きそれを所有権情報と比較します。リクエストが複数のサービスを必要とする場合は、1つのユニバーサルオーディエンスではなく、短命な交換を複数回行うアプローチを採用します。
ステップ4:短命で検証可能なダウンストリームトークンの発行
アクセストークンには、iss、sub、aud、exp、iat、スコープ、テナントID、クライアントID、ポリシーバージョン、アクター関係を含める必要があります。その有効期限は、アップストリームの残り有効期間またはビジネスリスクウィンドウを超えてはなりません。通常の交換結果としてリフレッシュトークンを返してはなりません。
課金サービスは、発行者、オーディエンス、JWKS、許可されたアルゴリズムを固定し、リクエストごとにリソースの認可を実行します。audの検証を行わずに署名検証のみを行うと、あるサービス向けに発行されたトークンを別のサービスが受け入れてしまう恐れがあります。送信者拘束(Sender-constrained)トークンを使用することで、ベアラートークンの値を単にコピーするだけでは不十分であることをさらに確実にできます。
ステップ5:キャッシュ、失効、鍵ローテーションの連携
発行者メタデータ、JWKS、短期間のポリシー結果をキャッシュするのは、キャッシュキーに発行者、サブジェクト、アクター、オーディエンス、スコープ、テナント、ポリシーバージョンが含まれている場合のみとします。あるテナントの許可決定を別のテナントに再利用してはなりません。失効が発生した場合は、まずオーソリティを更新して無効化イベントを発行します。交換エンドポイントは5秒以内に新規発行を停止し、新しいダウンストリームトークンの最大TTLは2分とします。課金サービスはリスクに応じてイントロスペクションまたは短いTTLでの検証を選択できます。
無効化メッセージの消失、ノードの再起動、レプリケーション遅延のテストを実施します。JWKSのローテーションには制限されたオーバーラップと追跡可能なkidが必要です。署名鍵の失効はその鍵で署名されたすべてのトークンに影響を与えるため、サブジェクトレベルの失効の代用にはなりません。
ステップ6:エラー、リプレイ、可用性の処理
未知の発行者、期限切れ、誤ったオーディエンス、委任不可のサブジェクト、不十分なスコープ、ポリシー利用不可に対しては、統一されたinvalid_grantまたはinvalid_targetクラスのエラーを返します。サブジェクトが存在するかどうかを外部に漏らさないようにし、リクエストIDと安全な理由コードを内部的に保持します。
交換リクエストにはベアラー認証情報が含まれているため、URL、通常のログ、トレース属性、エラーレポートに入力してはなりません。アップストリームトークンがリプレイ可能な場合、ゲートウェイのトラフィックを盗聴した攻撃者が交換を繰り返す可能性があります。TLS、送信者拘束、短いTTL、レート制限、リスクシグナルによってその攻撃ウィンドウを縮小します。ポリシーメタデータストレージが利用できない場合、高リスクの書き込み操作に対してはフェイルクローズ(拒否)します。低リスクの読み取りフォールバックには、古いポリシーの許容最長時間を設定する必要があります。
ステップ7:サブジェクトチェーンとテナント境界の監査
request_id、サブジェクト、アクター、クライアント、テナント、オーディエンス、要求されたスコープと発行されたスコープ、トークンID、ポリシーバージョン、発行者の鍵ID、決定結果を記録します。トークン本文は絶対に記録してはなりません。ゲートウェイの呼び出しをユーザーアクションに関連付けられるように、ダウンストリームログにはトークンIDとリクエストIDを参照させます。
リソースサービスは、ゲートウェイからのX-Tenant-IDヘッダーのみを信用してはなりません。署名済みトークンからテナントコンテキストを読み取り、リソースの所有権、請求アカウントの状態、承認ルールを確認します。テナント間アクセス、Confused Deputy問題、スコープ拡大、不正なオーディエンス、古いトークンのリプレイに対する敵対的テストを追加します。
ステップ8:段階的な検証を伴うロールアウト
1つのテストテナントに対して固定のオーディエンスとスコープを登録し、古いトークンでは課金サービスにアクセスできないことを実証します。交換拒否率、ポリシーレイテンシ、トークンTTL、オーディエンスエラー、キャッシュヒット率、失効伝播を監視しながら、新しい発行者と鍵をカナリアリリースします。測定結果が安定した後にのみ、本番テナントへ展開します。
新規発行を停止し、検証済みの正常なクライアント構成に戻すロールバックスイッチを保持します。ダウンストリームのオーディエンス検証を無効化したり、監査ログを削除したりする形でのロールバックは行わないでください。
模範的な高評価の回答
「私は交換サービスを信頼されたSTSとして扱います。ゲートウェイは、クライアント認証されたユーザーのsubject_token、ゲートウェイ自身のactor_token、固定された課金オーディエンス、最小限のスコープを送信します。サービスは、すべての発行者、署名、時間クレーム、トークンタイプ、クライアント、委任ルールを検証し、要求されたスコープとサブジェクト、アクター、オーディエンス、テナント状態ポリシーとの積集合を計算します。任意のオーディエンスや権限を拡大するリクエストはすべて拒否します。
ダウンストリームトークンは課金サービス専用であり、有効期限はアップストリームトークン以下とし、サブジェクト、アクター、テナント、スコープ、ポリシーバージョン、トークンIDを記録します。課金サービスは発行者、JWKS、オーディエンスを固定し、リソースレベルのテナント認可を引き続き実行します。トークンや交換リクエストがログに出力されることは決してありません。監査記録にはチェーンと決定メタデータのみが含まれます。
失効時はオーソリティが更新され無効化通知がブロードキャストされ、5秒以内に新規交換が停止されます。ダウンストリームトークンの有効期間は最大2分とし、リスクに応じてイントロスペクションまたは短TTL検証が選択されます。キャッシュキーにはすべてのIDおよびポリシーディメンションが含まれ、JWKSローテーションには制限されたオーバーラップが設けられます。トークン交換は権限を狭め、アカウンタビリティを維持するためのものであり、すべてのサービスにユーザーIDを単にコピーするものではありません。」
よくある間違い
- アップストリームのベアラートークンを転送してしまう。 オーディエンスが不適切であり、漏洩が横方向に拡大します。リソースごとに範囲を絞ったトークンに交換すべきです。
- 署名のみをチェックする。 有効な署名は、発行者、オーディエンス、またはスコープを証明するものではありません。
- サブジェクトとアクターを混同する。 ダウンストリームでユーザーとプロキシサービスを区別できなくなり、監査と失効の意味が失われます。
- リクエストボディの
tenant_idを信用する。 攻撃者が別のテナントを指定する可能性があります。検証済みの同一性とリソースの所有権から導出する必要があります。 - 交換を許可されたアクターが誰の代理でもできると思い込む。 委任には明示的なポリシーと信頼できるソースが必要です。
- デフォルトでリフレッシュトークンを発行する。 ゲートウェイに必要なのは短命なアクセストークンです。長寿命の認証情報はリスクウィンドウを広げます。
- すべてのディメンションを含めずに、または長いTTLでキャッシュする。 認可がテナントをまたいだり失効後も残存したりする可能性があります。キー、バージョン、伝播を制限してください。
- トークンや交換リクエストをログに記録する。 ベアラー値はリプレイ可能です。トークンID、公開メタデータ、理由コードのみをログに記録してください。
- すべてのダウンストリームに単一のトークンを使用する。 オーディエンスとスコープが広くなりすぎます。個別に交換してください。
- 成功ケースのみをテストする。 期限切れ、誤ったオーディエンス、失効、JWKSローテーション、再起動、ポリシー障害によって、実際の境界の堅牢性が明らかになります。
フォローアップの質問と模範回答
may_actとactは何を解決しますか?
may_actはサブジェクトトークンがどのアクターに行動を認可しているかを示し、actは新しいトークンにおいて誰が現在誰の代理として行動しているかを示します。どちらも署名、発行者、オーディエンス、またはローカルの認可チェックを置き換えるものではなく、信頼できるソースが必要です。
なぜユーザートークンを課金サービスに転送してはいけないのですか?
アップストリームトークンは通常ゲートウェイを対象としており、複数のリソースをカバーしている場合があります。転送すると、オーディエンスと権限のエクスポージャーが拡大します。交換により、課金サービス専用の短命で監査可能なトークンが発行されます。
課金サービスはオンラインイントロスペクションを使用すべきですか?
失効の目標、トラフィック、可用性の要件によります。リスクの高い書き込み操作にはイントロスペクションを使用し、通常の読み取りには短いTTLのJWTを使用できます。最悪の遅延を測定する必要があります。境界メカニズムなしにオフラインキャッシュと即時失効の両方を同時に満たすことはできません。
ポリシーサービスが利用できない場合はどうしますか?
高リスクの交換についてはフェイルクローズ(拒否)します。低リスクの読み取りについては、明示的に制限された古いポリシーキャッシュを使用することがあり、そのフォールバック状態はモニタリングと監査で可視化されます。デフォルトで許可してしまうと、可用性の問題が特権昇格につながります。
マルチテナントのキャッシュ混同をどのように防ぎますか?
発行者、サブジェクト、アクター、テナント、オーディエンス、スコープ、ポリシーバージョンをキャッシュキーに含め、リソースの所有権も引き続き確認します。ユーザーIDまたはオーディエンスのみに基づくキーは、あるテナントの決定を別のテナントに再利用してしまう可能性があります。
上流トークンが失効したにもかかわらず、下流トークンがまだ有効な場合はどうなりますか?
新規の交換を即座に停止し、ダウンストリーム側でリスクに応じてイントロスペクションを行うか無効化イベントを消費させます。オフライン検証のみを行う場合は、TTLを許容される最大ウィンドウに制限し、その間の操作を監査します。コミットされた操作を取り消すことはできません。
安全にロールバックするにはどうすればよいですか?
新規の交換を停止し、問題のあるオーディエンスまたはポリシーバージョンを取り消し、古い署名鍵の制限付き検証を保持し、検証済みの正常なクライアント構成を復元します。近道としてオーディエンスチェックを無効化したり、監査証跡を削除したりしてはなりません。