ブローカー サービスをセキュリティで保護する
既定では、仲介型サービスを使用できるのは、ローカル ユーザーと、これをアクティブ化した Visual Studio セッションに関連するプロセスのみになります。 これらの既定値では、仲介型サービスのセキュリティに関する考慮事項は、これらのプロセスで実行されている他のコードと変わりません。たとえば、次のようになります。
- 脅威モデルの観点からは、Visual Studio プロセス内で実行される拡張機能は完全に信頼できると見なされます。 プロセス外で実行される拡張機能では、Visual Studio サービス呼び出しを、信頼境界を越えるものとして扱う必要があります。
- コードでは、エントリ ポイントで引数を検証し、予期されるパターンまたは範囲に収まっていることを確認する必要があります。
- ディスクからデータを読み取る場合は、データが改ざんされている可能性があることを考慮してください。
- ネットワークまたはインターネットからデータを受信する場合は、一般的な脆弱性を避けるために、データの解析または逆シリアル化を行う際に注意が必要です。
ProvideBrokeredServiceAttribute.AllowTransitiveGuestClients フラグを設定してサービスが登録されている場合は、いくつかの重要なセキュリティ上の考慮事項が追加で適用されます。 この記事の残りの部分では、これらの考慮事項に焦点を当てています。
機密性の高い操作の承認チェック
承認サービスの取得
仲介型サービスには、パラメーターとして AuthorizationServiceClient を受け取るコンストラクターが必要です。 この引数はフィールドに格納し、サービス Dispose() のメソッドで破棄する必要があります。
class Calculator : ICalculator, IDisposable
{
private readonly AuthorizationServiceClient authorizationService;
internal Calculator(AuthorizationServiceClient authorizationService)
{
this.authorizationService = authorizationService;
}
public void Dispose()
{
this.authorizationService.Dispose();
}
}
この新しいパラメーターをサポートするために、提供するサービス ファクトリは若干変更されます。 BrokeredServiceFactory を IBrokeredServiceContainer.Proffer メソッドに指定する代わりに、AuthorizingBrokeredServiceFactory デリゲートを指定します。 このデリゲートで、仲介型サービスに渡す必要がある AuthorizationServiceClient を受け取ります。
Proffer コードに対するこの変更は、次のようになります。
container.Proffer(
CalculatorService,
- (moniker, options, serviceBroker, cancellationToken) => new ValueTask<object?>(new CalculatorService()));
+ (moniker, options, serviceBroker, authorizationService, cancellationToken) => new ValueTask<object?>(new CalculatorService(authorizationService)));
承認サービスの使用
機密情報を開示したりユーザーの状態を変更したりする可能性のある操作は、AuthorizationServiceClient.AuthorizeOrThrowAsync を使用して承認サービスで確認する必要があります。
呼び出し元がコードの所有者 (Live Share ホストのオペレーターと同じ ID) であることをアサートするには、こちらのコードを使用できます。
private static readonly ProtectedOperation ClientIsOwner = WellKnownProtectedOperations.CreateClientIsOwner();
public ValueTask ResetOperationCounterAsync(CancellationToken cancellationToken)
{
// Resetting the counter should only be allowed if the user is the owner.
await this.authorizationService.AuthorizeOrThrowAsync(ClientIsOwner, cancellationToken);
// Proceed with the operation.
this.operationCounter = 0;
}
WellKnownProtectedOperations クラスには、他のさまざまな承認レベルが定義されています。
サービス クライアントが同じコンピューターとユーザー アカウントで実行されている場合は、すべての承認チェックが常に承認されます。 また、ホストと同じ Microsoft アカウントで動作している Live Share ゲストに対してもすべて承認されます。
要求された操作が承認 "されない" 場合は、AuthorizeOrThrowAsync によって UnauthorizedAccessException がスローされます。 Live Share ホストは、失敗した試行の所有者に通知し、ProtectedOperation が認識された場合、クライアントでの後続の試行が成功するように、操作を完了するために必要なアクセス許可をホストが付与できるようにする場合があります。
AuthorizationServiceClient ではすべての承認チェックがローカルにキャッシュされるため、繰り返される承認チェックは高速になります。 ユーザーのアクセス許可セットが変更された (たとえば、Live Share ホストによってゲストのアクセス許可が変更された) 場合、ローカル キャッシュは自動的にフラッシュされます。
他の仲介型サービスの使用
仲介型サービス自体が別の仲介型サービスにアクセスする必要がある場合は、サービス ファクトリに提供されている IServiceBroker を使用する必要があります。 グローバル サービス ブローカーでは、この特定の仲介型サービス インスタンスのコンテキストと、他の動作をアクティブ化して呼び出すためにクライアントが持っている承認は認識されないため、使用 "しない" でください。
電卓サービスで動作を実装するために他の仲介型サービスが必要な場合は、IServiceBroker を受け入れるようにコンストラクターを変更します。
internal class Calculator : ICalculator
{
private readonly IServiceBroker serviceBroker;
private readonly AuthorizationServiceClient authorizationService;
internal class Calculator(IServiceBroker serviceBroker, AuthorizationServiceClient authorizationService)
{
this.serviceBroker = serviceBroker;
this.authorizationService = authorizationService;
}
}
この追加パラメーターは、サービス ファクトリの提供コードに影響します。
container.Proffer(
CalculatorService,
(moniker, options, serviceBroker, authorizationService, cancellationToken)
- => new ValueTask<object?>(new CalculatorService(authorizationService)));
+ => new ValueTask<object?>(new CalculatorService(serviceBroker, authorizationService)));
仲介型サービスの可用性の制限
仲介型サービスのクライアントが Live Share ゲスト (ホストの所有者とは異なるアカウント) の場合、コンテキスト サービス ブローカーは、セキュリティ上の予防措置として同様に AllowTransitiveGuestClients フラグを設定している他の仲介型サービスのみをアクティブ化します。 条件を満たしていない仲介型サービスをアクティブ化しようとすると、UnauthorizedAccessException がスローされます。
お使いの仲介型サービスで、AllowTransitiveGuestClients フラグが設定されていない別の仲介型サービスが必要な場合は、グローバル サービス ブローカーを使用して取得できますが、取得された仲介型サービスでは、信頼されていないゲストが最終的なクライアントであることは全く認識されていないことを考慮する必要があります。 他の VS サービスや他の API の呼び出しに関して次のセクションで示されているすべての同じ注意事項に従う必要があります。
仲介型サービスの使用について詳しくは、こちらをご覧ください。
他の VS サービスまたは他の API の使用
Live Share ゲストに公開されている仲介型サービスでは、標準の Visual Studio サービス、サード パーティ製ライブラリ、または標準の .NET API の呼び出しが許可されていますが、そのような呼び出しは、慎重に記述する必要があり、またすべての入力を最初に検証する必要があります。
ファイル パスまたは URL は慎重にチェックし、それらが有効であり、ゲストがアクセス許可を持っている、予期されたサブパス内にあることを確認する必要があります。
たとえば、仲介型サービスで、パスに基づくファイルの読み取りまたは書き込みが許可されている場合は、そのパスがオープン ソリューションに分類されること、およびゲストが実際に書き込みアクセス許可を持っていること (該当する場合) を確認する必要があります。
..
や、パスが正しいプレフィックスで始まっているように見せながら、許可されたソリューション ディレクトリをエスケープする他の方法を考慮すると、ファイル パスを適切に検証することは困難な場合があります。
上記のセクションで説明した AuthorizationServiceClient を必要に応じて利用して、独自のアクセス許可チェックが組み込まれていない API を呼び出す前に、クライアントがアクセス許可を持っていることをアサートします。 独自の承認チェックが含まれているのは、Visual Studio に組み込まれた仲介型サービスのみであると想定すべきです。そして、上記のセクションで説明したように、これらの仲介型サービスは、コンテキスト サービス ブローカーを使用して取得する必要があります。
非仲介型の Visual Studio サービス、またはグローバル サービス ブローカーを使用して取得された仲介型サービスを含む他のすべての API は、Live Share ゲストのアクセス許可レベルに関係なく、ユーザーの指示したとおりに実行される可能性があるため、Live Share ホストのセキュリティを保護するには独自の承認チェックが不可欠になります。
別の Visual Studio の仲介型サービスで既に公開されている機能を自身の仲介型サービスから公開すると、それによって攻撃対象領域が増加するので、行わないようにしてください。
仲介型サービス インスタンス間での状態の共有
お使いの仲介型サービスで、そのサービスの複数のインスタンス間で状態を共有することが必要な場合、このデータは、さまざまなアクセス許可セットを持つ複数のユーザーに公開される可能性があります。 これらのユーザー間でこのデータを保護することが仲介型サービスにとって重要になります。 脅威を特定し、分類し、最終的に軽減するには、STRIDE モデルを使用します。
共有状態を信頼済みとして扱い、内部的に必要な操作 (VS サービスへのアクセスやグローバル サービス ブローカーの使用など) を行うためのアクセス許可を与えると決める場合があります。 このような場合、共有状態に対して行われた呼び出しを保護し、承認サービスを使用して、独自のユーザーのアクセス権を前提としてすべての入力が適切であることを確認することは、個々の仲介型サービス インスタンスの責任になります。
Microsoft Threat Modeling Tool は、共有状態とユーザーをセキュリティで保護するための便利なツールです。