セキュリティ保護された ASP.NET アプリケーションの構築 : 認証、認定、および通信のセキュリティ保護 動作のしくみ
J.D. Meier, Alex Mackman, Michael Dunner, and Srinath Vasireddy
Microsoft Corporation
November 2002
日本語版最終更新日 2003 年 3 月 17 日
適用対象:
Microsoft® ASP.NET
Microsoft® Windows® 2000
全体の概要については、「セキュリティ保護された ASP.NET アプリケーションの構築」の開始ページを参照してください。
要約 : Tこの付録では、このガイドの各章で取り上げたコンセプトやプロセスが実際にどのように動作するかを詳細に説明します。
目次
IIS と ASP.NET の処理
ASP.NET のパイプライン処理
IIS と ASP.NET の処理
メモ ここで述べる内容は、Windows 2000 上で稼動している インターネット インフォメーション サービス (IIS) 5 に該当します。
ASP.NET Web アプリケーションと Web サービスは、ASP.NET ワーカー プロセスの単一のインスタンス (aspnet_wp.exe) で実行されるコードによって処理されます。ただし、マルチプロセッサ コンピュータの場合は、プロセッサの数と同じだけの複数のインスタンスを構成することができます。
IIS は、呼び出し元を認証し、呼び出し元に対応する Windows アクセス トークンを作成します。IIS 内で匿名アクセスを有効化している場合は、匿名のインターネット ユーザー アカウント (IUSR_MACHINE など) の Windows アクセス トークンが作成されます。
ASP.NET ファイル タイプの要求は、IIS (inetinfo.exe) プロセスのアドレス空間で実行される ASP.NET ISAPI 拡張機能 (aspnet_isapi.dll) によって処理されます。図 1 に示すように、aspnet_isapi.dll は、名前付きパイプを使用して ASP.NET ワーカー プロセスと通信します。IIS は、呼び出し元を表す Windows アクセス トークンを ASP.NET ワーカー プロセスに渡します。ASP.NET Windows 認証モジュールは、このアクセス トークンを使用して WindowsPrincipal オブジェクトを構築します。ASP.NET File 認定モジュールは、構築された WindowsPrincipal オブジェクトを使用して、Windows アクセス チェックを実施し、呼び出し元が要求したファイルにアクセスする権限を持っていることを確認します。
図 1 IIS と ASP.NET の通信
メモ アクセス トークンは、プロセスを基準とする相対トークンなので、inetinfo.exe で実行されている ASP.NET ISAPI DLL は、DuplicateHandle を呼び出して、aspnet_wp.exe プロセスのアドレス空間にトークン ハンドルを複製し、名前付きパイプを通じてハンドル値を渡します。
アプリケーションの分離
ワーカー プロセス内では、IIS 仮想ディレクトリごとに 1 つずつ (つまり、ASP.NET Web アプリケーションまたは Web サービスごとに 1 つずつ) の個別のアプリケーション ドメインを使用することにより、アプリケーションの分離が実現されます。
これに対し、従来の ASP では、IIS メタベース内で構成されたアプリケーション保護レベルによって、ASP アプリケーションを IIS のプロセス (inetinfo.exe) 内で実行するか、プロセス外の専用 Dllhost.exe インスタンスで実行するか、共有 (プール化) Dllhost.exe インスタンスで実行するかどうかが決定されるようになっていました。
重要 IIS 内でのプロセス分離レベルの設定は、ASP.NET Web アプリケーションがどのように処理されるかに一切影響を及ぼしません。
ASP.NET ISAPI 拡張機能
ASP.NET ISAPI 拡張機能 (aspnet_isapi.dll) は、IIS プロセスのアドレス空間 (inetinfo.exe) 内で実行され、ASP.NET ファイル タイプの要求を名前付きパイプを経由して ASP.NET ワーカー プロセスに転送します。
ASP.NET ISAPI 拡張機能には、IIS メタベース内で定義されているマッピングによって、特定の ASP.NET ファイル タイプがマップされます。標準 ASP.NET ファイル タイプ (.aspx、.asmx、.rem、.soap など) のマッピングは、.NET Framework のインストール時に確立されます。
■ アプリケーションのマッピングを表示するには
[管理ツール] プログラム グループから[インターネット サービス マネージャ]を起動します。
Web サーバー コンピュータ上の既定の Web サイトを右クリックし、[プロパティ] をクリックします。
[ホーム ディレクトリ] タブをクリックし、[構成] をクリックします。
マッピングの一覧が表示されます。どのファイル タイプが Aspnet_isapi.dll にマップされているかを確認できます。
IIS 6.0 と Windows Server 2003
従来のプロセス配置に関する常識は、Windows Server 2003 上で稼動する IIS 6.0 の登場により大きく塗り替えられることになります。
- 各アプリケーション プールが 1 つ以上のプロセス インスタンス (w3wp.exe) で維持される複数のアプリケーション プールを構成することが可能になります。これにより、フォールト トレランスと管理性が向上すると共に、個別のアプリケーションを別々のプロセスに分離できるようになります。
- ASP.NET は、IIS 6.0 のカーネル モード HTTP リスナと統合されています。これにより、オペレーティング システムから ASP.NET ワーカー プロセスに直接、要求を渡すことが可能になります。
詳細情報
IIS6 の詳細については、「インターネット インフォメーション サービス (IIS) 6.0 の技術概要」を参照してください。
ASP.NET のパイプライン処理
ASP.NET の認証/認定のしくみは、標準 ASP.NET パイプライン処理の一部として呼び出される HTTP モジュール オブジェクトを使用して実装されています。個々の Web 要求および Web 応答は、図 2 に示すように、オブジェクトのパイプラインを通じて受け渡されます。
図 2 ASP.NET のパイプライン処理
ASP.NET の パイプライン モデルは、HttpApplication オブジェクト、各種 HTTP モジュール オブジェクト、HTTP ハンドラ オブジェクトで構成されます。さらに、これらに関連するファクトリ オブジェクトも含まれますが、便宜上、図 2 では省略されています。HttpRuntime オブジェクトは処理シーケンスの開始時に使用され、HttpContext オブジェクトは要求のライフサイクル全体を通じて、要求と応答の詳細を伝達する目的で使用されます。
HTTP 処理パイプラインに関連する各オブジェクトの機能と動作を以下に示します。
HttpRuntime オブジェクトは、IIS から受信した要求を検証し、HttpApplication オブジェクトの適切なインスタンスに転送して、処理させます。Aspnet_wp.exe 内の各アプリケーション ドメインには、HttpApplication オブジェクトのプールがあります。アプリケーション ドメイン、HttpApplication オブジェクト、および IIS 仮想ディレクトリの間には、1 対 1 のマッピングがあります。つまり、ASP.NET では、IIS 仮想ディレクトリが個別のアプリケーションとして扱われます。
メモ どの Web アプリケーション ドメインにも、HttpRuntime のインスタンスが 1 つずつあります。
HttpApplication オブジェクトは、パイプライン処理を制御します。複数の HTTP 要求が発生すると、要求ごとに HttpApplication オブジェクトが 1 つずつ作成されます。HttpApplication オブジェクトは、パフォーマンス上の理由によりプーリングされるようになっています。
HTTP モジュール オブジェクトは、パイプラインを通じてフローされる HTTP 要求およびHTTP 応答のメッセージを処理するフィルタです。これらのオブジェクトは、要求メッセージと応答メッセージの内容を参照または変更することができます。HTTP モジュールは、IHttpModule を実装したクラスです。
HTTP ハンドラ オブジェクトは、HTTP 要求の終点となり、特定のファイル タイプの要求を処理します。たとえば、*.aspx ファイル用のハンドラや *.asmx ファイル用のハンドラなどがあります。HTTP 応答メッセージは、HTTP ハンドラにより生成されて返されます。HTTP ハンドラは、IHttpHandler を実装したクラスです。
HttpContext オブジェクトは、パイプライン全体を通じて、現在の Web 要求および Web 応答を表すために使用されます。このオブジェクトは、パイプライン内のすべてのモジュールとパイプライン終点のハンドラ オブジェクトから使用できます。HttpContext オブジェクトは、呼び出し元を表す IPrincipal オブジェクトが格納されている User プロパティなど、さまざまなプロパティを公開します。
Web 要求の詳細
ASP.NET ISAPI ライブラリ (Aspnet_isapi.dll) は、IIS プロセス アドレス空間 (Inetinfo.exe) の内側で実行され、ASP.NET ワーカー プロセス (Aspnet_wp.exe) 内の HttpRuntime オブジェクトに要求を転送します。ASP.NET が Web 要求を受信するたびに、以下の処理が行われます。
HttpRuntime オブジェクトが要求を検証して、HttpApplication オブジェクトのインスタンスに転送します。
アプリケーション ドメインごとに少なくとも 1 つの HttpApplication オブジェクト インスタンスがプーリングされており、IIS 仮想ディレクトリごとにアプリケーション ドメインが 1 つあります。特定の仮想ディレクトリ内のファイルが最初に要求されると、新しいアプリケーション ドメインおよび新しい HttpApplication オブジェクト インスタンスが 1 つずつ作成されます。
Machine.config から HTTP モジュールのリスト (<httpModules> 要素で囲まれた部分) が読み込まれます。アプリケーションに応じて、付加的なカスタム HTTP モジュールを Web.config に追加することができます。次のコードは、Machine.config内の既定の <httpModules> 要素を示しています。
<httpModules>
<add name="OutputCache"
type="System.Web.Caching.OutputCacheModule"/>
<add name="Session"
type="System.Web.SessionState.SessionStateModule"/>
<add name="WindowsAuthentication"
type="System.Web.Security.WindowsAuthenticationModule"/>
<add name="FormsAuthentication"
type="System.Web.Security.FormsAuthenticationModule"/>
<add name="PassportAuthentication"
type="System.Web.Security.PassportAuthenticationModule"/>
<add name="UrlAuthorization"
type="System.Web.Security.UrlAuthorizationModule"/>
<add name="FileAuthorization"
type="System.Web.Security.FileAuthorizationModule"/>
</httpModules>
認証モジュールは AuthenticateRequest イベントをフックし、認定モジュールは AuthorizeRequest イベントをフックします。
要求は、パイプライン内のすべてのモジュールを順次通過します。ただし、ロードされる認証モジュールは 1 つだけです。どの認証モジュールがロードされるかは、Web.config 内の <authentication> 要素の構成に依存します。たとえば、次の <authentication> 要素の場合は、WindowsAuthenticationModule がロードされます。
<authentication mode="Windows" />
ロードされた認証モジュールは、IPrincipal オブジェクトを作成して HttpContext.User プロパティに保存する処理を担当します。下位の認定モジュールでは、この IPrincipal オブジェクトを使用して認定の決定を下すので、これは非常に重要な処理です。
IIS 内で匿名アクセスが有効化されており、ASP.NET に対し <authentication mode="None" /> の設定がある場合のように、認証が行われない場合は、そのために用意されている非構成モジュールによって、既定の匿名プリンシパルが HttpContext.User プロパティに格納されます。このため、認証後には、HttpContext.User が常に非 NULL になります。
カスタム認証モジュールを実装する場合は、そのモジュール内のコードで IPrincipal オブジェクトを作成して、HttpContext.User に格納する必要があります。
メモ さらに、ASP.NET は、AuthenticateRequest イベントの発生後に、HttpContext.User に基づいて Thread.CurrentPrincipal を通知します。
HttpApplication は、Global.asax でフック可能な AuthenticateRequest イベントを発生させます。これを利用して、カスタム処理コードを挿入することができます。たとえば、現在のユーザーに関連付けられているロールのセットをロードするコードなどを挿入できます。ただし、この処理は、WindowsAuthenticationModule によって自動的に行われます。認証した Windows ユーザーが所属している Windows グループのセットから、ロールのリストが取得されます。
適切な認証モジュールが処理を完了すると、要求が中止されない限り、認定モジュールが呼び出されます。
UrlAuthorizationModule は、呼び出されると、Machine.config および Web.config に <authorization> タグが含まれているかどうかをチェックし、含まれていれば、HttpContext.User から IPrincipal オブジェクトを取得し、動詞 (GET や POST など) を使用して、ユーザーが要求したリソースに対して指定された種類のアクセスを行う権限をユーザーが持っているかどうかをチェックします。ユーザーにアクセスの権限がなければ、UrlAuthorizationModule は HttpApplication.CompleteRequest を呼び出して、通常のメッセージ処理を中止させます。この場合、UrlAuthorizationModule は HTTP 401 状態コードを返します。
次に、FileAuthorizationModule が呼び出され、HttpContext.User.Identity 内の IIdentity オブジェクトが WindowsIdentity クラスのインスタンスかどうかをチェックします。
IIdentity オブジェクトが WindowsIdentity でなければ、FileAuthorizationModuleはこれ以上処理を行いません。
WindowsIdentity が存在すれば、FileAuthorizationModule は P/Invoke を通じて AccessCheck API を呼び出し、認証済みの呼び出し元 (アクセス トークンが既に IIS から ASP.NET に渡されており、WindowsIdentity オブジェクトによって公開されている呼び出し元) が要求したファイルにアクセスする権限を持っているかどうかをチェックします。ファイルのセキュリティ記述子の DACL に少なくとも読み取り ACE が含まれていれば、要求の処理が続行されます。この条件が満たされていなければ、FileAuthorizationModule は HttpApplication.CompleteRequest を呼び出して、状態コード 401 を返します。
フォーム認証の処理
Web.config に次の要素が含まれていると、FormsAuthenticationModule がアクティブ化されます。
<authentication mode="Forms" />
フォーム認証の場合は、Global.asax 内に Application_Authenticate イベントを実装します。フォーム認証における処理シーケンスは、以下のとおりです。
このコードの中で、IPrincipal オブジェクトを構築して HttpContext.User に格納することができます。このオブジェクトには、カスタム データ ストア (通常は、SQL Server データベースまたは Active Directory) から取得したロールのリストを格納するのが典型的です。IPrincipal オブジェクトは、通常は GenericPrincipal クラスのインスタンスですが、カスタム IPrincipal クラスにすることもできます。
FormsAuthenticationModule は、IPrincipal オブジェクトが既に作成されているかどうかをチェックします。IPrincipal オブジェクトが既に作成されていれば、下位の認定モジュールがそのオブジェクトを使用します。まだ作成されていなければ、FormsAuthenticationModule が GenericPrincipal (ロールなし) を作成し、コンテキストに格納します。
ロール情報が存在しないと、PrincipalPermssion など、ロールのメンバシップを要求する認定チェックがいずれも失敗することになります。
UrlAuthorizationModule は、AuthorizeRequest イベントを処理します。この処理における認定の決定は、HttpContext.User 内の IPrincipal オブジェクトに基づいて行われます。
Windows 認証の処理
Web.config に次の要素が含まれていると、WindowsAuthenticationModule がアクティブ化されます。
<authentication mode="Windows" />
Windows 認証における処理シーケンスは、以下のとおりです。
- WindowsAuthenticationModule は、IIS から ASP.NET に渡された Windows アクセス トークンを使用して WindowsPrincipal オブジェクトを作成します。
- WindowsAuthenticationModule は、Win32 関数を呼び出して、ユーザーの所属している Windows グループのリストを取得します。これらのグループから WindowsPrincipal ロール リストが構築されます。
- WindowsAuthenticationModule は、下位の認定モジュールが WindowsPrincipal オブジェクトを使用できるように、WindowsPrincipal オブジェクトを HttpContext.User 内に格納します。
イベント処理
HttpApplication オブジェクトから発生するイベントは、表 1 に示すとおりです。各 HTTP モジュールには、これらのイベントをフックするイベント ハンドラを実装できます。
表 1 HttpApplication オブジェクトから発生するイベント
イベント | メモ |
---|---|
BeginRequest | 要求処理の開始前に発生 |
AuthenticateRequest | 呼び出し元を認証 |
AuthorizeRequest | アクセス チェックを実行 |
ResolveRequestCache | キャッシュから応答を取得 |
AcquireRequestState | セッション状態をロード |
PreRequestHandlerExecute | 要求をハンドラ オブジェクトに送信する直前に発生 |
PostRequestHandlerExecute | 要求をハンドラ オブジェクトに送信した直後に発生 |
ReleaseRequestState | セッション状態を保存 |
UpdateRequestCache | 応答キャッシュを更新 |
EndRequest | 処理の終了後に発生 |
PreSendRequestHeaders | バッファ内の応答ヘッダーを送信する前に発生 |
PreSendRequestContent | バッファ内の応答本体を送信する前に発生 |
メモ HTTP ハンドラは、PreRequestHandlerExecute イベントが発生してから PostRequestHandlerExecute イベントが発生するまでの間に、実行されます。
最後の 2 つのイベントは、非決定論的なイベントであり、どの時点においても発生する可能性があります (たとえば、Response.Flush の結果として発生するなど)。この 2 つ以外のイベントは、いずれも逐次的に発生するイベントです。
単にこれらのイベントのいずれかをフックするだけの目的で HTTP モジュールを実装する必要はありません。イベント ハンドラは、Global.asax に追加することができるためです。表 1 に示したイベント (個々の HTTP モジュール オブジェクトでフックできるイベント) のほかに、ASP 開発者には馴染みの深い Application_OnStart イベントと Application_OnEnd イベントも HttpApplication オブジェクトから発生します。これらのイベントは、Global.asax 内でのみ処理できます。さらに、個々の HTTP モジュール オブジェクトから発生するイベントを処理するカスタム イベント ハンドラを Global.asax 内に実行することもできます。たとえば、セッション状態モジュールの場合なら、Session_OnStart イベントと Session_OnEnd イベントが発生します。
カスタム HTTP モジュールを実装する
■ 独自の HTTP モジュールを作成して ASP.NET 処理パイプラインに挿入するには
- IHttpModule を実装するクラスを作成します。
- モジュールを格納したアセンブリをアプリケーションの \bin サブディレクトリに置くか、グローバル アセンブリ キャッシュにインストールします。
- 次のように、アプリケーションの web.config に <HttpModules> 要素を追加します。
<system.web>
<httpModules>
<add name="modulename"
type="namespace.classname,assemblyname" />
</httpModules>
</system.web>
カスタム HTTP ハンドラを実装する
カスタム HTTP ハンドラを必要に応じて実装することができます。ここでは、.data ファイル拡張子を持つファイルを処理する場合を想定して、ハンドラの実装手順を示します。
■ カスタム HTTP ハンドラを実装するには
IIS メタベースにマッピングを追加して、.data ファイル拡張子を ASP.NET ISAPI 拡張機能 (Aspnet_isapi.dll) にマップします。
IIS MMC スナップインでアプリケーションの仮想ディレクトリを右クリックし、[プロパティ]をクリックし、[仮想ディレクトリ] タブの [構成] ボタンをクリックします。[追加] をクリックして、.data ファイルを C:\Winnt\Microsoft.NET\Framework\v1.0.3705\aspnet_isapi.dll にマップします。
メモ マッピングの追加時に [ファイルの存在を確認する] チェック ボックスをオンにした場合は、ファイルが物理的に存在していなければなりません。物理ファイルにマップされていないパスを仮想化する場合を除き、通常は、このチェック ボックスをオンにするのが妥当です。.rem または .soap で終る仮想化パスは、.NET リモート処理に使用されます。
IHttpHandler を実装するクラスを作成します。また、要求を非同期に処理したい場合は、IHttpAsyncHandler も実装します。
ハンドラを格納したアセンブリをアプリケーションの \bin サブディレクトリに置くか、グローバル アセンブリ キャッシュにインストールします。
アプリケーションの Web.config ファイルに <httpHandlers> セクションを挿入して、ハンドラを処理パイプラインに追加します。
<system.web>
<httpHandlers>
<add verb="*" path="*.data" type="namespace.classname, assemblyname" />
</httpHandlers>
</system.web>