はじめに
WebフォームやWebサービスの開発では、よく共有リソースへのアクセスが問題となります。多くのASP.NET開発者にとって、これはなかなかの難問です。Microsoft .NETでは、ユーザー認証とアクセス許可への新しいアプローチが採用されていて、プログラミング参考書や専門雑誌では一般に「セキュリティ」分野の事柄として扱われています。セキュリティ関連の論文や書籍では、フォーム認証の説明に大きなスペースが割かれていますが、最善の解決策を得るという観点からは、むしろASP.NETセキュリティのアーキテクチャを、IIS、ネットワーク、オペレーティングシステムのセキュリティとの関連で理解することが重要です。
図1に示すのは、IISとASP.NETで提供される基本セキュリティです(図はMSDNのWebサイトより引用)。ASPセキュリティとは根本的に異なっています。ユーザーがWeb要求を出すと、次の一連のイベントが発生します。
- ユーザーがHTTP要求を出します。
- 出されたWeb要求が、SYSTEMアカウントの下で動作しているIISに拾われます。SYSTEMアカウントは非常に強力なアカウントで、あらゆる種類の特権を持っています。IISは、選択された認証タイプに基づいて認証を行い、認証されたユーザーごとにアクセストークンを作成します。たとえば、匿名認証が選択されていれば、匿名ユーザーのアクセストークンを作成します(既定ではIUSR_MACHINE)。また、設定されているアクセス許可タイプに基づいてアクセス許可も実行します。たとえば、特定IPアドレスからしか要求を受け付けないようにIISを設定することができます。
- 認証されたユーザーの要求とWindowsアクセストークンが、IISから「aspnet_isepi.dll」に引き渡されます。「aspnet_isepi.dll」は.aspx URLを扱うISAPI拡張機能を備えたIISモジュールで、IISとASP.NETランタイム環境をつなぐブリッジとして働きます。要求は、名前付きパイプ経由でワーカープロセスに送られます。
- ワーカープロセスは「aspnet_wp.exe」であり、ここにCLRが存在します。このワーカープロセスは、既定ではASPNETアカウントの下で動作します。ASPNETアカウントは、ASP.NETフレームワークのインストール時に作成されるローカルアカウントで、SYSTEMアカウントがきわめて強力であるのと異なり、少数の特権しか持っていません。ASP.NETは、独自の認証設定に基づいてユーザー認証を行います。たとえば、設定がWindows認証になっていれば、IISからのトークンをすべて受け入れます(追加の認証はありません)。また、要求されたリソースやファイルごとにアクセス許可を判断できます。たとえば、FileAuthorizationModelを使用すれば、要求を出したユーザーに当該リソースへのアクセス権限があるかどうかを検査できます。Windows認証では、ユーザーのアクセストークンがACLと比較されます。
- ここまで順調に進むと、アプリケーションが特定IDの下でリソースにアクセスします。既定では、ASPNETプロセスアカウントの与えるIDが使用されますが、偽装が有効になっているときは、当該ユーザーの本来のIDを使用するほかに、別のIDの下でアプリケーションを実行するという偽装設定もできます。
セキュリティがクリアされれば、いよいよデータにアクセスできます。しかし、いまネットワークリソースに対してNTFSアクセス許可が設定されているとすると、先へ進む前に必ずやっておかなければならない作業が2つあります。1つは、ASP.NETを用いてUNC(汎用命名規則)シェア上のファイルとフォルダにアクセスする(つまり、ネットワーク上の共有ファイル/フォルダにアクセスする)タスクを指定することです。もう1つは、リソースアクセスに使用するIDを選択することです。まず2番目の作業を完了しておかないと、最初の作業も実行できません。
ネットワークリソースにアクセスする方法はいくつかあります。
- ASP.NETプロセスIDを使用する
- 匿名ユーザーアカウントを使用する
- LogonUser APIを使用する
- サービスコンポーネント(Enterprise Services)を使用する
ASP.NETプロセスIDを使用する方法には明らかな欠点があります。というのは、この方法で与えられるIDは、既定では、アプリケーションがリソースへのアクセスを試みたときのID(ASPNET)となるからです。簡単な解決方法として、ローカルアカウントを作成し、そのユーザー名とパスワードをリモートコンピュータ上のそれに一致させるやり方がありますが、ほとんどの企業は大規模なイントラネット環境を取り入れていますから、実際的な方法とは言えません。また、誰がどのリソースにアクセスするのかを知っておくことも重要でしょう。この方法でも、ネットワークリソースへのアクセスを実現することはできますが、実に非効率的です。
2番目の方法では、匿名ユーザーアカウント(たとえばIUSR_MACHINE)を使用します。しかし、最初の方法と同じ理由から、この方法も非効率的であるのは明らかでしょう。
3番目は、LogonUser APIを使用する方法です。ここではWin32 LogonUser APIを呼んで、特定のIDを偽装します。また、ASP.NETプロジェクトの「web.config」ファイルで<identity>
エレメントを設定しておく偽装方法もあります。ただ、MSDNの記事によると、「これらのやり方は推奨できません。この方法だと、必然的に、<オペレーティングシステムの一部として動作する>権限をASP.NETプロセスアカウントに与えることになってしまい、Webアプリケーションのセキュリティが大幅に低下します」。したがって、理想的なアプローチとはとうてい言えません。
最後の(そして、選択可能な唯一の)オプションは、あるサービスコンポーネントを固定IDとして動作するよう設定し、ネットワークリソースにアクセスさせるという方法です。一見たいへんそうなやり方ですが、断然すぐれた方法でもあります。この方法のアーキテクチャを次の図に示しておきます。
J. D. Meierらが発表したセキュリティ論文によると、Enterprise Servicesサーバーアプリケーションでプロセス外サービスコンポーネントを使う方法には、次の利点があります。
- 使用するIDを柔軟に設定でき、ASP.NETのIDに依存せずに済みます。
- トラステッド(高度の特権を持つ)コードをメインのWebアプリケーションから分離できます。
- プロセスホップが1つ増え、飛び越すべきセキュリティバーが高くなります。特権が高度になることで、攻撃者にとっては、プロセス境界を越えてプロセスに入り込むことがずっと難しくなります。
- LogonUser APIコールを使い、手作業で偽装する必要があるときも、メインのWebアプリケーションから切り離されたプロセスでそれを実行できます。
サービスコンポーネントの作成
COM+からサービスを受けるアセンブリをサービスコンポーネントと呼びます。サービスコンポーネントの作成には、COM+技術に精通し、十分な経験を積んでいることが要求されます。COM+アプリケーションはユーザーインターフェイスを含んでおらず、その点で伝統的な意味でのアプリケーションとは異なります。むしろ、アプリケーションを構成しているコンポーネント群のコンテナであり、COMと.NETアセンブリDLLがそのアプリケーションのビジネスロジック部分を受け持ちます。COM+は、COMの新版でも、COMとDCOMを組み合わせたものでもなく、MTS(Microsoft Transaction Services)を継承する技術です。COM+サービスを使用できるのは.NETコンポーネントであり、それをEnterprise Servicesと呼んでいます。これの実装は中間層.NETコンポーネントと同様ですが、いくつか新しい点があります。
サービスコンポーネントの作成手順を以下に示します。
- 新しいクラスライブラリプロジェクトを作成します。目的は、Webアプリケーションの初期クラスライブラリとなる中間層コンポーネントを構築することです。
- 適切なクラス、メソッド、プロパティを追加します。ファイルとフォルダにアクセスするので、System.IO名前空間のインポートが必要です。
- Webフォームアプリケーションを作成します。
- 強い名前を持つアセンブリを作成します。
- Visual Studio .NETコマンドプロンプトを実行します([スタート]→[プログラム]→[Microsoft Visual Studio .NET]→[Visual Studio .NET Tools]→[Visual Studio .NET Command Prompt]を選択)。
- プロジェクトディレクトリへ移動し、「sn -k KeyPair.snk」というコマンドを入力します。
- これにより公開鍵/秘密鍵のペアが作成されます。これらの鍵は、Visual Studio .NET IDEでコンポーネントに強い名前を与えるために使用されます。このとき、プロジェクトディレクトリに「KeyPair.snk」ファイルが作成されることに注意してください。
- 「AssemblyInfo.vb」ファイルのコードウィンドウを開き、
<Assembly: AssemblyKeyFile("KeyPair.snk")>
というアセンブリ属性を追加します。 - プロジェクトをコンパイルすると、強い名前を持つアセンブリが作成されます。
- オブジェクトをGAC(Global Assembly Cache)に追加します。
- .NET Framework Configuration Toolを開きます([スタート]→[プログラム]→[Administrative Tools]→[Microsoft .NET Framework Configuration]を選択)。
- [Assembly Cache]→[View List of Assemblies in the Assembly Cache]を選択して、GACにあるすべてのアセンブリを表示します。
- [Assembly Cache]アイコンを右クリックし、[追加]を選択します。
- プロジェクトの「bin」ディレクトリにある「AccessingSharedResources.dll」ファイルを探し、それをダブルクリックします。
<authentication mode="Window" /> <identity impersonate="true" />
Dim objEnterprise As New AccessingSharedResources.dal_AccessNetwork()
- 「System.EnterpiseServices.dll」への参照を追加します。
- 次のように記述し、必要なEnterprise Services名前空間をインポートします。
Imports System.EnterpriseServices
Imports System.Runtime.CompilerServices
Imports System.Reflection
- 各クラスが
ServicedComponent
クラスを継承するようにします。 - サービスコンポーネント関連のアセンブリ属性を、サービスコンポーネントをサポートする「AssemblyInfo.vb」ファイルに追加します。
- System.EnterpriseServices名前空間をインポートします。
- 次を追加します。
- AssemblyVersionを設定します。
- 新しいCOM+アプリケーションを作成し、そこにアセンブリを登録します。
- Visual Studio .NETコマンドプロンプトを実行します(ステップ4を参照)。
- dllのある場所(「bin」ディレクトリの下です)に移動します。
- 「regsvcs /c AccessingSharedResources.dll」と入力して、[Enter]キーを押します。
- Webアプリケーションを手直しします。
- 「System.EnterrpiseServices.dll」への参照を追加します。
Imports System.EnterpriseServices
文をWebクライアントに追加します。
Public Class dal_AccessNetwork Inherits ServicedComponent
'COM+ Application Name < Assembly: ApplicationName("AccessingSharedResources")> 'COM+ Activation Type (Server which is out-of-process) <Assembly: ApplicationActivation(ActivationOption.Server)>
<Assembly: AssemblyVersion("1.0.*")>
となります。MSDNの記事によると、プロジェクトを再構築すると、そのたびに新しいアセンブリバージョンが生成されますし、サービスコンポーネントクラスを指し示すためのクラスID(CLSID)も新しくなります。したがって、「Regsvcs.exe」を用いてアセンブリをコンポーネントサービスに繰り返し登録すると、CLSIDだけが異なる重複コンポーネント(クラスのみ)が「Components」フォルダにいくつも現れます。AssemblyVersion
属性を用いて、明示的なバージョン設定を行うことを考えてもよいでしょう。たとえば、
<Assembly: AssemblyVersion("1.0.0.1")>
これでEnterprise Servicesコンポーネント(別名サービスコンポーネント)が完成しました。このコンポーネントをCOM+アプリケーションとして表示します。
- Component Services Managerを実行します([スタート]→[プログラム]→[Administrative Tools]→[Component Services]を選択)。
- [Console Root]→[Component Services]→[Computers]→[My Computer]→[COM+ Applications]→[AccessingSharedResources]→[Components]と展開します。
メリットとデメリット
サービスコンポーネントを使用することのメリットは次のとおりです。
- IDを柔軟に使用でき、ASP.NETのIDに依存せずに済みます。
- トラステッド(高度の特権を持つ)コードをメインのWebアプリケーションから分離できます。
- プロセスホップが1つ増え、飛び越すべきセキュリティバーが高くなります。特権が高度になることで、攻撃者にとっては、プロセス境界を越えてプロセスに入り込むことがずっと難しくなります。
- LogonUser APIコールを使い、手作業で偽装する必要があるときも、メインのWebアプリケーションから切り離されたプロセスでそれを実行できます。
デメリットは次のとおりです。
- サービスコンポーネントの呼び出しは、ネイティブの.NETオブジェクトを呼ぶときほど速くなく、アプリケーションのパフォーマンスに悪影響があります。
- ステップ数とコード量が増えます。
- ネイティブの.NETコンポーネントよりも管理が困難です。
- COM+アプリケーションにDLLをインストールする必要があります。
まとめ
COM+の基本機能とも言うべきオブジェクトプーリング、トランザクションサポート、同期化、JITA(Just-In-Time Activation)、イベント追跡などを使用せずに、目標を達成できました。COM+の諸機能はいずれも高度であり、現実に使用するには深い理解が必要です。しかし、ネットワーク上の共有リソースにアクセスするだけならば、ネイティブの.NETクラスをCOM+アプリケーションにインストールし、COM+固有のコードを数行追加するだけで済みます。
参考資料
- MSDNライブラリ 『洗練されたWebフォームコントロールを.NET Frameworkで簡単に開発するには』 David S. Platt 著
- MSDNライブラリ 『ASP.NETとIISで安全なサイトを構築し、配備するには(初歩的な手引き、その1)』 Jeff Prosise 著
- MSDNライブラリ 『ASP.NETとIISで安全なサイトを構築し、配備するには(初歩的な手引き、その2)』 Jeff Prosise 著
- MSDNライブラリ 『安全なASP.NETアプリケーションを作成するには(認証、アクセス許可、安全な通信)- ASP.NET Security』 J.D. Meier・Alex Mackman・Michael Dunner・Srinath Vasireddy 著
- MSDNライブラリ 『安全なASP.NETアプリケーションを作成するには(認証、アクセス許可、安全な通信)- Enterprise Services Security』 J.D. Meier・Alex Mackman・Michael Dunner・Srinath Vasireddy 著
- 15seconds 『サービスコンポーネントの配備』 Thiru Thangarathinam 著