静的サーバー側の ASP.NET Core Blazor レンダリングの脅威軽減策に関するガイダンス

この記事では、開発者が静的サーバー側レンダリングを使用して Blazor Web Apps を開発するときに考慮する必要があるセキュリティに関する考慮事項について説明します。

Blazor を使用すると、対話型 Web アプリを作成するために、3 つの異なるモデルが 1 つに結合されます。 従来のサーバー側レンダリング (HTTP に基づく要求/応答モデル)、 対話型サーバー側レンダリング (SignalR に基づくレンダリング モデル)、 最後に、クライアント側レンダリング (WebAssembly に基づくレンダリング モデル) です。

サポートされるレンダー モードのいずれかで対話型コンポーネントがレンダリングされる場合、Blazor Web Apps には、対話型レンダリング モードに対して定義されるセキュリティに関する一般的な慮事項はすべてが適用されます。 以降のセクションでは、Blazor Web Apps での非対話型のサーバー側レンダリングに固有のセキュリティに関する考慮事項と、レンダー モードが相互にやり取りするときに適用される特定の側面について説明します。

サーバー側レンダリングに関する一般的な考慮事項

サーバー側レンダリング (SSR) モデルは、HTTP の従来の要求/応答モデルに基づきます。 そのため、SSR と HTTP 要求/応答の間には共通の懸念領域があります。 セキュリティに関する一般的な考慮事項と特定の脅威を適切に軽減する必要があります。 フレームワークには、これらの脅威の一部を管理するためのメカニズムが組み込まれていますが、それ以外の脅威はアプリ コードに固有であり、アプリで対処する必要があります。 これらの脅威は、次のように分類できます。

  • 認証と承認: アプリでは、ユーザーが認証され、アプリとアプリで公開されているリソースへのアクセスを承認されていることを確認する必要があります。 フレームワークには、認証と承認のためのメカニズムが組み込まれていますが、アプリでは、このメカニズムが適切に構成され、使用されていることを確認する必要があります。 認証と承認のための組み込みメカニズムについては、Blazor ドキュメントの "サーバー" セキュリティ ノードおよび ASP.NET Core ドキュメントの "セキュリティと Identity" ノードに関するページで説明されているため、ここでは説明しません。

  • 入力の検証とサニタイズ: クライアントから送られてきた入力はすべて、使用する前に検証し、サニタイズする必要があります。 そうしなければ、アプリが、SQL インジェクション、クロスサイト スクリプティング、クロスサイト リクエスト フォージェリ、オープン リダイレクトなどの形式の攻撃にさらされるおそれがあります。 入力は、要求内のどこからでも取得される可能性があります。

  • セッション管理: アプリがセッション固定、セッション ハイジャックなどの攻撃にさらされないようにするには、ユーザー セッションを適切に管理することが非常に重要です。 セッションに格納される情報は適切に保護し、暗号化する必要があり、アプリのコードでは、悪意のあるユーザーがセッションを推測または操作できないようにする必要があります。

  • エラー処理とログ: アプリでは、エラーが適切に処理およびログされていることを確認する必要があります。 そうしなければ、アプリが、情報漏えいなどの攻撃にさらされるおそれがあります。 これは、アプリによって、応答で機密情報が返される場合、またはアプリを攻撃するために使用されるおそれのあるデータを含む詳細なエラー メッセージが返される場合に発生する可能性があります。

  • データ保護: 機密データは適切に保護する必要があります。これには、WebAssembly で実行するときのアプリ ロジックなどがあり、これは簡単にリバース エンジニアリングできます。

  • サービス拒否: アプリは、サービス拒否などの攻撃にさらされないようにする必要があります。 これは、たとえば、アプリが、ブルート フォース攻撃に対して適切に保護されていない場合や、アクションによってアプリでリソースが過度に使用される場合などに発生します。

入力の検証とサニタイズ

クライアントから送られてきた入力はすべて、その情報がサーバーで生成され、保護されていたもの (CSRF トークン、認証 cookie、セッション識別子、認証された暗号化で保護されているその他のペイロードなど) でない限り、信頼できない情報と見なす必要があります。

通常、入力は、バインディング プロセス (たとえば、[SupplyParameterFromQuery] 属性[SupplyParameterFromForm] 属性など) を通じてアプリで使用することができます。 この入力を処理する前に、アプリでは、データが有効であることを確認する必要があります。 たとえば、フォーム データをコンポーネント プロパティにマップする場合、バインディング エラーがないことをアプリで確認する必要があります。 そうしなければ、アプリは無効なデータを処理する可能性があります。

