解説
インサイド .NET Framework
第14回 ロールベース・セキュリティ
インフォテリア株式会社
吉松 史彰
2003/03/13
|
 |
Webアプリケーションとロールベース・セキュリティ
Windows上で動作するWindowsアプリケーションや、コマンド・プロンプトで動作するコンソール・アプリケーションの場合、ロールベース・セキュリティにおいて利用するのはWindowsのユーザーであることが多いだろう。その場合は、先述した例のようにAppDomainクラスのSetPrincipalPolicyメソッドを実行するだけで、.NET Frameworkが自動的にWindowsPrincipalオブジェクトを作成してくれる。だがWebアプリケーションの場合は、必ずしも認証方式がWindowsであるとは限らない。事実、ASP.NETには3種類の認証方式(Forms、Windows、Passport)が用意されている。これらの方式で認証された場合、ロールベース・セキュリティはどうなるのだろうか。
ASP.NETでは、3種類の認証方式のどれかを使って認証が行われた場合、それぞれに対応するプリンシパル・オブジェクトが作成される。例えばASP.NETのweb.config構成ファイルに次の記述を行うと、Windows認証が行われ、認証にパスすればWindowsPrincipalクラスのインスタンスが作成される。そのオブジェクトのIdentityプロパティに設定されているのは、WindowsIdentityクラスのインスタンスになる。このオブジェクトはIISが認証したWindowsユーザーを表す。
<authentication mode="Windows"/>
<authorization>
<deny users="?"/>
</authorization>
|
|
ASP.NETでWindows認証を行うためのweb.config構成ファイルの設定 |
フォーム認証を利用するために<authentication>要素のmode属性をFormsに設定して、ログイン・ページの作成などの適切な作業を行い、正しくログインした場合は、GenericPrincipalクラスのインスタンスが作成される。そのオブジェクトのIdentityプロパティに設定されているのは、FormsIdentityクラスのインスタンスになる。このオブジェクトは、フォーム認証のコードで認証されたユーザーを表す。
これらのオブジェクトを作成するのは、それぞれの認証方式に対応するHTTPモジュールに格納されているクラスの役割だ。%SYSTEMROOT%\Microsoft.NET\Framework\vx.x.x\CONFIGフォルダ(通常はC:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\CONFIG)に格納されているmachine.configファイルを開くと、中には次のようなエントリがある。
<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>
|
|
machine.configに記述されている認証のためのモジュール(抜粋) |
つまり、Windows認証が行われるときはSystem.Web.Security.WindowsAuthenticationModuleクラスがそれを担当し、フォーム認証が行われるときはSystem.Web.Security.FormsAuthenticationModuleが担当する。これらのクラスがプリンシパル・オブジェクトを作成する。
作成されたプリンシパル・オブジェクトは、Webアプリケーションでは2つの場所に格納される。1つはSystem.Web.HttpContextクラスのUserプロパティだ。もう1カ所は、前述のSystem.Threading.Thread.CurrentPrincipalプロパティだ。前者に対する設定は、WindowsAuthenticationModuleやFormsAuthenticationModuleが行い、後者の設定はSystem.Web.Security.DefaultAuthenticationModuleクラスが行っている。この2カ所から参照されているオブジェクトは同一だ。つまり、次のコードはtrueを返す。
bool IsTheSame = System.Object.ReferenceEquals(
Context.User, Thread.CurrentPrincipal);
なぜ同じオブジェクトを2カ所から参照しているかというと、それは用途が違うからだ。ASP.NETの承認機能、つまりweb.configファイルの<authorization>要素の記述(System.Web.Security.UrlAuthorizationModuleが処理する)と、ファイルのNTFSアクセス権(System.Web.Security.FileAuthorizationModuleが処理する)によるアクセス制御のときに使われるのは、HttpContextクラスのUserプロパティである。一方、すでに説明したPrincipalPermissionクラスでのチェックに使われるのが、ThreadクラスのCurrentPrincipalプロパティである。例えばweb.configに次のような記述があるとする。
<location path="test.aspx">
<system.web>
<authorization>
<allow users="User1" />
<deny users="*" />
</authorization>
</system.web>
</location>
|
|
web.configファイルによるアクセス制御 |
このアクセス制御はUrlAuthorizationModuleによって行われるので、HttpContextクラスのUserプロパティの内容が重要になる。web.configにこれを記述しなくても、test.aspxが次のように記述されていれば、同じ結果になる。
<%@Page Language="C#"%>
<%@Import Namespace="System.Security.Permissions"%>
<%
PrincipalPermission perm
= new PrincipalPermission("User1", null);
perm.Demand();
%>
|
|
PrincipalPermissionクラスによるアクセス制御 |
どちらも、User1という名前のユーザー以外はtest.aspxにアクセスできなくなる。もっとも、web.configに記述した場合はログイン・ページが表示されるのだが、aspxファイルに記述した方はセキュリティ例外になるので、ユーザーに対する見た目を考慮しなければならない。
独自のロール定義
ASP.NETでWindows認証を行っているときは、ロールはすなわちWindowsのグループなのだが、フォーム認証を行っているときは、ロールはどうなるのだろうか。実は、デフォルトではロールは作成されない。誰をどのロールに属させるかは、開発者が書くコードに一任されている。あるロールにどのアイデンティティが属しているかを決定する仕組みは、それぞれの認証モジュール(XXXAuthenticationModule)クラスのIsInRoleメソッドの実装内容に依存する。
例えば、WindowsPrincipalクラスのIsInRoleメソッドでは、恐らくWindowsのSAM(Security Account Manager)データベースにアクセスして、Identityプロパティで表されているWindowsユーザーが指定されたWindowsグループに属しているかどうかを調べているだろう。何らかのデータベースでユーザーとロールとの関連を管理しているシステムであれば、GenericPrincipalクラスを作成するときにデータベースにアクセスして、ユーザーが属するグループをすべて取得し、文字列の配列にしてGenericPrincipalクラスのコンストラクタに渡せばよい。あとはIsInRoleメソッドが、その配列に入っている文字列に対してはtrue、入っていなければfalseを返す。例えば次のようなコードで、ManagersとSalesの2つのロールに属するUserを定義できる。
HttpContext.Current.User = new GenericPrincipal(
principal.Identity, new string[]{"Managers", "Sales"});
次の疑問は、このコードはどこに記述すればいいのか、どこで実行すればいいのかということだろう。ASP.NETでは、HttpApplicationクラスにAuthenticateRequestイベントがある。ユーザーのアクセスが認証を必要とする場合、このイベントが発生する。そこで、global.asaxファイルに次の記述を行えばよいことになる。Thread.CurrentPrincipalにも設定していることに注意してほしい。
<%@Application Language="C#" %>
<%@Import Namespace="System.Security.Principal"%>
<%@Import Namespace="System.Threading"%>
<script runat="server">
protected void
Application_AuthenticateRequest(object o, EventArgs e) {
IPrincipal principal = HttpContext.Current.User;
if ((principal != null) && (principal.Identity.Name == "User1")) {
HttpContext.Current.User = new GenericPrincipal(
principal.Identity, new string[]{"Managers", "Sales"});
Thread.CurrentPrincipal = HttpContext.Current.User;
}
}
</script>
|
|
独自のロールを定義する場合のglobal.asaxの記述例 |
ASP.NETでは、ユーザーのアクセスが認証を必要とするときに、AuthenticateRequestイベントが発生する。フォーム認証を利用する場合、このタイミングで独自のロールを定義することができる。 |
こうしておけば、あとはweb.configで次のように記述して「ロールベース」セキュリティを実現できる。
<location path="test.aspx">
<system.web>
<authorization>
<allow roles="Managers" />
<deny users="*" />
</authorization>
</system.web>
</location>
|
|
独自ロールを利用したアクセス制御を行うためのweb.configファイル記述例 |