クレームベースのアプリケーション
WIF によるクレームベースの承認
Michele Leroux Leroux
この数年で、フェデレーション セキュリティ モデルとクレームベースのアクセス制御がますます普及してきました。フェデレーション セキュリティ モデルでは、セキュリティ トークン サービス (STS) によって認証を実行できます。STS では、セキュリティ トークンにクレームを添えて発行することができるため、このクレームによって申告される認証済みのユーザーの ID とアクセス権が行使されます。フェデレーションにより、ユーザーは自身が参加するドメインで認証されると同時に、信頼関係が確立されている別のドメインに属するアプリケーションやサービスへのアクセスも許可されます。そのため、1 人のユーザーが複数のアカウントを準備して管理する必要がなくなり、シングル サインオン (SSO) シナリオが可能になります。クレームベースのアクセスは、フェデレーション セキュリティ モデルの中心です。このモデルにより、アプリケーションやサービスでは、信頼関係のあるドメインの発行者 (STS) からの申告 (クレーム) に基づいてさまざまな機能へのアクセスが承認されます。クレームには、ユーザー、ロール、またはアクセス許可に関する情報を含めることができるため、非常に柔軟な承認モデルを構築できます。フェデレーション セキュリティとクレームベースのアクセスを組み合わせれば、さらに幅広いエコシステムの複数のアプリケーション、部門、またはパートナーにまたがる多様な統合シナリオを実現できます。
この分野のプラットフォーム ツールも飛躍的に進化しました。Windows Identity Foundation (WIF) は、クレームベースのアプリケーションやサービスの構築と、アクティブおよびパッシブなフェデレーション セキュリティ シナリオのサポートを目的に設計され、豊富な機能を備えた ID モデル フレームワークです。WIF を使用すると、すべての ASP.NET アプリケーションでパッシブ フェデレーションを実現でき、クレームベースの承認モデルを ASP.NET アプリケーションや WCF サービスに簡単に統合できます。さらに、WIF には、STS のカスタム実装を構築するためのプラミングが用意され、マネージ情報カードや ID セレクター (Windows CardSpace など) を必要とする認証シナリオをサポートする機能やコントロールが含まれています。
WIF を利用すると、フェデレーションやクレームベースのセキュリティを必要とする機能豊富なアプリケーション シナリオの実装に必要なコードが大幅に少なくなります。この 2 部構成の記事では、ASP.NET アプリケーションでパッシブ フェデレーションを実現し、WCF と ASP.NET の両方でクレームベースのセキュリティ モデルをサポートするための、フレームワークのコア機能に重点を置いて説明します。今回は WCF に注目し、次回は ASP.NET に重点を置きます。
フェデレーションとクレームベースのセキュリティが必要な理由
フェデレーションとクレームベースのセキュリティのメリットは、次のようないくつかの目標に照らして考えることができます。
- アプリケーションやサービスから認証メカニズムを分離する
- 承認をさらに柔軟かつきめ細かくするためにロールをクレームに置き換える
- ユーザー アカウントの準備と解除に関わる IT 負担を軽減する
- 外部のフェデレーション パートナーなどの、信頼関係のあるドメインにあるアプリケーションのさまざまな機能へのアクセスを許可する
こうした目標を 1 つでもお持ちなら、フェデレーション セキュリティをすぐに、または最終的に実現できるクレームベースのモデルを採用すれば驚くほど役立ちます。
アプリケーションやサービスを設計するときに、設計作業の一環として必ず認証と承認のモデルを検討します。たとえば、イントラネット アプリケーションであれば、通常、ユーザーが各自の Windows 資格情報を使って特定のドメインで認証されることを想定します。それに対して、インターネット アプリケーションであれば、通常、Microsoft SQL Server などのカスタム資格情報ストアを使用します。また、アプリケーションでは、証明書認証やスマート カード認証を要求したり、複数種類の資格情報をサポートしたりして、さまざまなユーザー グループがそれぞれ適切な種類の認証を使用できるようにすることもできます。アプリケーションがユーザーを認証するときに、(常に) 1 種類の資格情報しか使用しないと想定するならば設計は簡単です。しかし、多くの場合、代替の認証モードやさまざまなユーザーに対応する認証モードを追加サポートする必要が生じ、アプリケーションがサポートする資格情報の種類が増えていきます。
たとえば、アプリケーションで、ドメインのファイアウォールの内側にいる社内ユーザーをサポートすると同時に、インターネット経由でアクセスしてくる社外ユーザーもサポートすることが考えられます。クレームベースのモデルを採用している場合など、アプリケーションのセキュリティ モデルと認証モードが分かれていると、新しい認証モードを導入する必要が生じた場合、アプリケーションへの影響はあるとしてもごくわずかです。
同様に、固定的なロールのセットに承認が結び付けられていなければ、アプリケーションの柔軟性は高まります。アプリケーションでアクセスを承認する際に常ロールの特定のセットを使用し、これらのロールが常に同じ意味合いのアクセス権でさまざまなで機能を実行するのであれば、やはり設計は簡単です。しかし、一般に、アプリケーションを使用する部門によってロールの意味は異なるため、カスタマイズが必要になります。そのため、ユーザーが所属するドメインによってロールの評価を変えたり、アクセス権を制御するためにカスタム ロールを作成できるようにしたりします。WIF を使用するとクレームベースのセキュリティ モデルの採用が容易になるため、必要に応じてロールを承認メカニズムから分離できます。その結果、論理的なロールをもっときめ細かいクレームのセットに対応付け、アプリケーションではそのクレームに基づいてアクセスを承認できます。ロールを変更したり、新しいロールを追加したりしたために、クレームのさまざまなセットを発行する必要が生じても、アプリケーションは影響を受けません。
もちろん、クレームにはロールやアクセス許可を上回るメリットがあります。クレームベースのモデルを採用すると新たに得られるメリットの 1 つは、電子メール アドレス、フル ネーム、生年月日など、認証対象のユーザーについての情報をクレームに含めることができる点です。クレームを使えば、たとえば、ユーザーの実年齢や生年月日 (多くのユーザーが公開を望まない情報) を共有しなくても、情報を確認できます。クレームでは、ユーザーが操作を実行できる最低年齢に達しているかどうかを示したり (IsOver21 または IsOver13 を示すブール値のクレーム)、ユーザーが所属する部門の一覧をすべて共有しないで、ユーザーが特定の部門に所属しているかどうかを確認したりすることができます。
認証メカニズムと特定のロールをアプリケーションやサービスから分離すれば、変更への対応が容易になります。しかし、それだけではなく、クレームベースのモデルはフェデレーション セキュリティ シナリオでも中心的な役割を果たし、ユーザーが所属するドメインと信頼関係のあるすべてのドメインでのアクセス許可をこれまでよりもはるかに簡単に管理することができます。フェデレーションによって、IT のオーバーヘッドが抑えられ、ID 管理に関連するリスクが低減されます。複数のアプリケーションやドメインでそれぞれ資格情報を管理する必要がなくなるため、ドメインごとのアカウントの準備や解除に伴うリスク (複数の場所にあるアカウントを削除し忘れるような事態) が低減されます。アカウントを複数コピーして管理する必要がなくなるため、パスワードの同期も必要なくなります。フェデレーションでは、ユーザーがあるアプリケーションにログインしたら、認証手続きを繰り返すことなく別のアプリケーションに (場合によってはセキュリティ ドメインをまたいで) アクセスできることから、SSO シナリオが容易になります。最後に、Active Directory フェデレーション サービス (AD FS)、WIF などのフェデレーション セキュリティ プラットフォームを使用すると、ドメイン間への新しい信頼関係の追加も容易になります。したがって、アプリケーションを社内の別のドメインに展開でき、さらには外部パートナーのドメインにも円滑に拡張できるようになります。
WIF によるアクティブ フェデレーション
アクティブ フェデレーション シナリオは、WS-Federation のアクティブな要求側プロファイル (「OASIS Web Services Federation (WSFED) TC」(oasis-open.org/committees/tc_home.php?wg_abbrev=wsfed、英語) 参照) と、WS-Trust 仕様 (「WS-Trust 1.3」(docs.oasis-open.org/ws-sx/ws-trust/200512/ws-trust-1.3-os.html、英語) 参照) に基づいています。大まかに述べると、WS-Trust では 4 種類のサービス操作 (Issue、Validate、Renew、および Cancel) を備えたコントラクトが規定されています。これらのサービス操作はそれぞれ、セキュリティ トークンの要求、セキュリティ トークンの検証、有効期限の切れたセキュリティ トークンの更新、および使用しなくなったセキュリティ トークンの取り消しのために、クライアントから呼び出されます。各操作は、WS-Trust 仕様に準拠する RST (Request for Security Token) 形式のメッセージを処理し、RSTR (RST Response) 形式で応答を返信します。これらの WS-Trust 機能は、すべてのフェデレーション セキュリティ シナリオの重要な構成要素である STS (トークン発行者) によって実装されます。
簡単なアクティブ フェデレーション シナリオを図 1 に示します。このシナリオには、Windows クライアント アプリケーション (要求者)、WCF サービス (RP: 証明書利用者)、および RP ドメインに属する STS (RP-STS) が関与します。図からわかるように、クライアントは WCF プロキシを使用して、最初の認証を RP-STS と調整し、セキュリティ トークンを要求します。次に、発行されたセキュリティ トークンを要求に添えて RP を呼び出します。
図 1 簡単なアクティブ フェデレーション シナリオ
このシナリオでは、RP-STS は、RP ドメインで認証されるユーザーにとっては ID プロバイダー (IdP) でもあります。つまり、RP-STS は、ユーザーの認証、認証したユーザーの ID のアサート、および RP での承認に関連するクレームの発行を行います。RP は、セキュリティ トークンが RP-STS から発行されていることを確認し、発行されたクレームに基づいてアクセスを承認します。
ここでは、このシナリオの実装を説明しやすいように、Todo List アプリケーションを作成しています。付属のコード サンプルには、WPF クライアント、WCF サービス、および WIF を使用して実装されたアクティブ STS が含まれています。さらに詳細なコンテキストを提供するために、TodoListService WCF サービスは、ITodoListService コントラクトを実装します (図 2 参照)。クライアントは、WCF プロキシを使用してこのサービスを呼び出し、すべての Todo 項目の取得、および項目の追加、更新、または削除を実行します。TodoListService は、作成、読み取り、更新、および削除のクレームを信頼して、これらの操作へのアクセスを承認します。
図 2 ITodoListService の定義
[ServiceContract(Namespace="urn:TodoListApp/2009/06")]
public interface ITodoListService
{
[OperationContract]
List<TodoItem> GetItems();
[OperationContract]
string CreateItem(TodoItem item);
[OperationContract]
void UpdateItem(TodoItem item);
[OperationContract]
void DeleteItem(string id);
}
このアクティブ フェデレーション シナリオを実装するには、次の 4 つの手順を実行する必要があります。
- TodoListService のフェデレーション セキュリティ WCF エンドポイントを公開する
- クライアント アプリケーションの WCF プロキシを生成し、RP-STS で認証するための資格情報を指定してプロキシを初期化する
- TodoListService で WIF を有効にし、クレームベースの承認を可能にする
- アクセス許可の要求 (IsInRole) やその他の承認チェックを行い、サービス操作などの機能へのアクセスを制御する
これ以降は、これらの手順について説明します。
フェデレーション エンドポイントの公開
クレームベースの WCF サービスでは、通常、発行されたトークン (SAML 標準に基づくトークンなど) を受信するフェデレーション エンドポイントを公開します。WCF には、WS-Trust を使用してフェデレーション セキュリティ シナリオをサポートする 2 つのバインディングが用意されています。WSFederationHttpBinding は、WS-Trust 2005 (以前のバージョンのプロトコル) に基づくオリジナルの標準バインディングです。WS2007FederationHttpBinding は、最新バージョンのバインディングで (Microsoft .NET Framework 3.5 と共にリリース)、認定済み標準の WS-Trust 1.3 をサポートします。通常、以前のバージョンとの相互運用が必要でない限り、WS2007FederationHttpBinding を使用してください。AD FS 2.0 または WIF に基づく STS では、どちらのバージョンの WS-Trust もサポートできます。
サービスのフェデレーション エンドポイントを公開する際は、通常、想定しているセキュリティ トークン形式、必須またはオプションのクレームの種類、および信頼されたトークンの発行者についての情報を指定します。図 3 に、TodoListService の system.serviceModel のコードを示します。ここでは、1 つのフェデレーション エンドポイントを WS2007FederationHttpBinding 経由で公開します。
図 3 TodoListService によって公開されるフェデレーション エンドポイント
<system.serviceModel>
<services>
<service name="TodoList.TodoListService"
behaviorConfiguration="serviceBehavior">
<endpoint address="" binding="ws2007FederationHttpBinding" bindingConfiguration="wsFed" contract="Contracts.ITodoListService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/TodoListService"/>
</baseAddresses>
</host>
</service>
</services>
<bindings>
<ws2007FederationHttpBinding>
<binding name="wsFed">
<security mode="Message" issuedTokenType=
“http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-.1#SAMLV1.1" issuedKeyType="SymmetricKey" negotiateServiceCredential="true">
<message>
<claimTypeRequirements>
<add claimType=
“https://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" isOptional="false"/>
<add claimType= "urn:TodoListApp/2009/06/claims/permission"
isOptional="false"/>
</claimTypeRequirements>
<issuerMetadata address="http://localhost:8010/rpsts/mex" />
</message>
</security>
</binding>
</ws2007FederationHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
フェデレーション セキュリティ シナリオでは SAML トークンを使用するのが一般的ですが、これは厳密な要件ではありません。このシナリオでは、issuedTokenType の URI (docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0.pdf、英語) に示しているように、SAML 1.1 トークンを使用しています。SAML 1.0 や SAML 2.0 など、トークンの種類を変更する場合は、その標準の URI を使用します。もちろん、フェデレーション バインディングの構成で示される STS が、要求するトークンの種類をサポートしていなければなりません。
message セクションには関連する設定として、issuedKeyType と negotiateServiceCredential があります。issuedKeyType 設定は、証明キー (blogs.msdn.com/vbertocci/archive/2008/01/02/on-prooftokens.aspx (英語) 参照) が対称 (既定) または非対称 (追加のオーバーヘッドが発生) のいずれであるかを示します。この設定も、STS と互換性がある必要があります。negotiateServiceCredential を true に設定すると、クライアントは事前に RP 公開キーにアクセスする必要がなくなりますが、ネゴシエーションは相互運用可能なプロトコルではありません。クライアントが WCF クライアントでなければ、negotiateServiceCredential を false に設定してください。でも心配する必要はありません。false に設定しても、SvcUtil を使用してプロキシを生成すれば、クライアントに RP の公開キーのコピーが base64 でエンコードされて提供されます。
claimTypeRequirements セクションで指定するクレームの種類は、サービスが承認に使用する必須またはオプションのクレームの種類を示します。この例のサービスでは、ユーザーを特定する name クレーム、および少なくとも 1 つの permission クレームを想定しています。permission クレームは、Todo 項目を作成、読み取り、更新、または削除するためのユーザーの権利を表す、カスタム クレームの種類です (これらのクレームの種類については、以下の図 4 に一覧しています)。クレームの種類の一覧をサービスのメタデータに含め、クライアントがこの情報を RST に含めることができるようにします。特定の RP に発行されるクレームは STS が認識していることが多いので、フェデレーション バインディングでの一覧にすべてのクレームを含める必要はありません。
図 4 Todo List アプリケーションのシナリオでユーザー単位に発行されるクレーム
このシナリオの信頼されたトークン発行者は RP-STS で、WIF を使用して実装されています。RP-STS では、1 つの WS-Trust エンドポイントが http://localhost:8010/rpsts で公開され、そのメタデータ交換アドレスは http://localhost:8010/rpsts/mex です。図 3 では、クライアントでプロキシを生成する際に、使用できる STS エンドポイントを見つけられるよう、発行者のメタデータ アドレスが issuerMetadata セクションに指定されています。
STS が複数のエンドポイントを公開していたとします。たとえば、Windows 資格情報を使用してイントラネット ユーザーを http://localhost:8010/rpsts/internal で認証し、ユーザー名とパスワードを使用してインターネット ユーザーを http://localhost:8010/rpsts/external で認証します。RP サービスでは、サービスのフェデレーション エンドポイント構成に関連付けられた特定の発行者エンドポイントを選択して指定することで、クライアントでプロキシを生成する際に、STS と通信する構成が、最初の互換性のあるエンドポイントではなく指定したエンドポイントになるようにできます。このようなエンドポイントを指定するには、次のように issuerMetadata 要素と issuer 要素の両方にアドレスを指定します。
<issuerMetadata address="http://localhost:8010/rpsts/mex" />
<issuer address="http://localhost:8010/rpsts/mex/external" />
この方法のメリットは、選択対象の STS エンドポイントが複数存在し、使用するエンドポイントを RP が変更する必要があるときに、クライアントでのプロキシの生成が簡略化されることです。クライアントの認証先エンドポイントを RP で判断しない場合は、issuerMetadata 設定だけを指定し、適切な認証用エンドポイントをクライアント アプリケーションで判断できるようにすることをお勧めします。
サービス構成で issuerMetadata 要素を省略し、発行者のアドレスだけを指定すると、アドレスは発行者の論理 URI (http://localhost:8010/rpsts/issuer) に評価されるため、STS の物理エンドポイント アドレスにマップされるとは限りません。クライアントで同様の構成を行うと、同じ発行者のマネージ情報カードを (Windows CardSpace 経由で) 選択するようメッセージが表示されます。カードは、要求されたトークン形式とクレームの種類の条件も満たしている必要があります。Windows CardSpace を使用したアクティブ フェデレーション シナリオの詳細については、wpfandcardspace.codeplex.com (英語) を参照してください。
クライアント プロキシの生成
SvcUtil またはサービス参照の追加を使用して Windows クライアントのプロキシを生成するときは、発行者のメタデータ交換アドレスを使用して、発行者が公開するエンドポイントについての情報を収集します。言い換えると、次のようなシナリオが考えられます。
- RP サービス エンドポイントのフェデレーション バインディングで、特定の発行者アドレスを指定せずに発行者のメタデータ アドレスを指定すると、クライアント構成には最初のプロトコル互換 STS エンドポイント、およびクライアント開発者が必要に応じて使用するようコメントされたその他すべての互換性のあるエンドポイントが含まれます。
- RP サービスのフェデレーション バインディングで、発行者のメタデータ アドレスと特定の発行者アドレスを指定すると、(プロトコルと互換性があると想定して) クライアント構成には特定のアドレスが含まれます。
- RP サービスのフェデレーション バインディングでメタデータ アドレスだけを指定すると、クライアント構成にもメタデータ アドレスだけが含まれ、発行者のバインディング構成は含まれません。つまり、既に説明したように、CardSpace などの ID セレクターが起動します。
クライアントで、図 3 に構成を示した TodoListService のプロキシを生成し、STS が 1 つのエンドポイントを公開するとすると、クライアント側の WS2007FederationHttpBinding 構成には、次のような issuer 設定と issuerMetadata 設定が含まれます。
<issuer address="http://localhost:8010/rpsts"
binding="ws2007HttpBinding"
bindingConfiguration="http://localhost:8010/rpsts">
<identity>
<certificate encodedValue="[base64 encoded RP-STS certificate]" />
</identity>
</issuer>
<issuerMetadata address="http://localhost:8010/rpsts/mex" />
issuer 要素では、発行者のエンドポイント、およびこのエンドポイントとの通信に必要なバインディング構成が指定されていることに注目してください。この場合、以下の WS2007HttpBinding 構成に示すように、メッセージ セキュリティを使用し、クライアントはユーザー名とパスワードによって STS で認証されます。
<ws2007HttpBinding>
<binding name="http://localhost:8010/rpsts" >
<security mode="Message">
<message clientCredentialType="UserName"
negotiateServiceCredential="false"
algorithmSuite="Default"
establishSecurityContext="false" />
</security>
</binding>
</ws2007HttpBinding>
クライアント エンドポイントでは、次のようにフェデレーション バインディング構成が RP エンドポイントに関連付けられます。
<client>
<endpoint address="http://localhost:8000/TodoListService"
binding="ws2007FederationHttpBinding"
bindingConfiguration="wsFed"
contract="TodoList.ITodoListService" name="default">
<identity>
<certificate encodedValue="[base64 encoded RP certificate" />
</identity>
</endpoint>
</client>
この構成では、クライアント プロキシはサービスを呼び出す前に有効なユーザー名とパスワードで初期化するだけです。
TodoListServiceProxy _Proxy = new TodoListServiceProxy("default");
if (!ShowLogin()) return;
this._Proxy.ClientCredentials.UserName.UserName = this.Username;
this._Proxy.ClientCredentials.UserName.Password = this.Password;
this._TodoItems = this._Proxy.GetItems();
トークンの発行
プロキシでは、まず、RP-STS で認証を受けるための資格情報を提供し、SAML 1.1 トークンを要求する RST を送信して、RP に少なくとも 1 組の name と permission のクレームが必要なことを示します。ユーザーは STS 資格情報ストアと照合されて認証され、認証済みのユーザー向けに適切なクレームが発行されます。続いてプロキシでは、発行されたトークンを含む RSTR を処理し、このトークンを RP に渡して、認証済みユーザー向けにセキュリティが確保されたセッションを確立します。
この例では、STS は WIF を使用して構築されているため、カスタム資格情報ストアに照合してユーザーが認証され、図 4 に応じて各ユーザーにクレームが発行されます。
ただし、AD FS 2.0 に基づく STS では、Windows ドメインに照合してユーザーが認証され、AD FS 構成に従ってクレームが発行されます。WIF に基づくカスタム STS では、選択した資格情報ストアに照合してユーザーを認証できますが、独自のコードを組み込んで、資格情報ストアと関連するクレームのマッピング処理を管理する必要があります。
ID モデルの構成
WIF を使用して WCF サービスでクレームベースの承認を有効にするには、フェデレーション向けに ServiceHost インスタンスを初期化します。次のように、FederatedServiceCredentials 型によって公開される ConfigureServiceHost メソッドを呼び出すと、この処理をプログラムで実行できます。
ServiceHost host = new ServiceHost(typeof(TodoList.TodoListService));
FederatedServiceCredentials.ConfigureServiceHost(host);
host.Open();
次のように ConfigurationServiceHostBehaviorExtension ビヘイビア拡張を使用すると、同じ処理を宣言で行うことができます。
<serviceBehaviors>
<behavior name="fedBehavior" >
<federatedServiceHostConfiguration/>
<serviceMetadata />
</behavior>
</serviceBehaviors>
どちらの方法でも、ServiceHost には FederatedServiceCredentials 型のインスタンスが割り当てられ、サービスのクレームベースの承認動作が制御されます。この型は、プログラムで初期化することも、サービスの microsoft.identityModel 構成セクションで初期化することもできます。ID モデルの設定は WIF 固有の設定であり、ASP.NET アプリケーションや WCF アプリケーションでのクレームベースの承認用設定を提供します。図 5 に、ほぼすべての設定の概要を示します。
図 5 重要な microsoft.identityModel 要素の概要
WIF を使用する WCF サービスでは、ServiceHost を通常の WCF の認証ビヘイビアと承認ビヘイビアで初期化する必要がなくなりました。WIF はこの機能の後継であり、全般的なセキュリティを構成するさらに優れた方法が提供されます (WIF は、クレームベースおよびフェデレーション シナリオ以外でも役立ちます)。図 6 に、TodoListService で使用される ID モデルの設定を示します。
図 6 WCF サービスによく指定される ID モデルの設定
<microsoft.identityModel>
<service>
<issuerNameRegistry type="Microsoft.IdentityModel.Tokens.
ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<trustedIssuers>
<add name="http://localhost:8010/rpsts" thumbprint=
"c3 95 cd 4a 74 09 a7 77 d4 e3 de 46 d7 08 49 86 76 1a 99 50"/>
</trustedIssuers>
</issuerNameRegistry>
<serviceCertificate>
<certificateReference findValue="CN=RP" storeLocation="LocalMachine"
storeName="My" x509FindType="FindBySubjectDistinguishedName"/>
</serviceCertificate>
<audienceUris mode="Always">
<add value="http://localhost:8000/TodoService"/>
</audienceUris>
<certificateValidation certificateValidationMode="PeerTrust" />
<securityTokenHandlers>
<remove type="Microsoft.IdentityModel.Tokens.Saml11.
Saml11SecurityTokenHandler, Microsoft.IdentityModel,
Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
<add type="Microsoft.IdentityModel.Tokens.Saml11.
Saml11SecurityTokenHandler, Microsoft.IdentityModel,
Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35">
<samlSecurityTokenRequirement >
<roleClaimType
value="urn:TodoListApp/2009/06/claims/permission"/>
</samlSecurityTokenRequirement>
</add>
</securityTokenHandlers>
<claimsAuthorizationManager
type="TodoList.CustomClaimsAuthorizationManager, TodoList"/>
</service>
</microsoft.identityModel>
issuerNameRegistry 設定は、任意の信頼された証明書発行者を指定するために使用します。図 6 のように ConfigurationBasedIssuerNameRegistry を使用する場合は、発行者の拇印を指定して、信頼された証明書発行者の一覧を指定する必要があります。実行時に、ConfigurationBasedIssuerNameRegistry によって X509 セキュリティ トークンがこの一覧と照合してチェックされ、拇印が一覧に含まれていないトークンは拒否されます。SimpleIssuerNameRegistry を使用して任意の X509 トークンまたは RSA トークンを許可することもできますが、多くの場合は、カスタムの IssuerNameRegistry 型を指定して、ConfigurationBasedIssuerNameRegistry が機能しない場合に独自のヒューリスティックを使用してトークンを検証します。
図 6 の構成では、RP-STS によって署名されていないすべてのトークンが拒否されます (CN=RPSTS の証明書の拇印を使用しています)。一方、以下の構成では、TrustedIssuerNameRegistry というカスタムの IssuerNameRegistry 型を指定しています。
<issuerNameRegistry type="TodoListHost.TrustedIssuerNameRegistry, TodoListHost"/>
以下の TrustedIssuerNameRegistry の実装を使用すると、同じ結果が得られます。つまり、受信トークンのサブジェクト名をチェックすることで、CN=RPSTS によって署名されていないトークンが拒否されます。
public class TrustedIssuerNameRegistry : IssuerNameRegistry
{
public override string GetIssuerName(SecurityToken securityToken)
{
X509SecurityToken x509Token = securityToken as
X509SecurityToken;
if (x509Token != null)
{
if (String.Equals(x509Token.Certificate.SubjectName.Name,
"CN=RPSTS"))
{
return x509Token.Certificate.SubjectName.Name;
}
}
throw new SecurityTokenException("Untrusted issuer.");
}
}
図 6 の serviceCertificate 設定は、発行元の STS によってトークンが RP 向けに暗号化されていることを前提に、受信セキュリティ トークンの暗号化解除に使用する証明書を示します。Todo List アプリケーションの場合、RP-STS によって、RP の公開キー (CN=RP) を使用してトークンが暗号化されます。
通常、SAML トークンには、RP に評価され、トークンの発行先を表す対象 URI 要素が含まれます。RP への送信を目的としていないトークンを明示的に拒否できます。既定では、audienceUris の mode 属性は Always に設定されています。つまり、受信トークンと照合して検証するために、少なくとも 1 つの URI を指定する必要があります。図 6 では、TodoListService のアドレスに一致する対象 URI が含まれている SAML トークンだけが構成により許可されています。一般にお勧めできませんが、次のように audienceUris の mode 属性を Never に設定すると、受信 SAML トークンに対する対象制限条件が評価されません。
<audienceUris mode="Never"/>
クライアントから RST を STS に送信するときに、通常、RST にトークンの発行先 (RP) を示す AppliesTo 設定が含まれます。STS では、この情報を使用して SAML トークンの対象 URI を設定できます。
certificateValidation 設定では、受信 X509 トークン (トークンの署名に使用されるトークンなど) の検証方法を制御します。図 6 では、certificateValidationMode が PeerTrust に設定されています。つまり、関連付けられた証明書が TrustedPeople ストアに見つかった場合にのみ証明書が有効になります。信頼された証明書を証明書ストアに明示的にインストールする必要があるため、この設定は、PeerOrChainTrust (既定値) よりもトークン発行者の検証に適しています。PeerOrChainTrust は、ルート証明機関 (CA) が信頼されている場合にも署名が承認されることを指定します (ほとんどのコンピューターでは、ルート CA には大量の信頼済み CA の一覧が含まれています)。
図 5 と図 6 のその他の設定の一部を、簡単に紹介しましょう。WIF の初期化に関してもう 1 つ説明すると、FederatedServiceCredentials のインスタンスを microsoft.identityModel セクションで初期化する代わりに、プログラムで初期化して ConfigureServiceHost を渡すこともできます。次のコードはこの処理を示しています。
ServiceHost host = new ServiceHost(typeof(TodoList.TodoListService));
ServiceConfiguration fedConfig = new ServiceConfiguration();
fedConfig.IssuerNameRegistry = new TrustedIssuerNameRegistry();
fedConfig.AudienceRestriction.AudienceMode = AudienceUriMode.Always;
fedConfig.AudienceRestriction.AllowedAudienceUris.Add(new
Uri("http://localhost:8000/TodoListService"));
fedConfig.CertificateValidationMode =
X509CertificateValidationMode.PeerTrust;
fedConfig.ServiceCertificate = CertificateUtil.GetCertificate(
StoreName.My, StoreLocation.LocalMachine, "CN=RP");
FederatedServiceCredentials fedCreds =
new FederatedServiceCredentials(fedConfig);
FederatedServiceCredentials.ConfigureServiceHost(host,fedConfig);
host.Open();
プログラムからの初期化は、サーバー ファーム全体に適用されるデータベース設定で ServiceHost を初期化する際に特に有効です。
WIF コンポーネントのアーキテクチャ
WIF の動作を ServiceHost に適用すると、クレームベースの承認を容易にするためにいくつかの WIF コンポーネントが初期化されます。その多くは WCF の拡張機能です。最終的には、ClaimsPrincipal が要求スレッドにアタッチされ、クレームベースの承認がサポートされます。図 7 は、WIF のコア コンポーネントと ServiceHost の関係を表します。
図 7 WIF と共にインストールされるコア コンポーネント
FederatedServiceCredentials 型は既定の ServiceCredentials 動作に置き換わり、IdentityModelServiceAuthorizationManager (FederatedServiceCredentials の初期化時にインストール) は既定の ServiceAuthorizationBehavior に置き換わります。FederatedServiceCredentials では、FederatedSecurityTokenManager のインスタンスも作成されます。これらの型が連携することで、ClaimsAuthenticationManager、ClaimsAuthorizationManager、および特定の要求に適用される SecurityTokenHandler を使用して、要求ごとに認証と承認が実行されます。
図 8 は、これらの各コンポーネントのコミュニケーションの流れを表しています。このようなコミュニケーションによって、要求スレッドのセキュリティ プリンシパル (この場合は ClaimsPrincipal 型) が作成され、このセキュリティ プリンシパルに基づいてアクセスを承認できるようになります。
図 8 ClaimsPrincipal を作成して ClaimsPrincipal に照合して承認できるコンポーネント
FederatedSecurityTokenManager からは要求の適切なトークン ハンドラー (ここでは Saml11SecurityTokenHandler) が返され、ClaimsAuthorizationManager への参照が指定されます。トークン ハンドラーでは受信トークンから ClaimsIdentity が作成され、ClaimsPrincipal が (ラッパー クラスを使用して) 作成されて ClaimsAuthorizationManager の ValidateToken メソッドに渡されます。この結果、要求スレッドにアタッチされる ClaimsPrincipal を変更または置換するチャンスが生まれます。以下の既定の実装では、単に渡されたものと同じ ClaimsPrincipal が返されます。
public virtual IClaimsPrincipal Authenticate(string resourceName, IClaimsPrincipal incomingPrincipal)
{
return incomingPrincipal;
}
カスタムの ClaimsAuthenticationManager を指定して、セキュリティ トークンから受信したクレームを、RP でアクセスの承認に使用できる形式に変換しようと考えた方もいるでしょう。しかし、この例では、RP-STS から発行された適切な RP クレームが SAML トークンで保持されているため、このようなクレームから作成された ClaimsPrincipal は承認用に機能します。
次に、ClaimsAuthorizationManager を参照する IdentityModelServiceAuthorizationManager によって、その CheckAccess が呼び出され、アクセスの制御方法をカスタマイズするチャンスが生まれます。以下の既定の実装では、アクセスが制限されません。
public virtual bool CheckAccess(AuthorizationContext context)
{
return true;
}
AuthorizationContext パラメーターによって、ClaimsPrincipal と関連付けられたクレームにアクセスできるようになります。関連付けられたクレームは、要求に関連する操作のコレクション (呼び出されるサービス操作を示す URI など)、および要求に関連付けられたリソースについての情報 (サービス URI など) です。このような情報は、同じ承認手順で処理される複数のサービスに対する呼び出しを明確に区別するのに役立ちます。一元化された承認を実装するために、独自の ClaimsAuthorizationManager を提供できます。このような例については、承認の手法を説明する際に紹介します。
.NET Framework のロールベースのセキュリティは、IPrincipal に基づくセキュリティ プリンシパルが各スレッドにアタッチされ、認証済みユーザーの ID がこのセキュリティ プリンシパルによって IIdentity 実装にラップされるという前提に基づいています。WIF を使用しない場合、WCF では、認証と承認のための system.serviceModel 構成に基づいて、セキュリティ プリンシパルが要求スレッドごとにアタッチされます。IIdentity 型は、認証用に提示されている資格情報の種類に基づいて作成されます。たとえば、Windows 資格情報は WindowsIdentity に評価され、X.509 証明書は X509Identity に評価され、UserName トークンは GenericIdentity に評価されます。ServiceAuthorizationBehavior では、ID の IPrincipal ラッパーの種類が制御されます。たとえば、Windows 承認の場合は WindowsPrincipal が作成され、ASP.NET メンバーシップ プロバイダーの場合は RoleProviderPrincipal が作成されます。その他の場合は、カスタム承認ポリシーを使用して、選択した IPrincipal オブジェクトが作成されます。IPrincipal オブジェクトでは、直接、またはアクセス許可の要求を通じて間接的に呼び出せる IsInRole メソッドが公開され、このメソッドで機能へのアクセスが制御されます。
WIF は、最終的には IPrincipal と IIdentity 型から派生される ClaimsPrincipal 型と ClaimsIdentity 型 (IClaimsPrincipal と IClaimsIdentity に基づく) を提供することで、このモデルを拡張します。すべてのトークンは WIF によって ClaimsIdentity にマップされます。各受信セキュリティ トークンが検証されるとき、トークンに関連付けられた SecurityTokenHandler 型によって ClaimsIdentity が作成され、適切なクレームが提供されます。この ClaimsIdentity は ClaimsIdentityCollection にラップされ (イベント内では、トークンによって複数の ClaimsIdentity インスタンスが生成されます)、このコレクションは ClaimsPrincipal にラップされて要求スレッドにアタッチされます。この ClaimsPrincipal こそが、WCF サービスでの WIF 承認の中核です。
クレームベースの承認
WCF サービスでは、承認の手段に次の手法の 1 つが必要です。
- ClaimsPrincipal を使用して、動的な IsInRole チェックを実行する
- PrincipalPermission 型を使用して、動的なアクセス許可の要求を実行する
- PrincipalPermissionAttribute を使用して、各操作に宣言によるアクセス許可の要求を提供する
- カスタムの ClaimsAuthorizationManager を提供して、アクセス確認を 1 つのコンポーネントに一元化する
最初の 3 つのオプションは最終的に、ClaimsPrincipal 型によって公開される IsInRole メソッドを使用します。これは、決してロール ベースのセキュリティを実施しているわけではありません。単に、クレームの種類として role を選択し、適切なクレームを、IsInRole に渡される要求クレームと照合してチェックされるようにしているだけです。WIF の既定のロールクレームの種類は、schemas.microsoft.com/ws/2008/06/identity/claims/role です。フェデレーション シナリオに関連付けられている STS でこの種類のクレームが発行される場合は、必要に応じてこのクレームの種類に基づいてアクセスを制御することもできます。Todo List アプリケーションのシナリオでは、既に述べたようにクレームの種類としてカスタムの permission が承認に使用されるため、IsInRole チェックを簡単にするには ID モデルの構成でこのクレームの種類を role として指定する必要があります。
クレームの種類 role を、想定するトークンの種類、この場合は Saml11SecurityTokenHandler としてSecurityTokenHandler に指定します。図 6 に示すように、SecurityTokenHandler を削除し、優先するプロパティ設定を指定して同じ型を追加すると、この型の既定の構成を変更できます。SAML トークン ハンドラーには samlSecurityTokenRequirement セクションがあり、このセクション内では、name や role のクレームの種類の設定に加え、証明書の検証や Windows トークンに関するその他の設定を指定できます。このシナリオでは、独自のロールのクレームの種類を指定しました。
<samlSecurityTokenRequirement >
<roleClaimType value= "urn:TodoListApp/2009/06/claims/permission"/>
</samlSecurityTokenRequirement>
これは、IsInRole が ClaimsPrincipal に対して呼び出されるたびに、有効なアクセス許可クレームを確認することを意味しています。この機能を実現する方法の 1 つは、特定のクレームを必要とするコードのセクションが実行される前に、IsInRole を明示的に呼び出すことです。次のように Thread.CurrentPrincipal プロパティを使用すると、現在のプリンシパルにアクセスできます。
if (!Thread.CurrentPrincipal.
IsInRole("urn:TodoListApp/2009/06/claims/permission/delete"))
throw new SecurityException("Access is denied.");
実行時の明示的な IsInRole チェックとは別に、PrincipalPermission 型を使用して、従来の、ロールベースのアクセス許可の要求を作成することもできます。次のように、必要なロール クレーム (コンストラクターの 2 番目のパラメーター) でこの型を初期化すると、Demand の呼び出し時に現在のプリンシパルの IsInRole メソッドが呼び出されます。クレームが見つからないと、例外がスローされます。
PrincipalPermission p = new PrincipalPermission("", "urn:TodoListApp/2009/06/claims/permission/delete");
p.Demand();
PermissionSet を構築して、チェック対象の複数のクレームを収集することもできます。
PermissionSet ps = new PermissionSet(PermissionState.Unrestricted);
ps.AddPermission(new PrincipalPermission("", "urn:TodoListApp/2009/06/claims/permission/create"));
ps.AddPermission(new PrincipalPermission("", "urn:TodoListApp/2009/06/claims/permission/read"));
ps.Demand();
アクセス確認がサービス操作全体に対して行われる場合は、代わりに PrincipalPermissionAttribute を適用することもできます。これは、呼び出し対象の操作に、必要なクレームを宣言によって関連付ける優れた方法です。このような属性を積み上げて、複数のクレームをチェックすることもできます。
[PrincipalPermission(SecurityAction.Demand, Role = Constants.Permissions.Create)]
[PrincipalPermission(SecurityAction.Demand, Role = Constants.Permissions.Read)]
public string CreateItem(TodoItem item)
場合によっては、承認を 1 つのコンポーネントに一元化すると便利なことがあります。つまり、カスタムの ClaimsAuthorizationManager を提供して、アクセス確認を実行します。図 6 は、カスタムの ClaimsAuthorizationManager を構成する方法を示しています。また、TodoListService 用のこの ClaimsAuthorizationManager の実装を図 9 に示します (わかりやすくするために一部だけを示しています)。
図 9 カスタムの ClaimsAuthorizationManager の実装
class CustomClaimsAuthorizationManager : ClaimsAuthorizationManager
{
public CustomClaimsAuthorizationManager()
{
}
public override bool CheckAccess(AuthorizationContext context)
{
if (context.Resource.Where(x=> x.ClaimType ==
System.IdentityModel.Claims.ClaimTypes.Name && x.Value ==
"http://localhost:8000/TodoListService").Count() > 0)
{
if (context.Action.Where(x=> x.ClaimType ==
System.IdentityModel.Claims.ClaimTypes.Name && x.Value ==
Constants.Actions.GetItems).Count() > 0)
{
return
context.Principal.IsInRole(
Constants.Permissions.Read);
}
// other action checks for TodoListService
}
return false;
}
}
ClaimsAuthorizationManager では、AuthorizationContext パラメーターを受け取る CheckAccess のオーバーライドが提供されます。このパラメーターでは、リソース (この例ではサービス URI)、操作のコレクション (この例では、サービス操作 URI を示す 1 つの操作)、および ClaimsPrincipal (要求スレッドに対して未割り当て) が参照されています。この例で説明のために処理しているように、コンポーネントがサービス全体で共有されているかどうかリソースをチェックできます。主に、サービス操作 URI に対して操作を確認し、操作の要件に従って IsInRole チェックを実行します。
一般に、私は保護された操作やコード ブロックからの承認チェックを分離することはあまり好みではありません。処理内容と関連している場所で宣言されるコードを管理する方がはるかに容易です。
次回に続く
これで、WCF と WIF を使用してアクティブ フェデレーション シナリオを設定する方法がよくおわかりいただけたでしょう。今回は、WCF とプロキシ生成の観点からフェデレーション バインディング、トークンの発行プロセス、サービスでの WIF の構成、さまざまなクレームベースの承認手法などについて説明しました。次回は、ASP.NET と WIF を使用するパッシブ フェデレーションについて取り上げます。
Michele Leroux Bustamante は、IDesign のチーフ アーキテクト、サンディエゴ地域の Microsoft Regional Director、および Connected Systems の Microsoft MVP を兼任しています。彼女の最新の著書は『Learning WCF』です。連絡を取るには、mlb@idesign.net (英語のみ) 宛てに電子メールを送るか、idesign.net (英語) にアクセスしてください。ブログは dasblonde.net (英語) です。