入力を使用してリダイレクトを実行する場合、アプリでは、入力が有効であること、およびそれが、無効と見なされるドメインまたはアプリの基本パス内の無効なサブパスを指していないことを確認する必要があります。 そうしなければ、アプリはオープン リダイレクション攻撃にさらされ、攻撃者がユーザーを悪意のあるサイトにリダイレクトするリンクを作成するおそれがあります。

入力を使用してデータベースのクエリを実行する場合、アプリでは、入力が有効であること、およびその入力によってアプリが SQL インジェクション攻撃にさらされていないことを確認する必要があります。 そうしなければ、攻撃者が、データベースからの情報の抽出やデータベースの変更に使用できる悪意のあるクエリを作成できるおそれがあります。

ユーザー入力から取得した可能性のあるデータは、応答に含める前にサニタライズする必要もあります。 たとえば、入力には、クロスサイト スクリプティング攻撃の実行に使用できる HTML または JavaScript が含まれている可能性があり、ユーザーから情報を抽出したり、ユーザーに代わってアクションを実行したりするために使用されるおそれがあります。

フレームワークには、入力の検証とサニタイズに役立つ次のメカニズムが用意されています。

  • バインドされたすべてのフォーム データは、基本的な正確性について検証されます。 入力を解析できない場合、バインディング プロセスによってエラーが報告されるため、アプリでは、データに対して何らかのアクションを実行する前にそれを検出することができます。 組み込みの EditForm コンポーネントでは、OnValidSubmit フォーム コールバックを呼び出す前に、これが考慮されます。 1 つ以上のバインディング エラーがある場合、Blazor によってコールバックの実行が回避されます。
  • フレームワークでは、偽造防止トークンを使用して、クロスサイト リクエスト フォージェリ攻撃から保護します。 詳しくは、「ASP.NET Core Blazor の認証と認可」と、「ASP.NET Core Blazor フォームの概要」を参照してください。

すべての入力とアクセス許可は、特定のアクションの実行時にサーバーで検証され、その時点でデータが有効かつ正確であること、およびユーザーがアクションの実行を許可されていることを確認する必要があります。 このアプローチは、対話型サーバー側レンダリングについて提供されているセキュリティに関するガイダンスと一致します。

セッションの管理

セッション管理は、フレームワークによって処理されます。 フレームワークでは、セッション cookie を使用してユーザー セッションを識別します。 セッション cookieは、ASP.NET Core データ保護 API を使用して保護されます。 セッション cookie は、ブラウザーで実行される JavaScript コードからアクセスできないため、ユーザーが簡単に推測または操作することはできません。

サービス内に格納されているデータなどの他のデータに関しては、スコープ指定されたサービス内に格納する必要があります。スコープ指定されたサービスは、特定のプロセス インスタンス内のすべてのユーザー セッション間で共有されるシングルトン サービスとは異なり、特定のユーザー セッションごとに固有であるためです。

SSR に関しては、サービスの有効期間が 1 つの要求に制限されているため、ほとんどの場合、スコープ付きサービスと一時的なサービスの間に大きな違いはありません。 次の 2 つのシナリオには違いがあります。

  • サービスが、複数の場所または要求中の異なる時間に挿入される場合。
  • サービスが対話型サーバー コンテキストで使用される可能性がある場合。この場合、サービスは複数のレンダーに対応するため、サービスのスコープをユーザー セッションに設定することが基本です。

エラー処理とログ記録

フレームワークには、フレームワーク レベルでアプリをログする機能が組み込まれています。 フォームの偽造防止トークンが検証に失敗したとき、ルート コンポーネントがレンダリングを開始したとき、アクションがディスパッチされたときなどの重要なイベントは、フレームワークによってログされます。 記録することが重要な可能性のあるその他のイベントをログするのは、アプリの責任です。

フレームワークには、フレームワーク レベルでアプリのエラー処理を行う機能が組み込まれています。 フレームワークは、コンポーネントのレンダリング中に発生したエラーを処理し、エラー境界メカニズムを使用してわかりやすいエラー メッセージを表示するか、エラー ページをレンダリングするように構成された例外処理ミドルウェアにエラーをバブルアップできるようにします。

クライアントへの応答の送信が開始された後、ストリーミング レンダリング中に発生したエラーは、最終的な応答に一般的なエラー メッセージとして表示されます。 エラーの原因の詳細は、開発中にのみ含まれます。

データ保護

フレームワークには、特定のユーザー セッションの機密情報を保護するためのメカニズムが用意されており、組み込みコンポーネントでは、これらのメカニズムを使用して機密情報が確実に保護されます (cookie 認証を使用する場合のユーザー ID の保護など)。 フレームワークで処理されるシナリオ以外で、他のアプリ固有の情報を保護するのは、開発者コードの責任です。 これを行うための最も一般的な方法は、ASP.NET Core データ保護 API またはその他の形式の暗号化を使用することです。 一般的な規則として、アプリには次の責任があります。

  • ユーザーが別のユーザーの個人情報を検査または変更できないようにする。
  • ユーザーが、内部識別子など、別のユーザーのユーザー データを変更できないようにする。

データ保護に関しては、コードがどこで実行されているかを明確に理解する必要があります。 静的サーバー側レンダリング (静的 SSR )および対話型サーバー側レンダリング (対話型 SSR) の場合、コードはサーバーに保存され、クライアントに到達することはありません。 対話型 WebAssembly レンダー モードの場合、アプリ コードは "常にクライアントに到達" します。つまり、アプリ コードに格納されている機密情報は、アプリにアクセスできるすべてのユーザーが利用できることになります。 コードを "保護" するための難読化やその他の同様の手法は有効ではありません。 コードはクライアントに到達すると、リバースエンジニアリングされて機密情報が抽出されるおそれがあります。

サービス拒否

サーバー レベルでは、フレームワークにより、要求の最大サイズやヘッダー サイズなど、要求/応答パラメーターに制限が課せられます。 アプリ コードに関しては、Blazor のフォーム マッピング システムにより、MVC のモデル バインド システムで定義されるものと同様の制限が定義されます。

  • エラーの最大数に対する制限。
  • バインダーの最大再帰深度に対する制限。
  • コレクション内でバインドされる要素の最大数に対する制限。

さらに、フォーム キーの最大サイズ、値のサイズ、エントリの最大数など、フォームに対して定義される制限もあります。

一般に、アプリでは、要求によって、サーバーによる非対称の量の作業がトリガーされる可能性がある場合を評価する必要があります。 この例としては、ユーザーが N でパラメーター化された要求を送信し、サーバーで、応答として N 倍のコストがかかる操作が実行される場合があります。この場合、N は、ユーザーが制御するパラメーターであり、無制限に増加する可能性があります。 通常、アプリでは、処理できる最大 N に制限を課すか、またはどの操作も、一定の係数で要求よりも少ないまたは多いか、要求と等しいコストであることを保証する必要があります。

この側面には、特定の 1→N の比較よりも、クライアントが実行する作業とサーバーが実行する作業との間の増加の違いの方が大きく関係します。 たとえば、クライアントは、実行に N 単位の時間を要する作業項目 (一覧への要素の挿入) を送信する場合がありますが、サーバーで処理するには N^2^ が必要です (非常に単純な処理を実行している可能性があるため)。 重要なのは N と N^2^ の差です。

そのため、サーバーで実行する必要がある作業量には制限があり、これはアプリに固有です。 リソースはサーバー上にあるため、この側面はサーバー側のワークロードに適用されますが、ほとんどの場合、クライアント上の WebAssembly ワークロードには必ずしも適用されません。

もう 1 つの重要な側面は、これは CPU 時間だけに限定されないということです。 これは、メモリ、ネットワーク、ディスク上の領域など、あらゆるリソースにも適用されます。

WebAssembly ワークロードの場合、クライアントでは通常、実行する作業量が利用可能なリソースによって制限されるため、通常は作業量に関する懸念はほとんどありません。 ただし、たとえば、アプリで他のユーザーからのデータを表示し、あるユーザーがシステムにデータを追加して、データを表示するクライアントに、ユーザーが追加したデータ量に比例しない量の作業の実行を強制できる場合など、クライアントが影響を受ける可能性のあるシナリオがいくつかあります。

  • ユーザーが認証され、アプリとアプリで公開されているリソースへのアクセスを承認されていることを確認する。
  • クライアントから送られてきた入力はすべて、使用する前に検証し、サニタイズする。
  • ユーザー セッションを適切に管理して、状態がユーザー間で誤って共有されないようにする。
  • エラーを適切に処理してログし、機密データが公開されないようにする。
  • アプリ内の重要なイベントをログして潜在的な問題を特定し、ユーザーによって実行されるアクションを監査する。
  • ASP.NET Core データ保護 API または使用可能なコンポーネントのいずれか (Microsoft.AspNetCore.Components.Server.ProtectedBrowserStoragePersistentComponentState) を使用して機密情報を保護する。
  • 特定の要求で使用できるリソースがアプリで認識され、サービス拒否攻撃を回避するための制限が設定されていることを確認する。