印刷用ページ       送信     
クリックして評価とフィードバックをお寄せください
 基礎 : WCF にコード アクセス セキュリティを追加する (第 2...
Related Articles

この記事では、CLR を使用したアセンブリのバインドと読み込みに関するベスト プラクティスをいくつか紹介します。

Aarthi Ramamurthy および Mark Miller

MSDN Magazine May 2009

...

Read more!

Jon Flanders が、Windows Communication Foundation と AtomPub を利用して Web フィードを作成および使用する方法について説明します。

Jon Flanders

MSDN Magazine April 2009

...

Read more!

ここでは、何がニーズに最も適合するかを決定するためのデータ永続化パターンを確認します。Active Record、Data Mapper、Repository、Identity Map、Lazy Loading、および Virtual Proxy を含む一連のパターンについて説明します。

Jeremy Miller

MSDN Magazine April 2009

...

Read more!

XNA Game Studio 3.0 を使用して Zune 用のゲームを作成するための基本事項について Mike Calligaro が説明します。

Mike Calligaro

MSDN Magazine May 2009

...

Read more!

Cobra は Python の子孫であり、特に、動的または静的に型指定されたプログラミング モデルを組み合わせて使用することや、組み込みの単体テスト機能、スクリプト機能、およびいくつかの契約による設計の定義が特徴です。そのすばらしい能力をご紹介します。

Ted Neward

MSDN Magazine June 2009

...

Read more!

Also by this Author

C# 2.0 introduces a wealth of exiting new features, such as generics, iterators, partial classes and anonymous methods. While generics are the most talked-about feature especially for former classic C++ developers, the rest of the new features are important additions to your .NET development arsenal, enhancing power and improving overall productivity. This article is dedicated to all the new C# 2.0 capabilities besides generics to give you a good overall picture of the upcoming features.

Juval Lowy

MSDN ...

Read more!

この記事では、Visual Studio 2005 の新機能が非常に興味深いものであるということを説明します。最初のバージョンの C# を使用した場合よりも全体の生産性が向上し、簡潔なコードをすばやく作成できます。

Juval Lowy

MSDN Magazine Visual Studio 2005 Guided Tour 2006

...

Read more!

Juval Lowy は、Windows Communication Foundation に基づくアプリケーションを対象とした、構成の容易なセキュリティ設定を設計しています。

Juval Lowy

MSDN Magazine August 2007

...

Read more!

同期コンテキストおよび WCF でのその用途について説明した後に、カスタム同期コンテキストを使用するために WCF を拡張する場合のさまざまなオプションを、プログラムと宣言の両方を使って説明します。

Juval Lowy

MSDN Magazine November 2007

...

Read more!

The .NET Framework can use contexts as an object's execution scope and intercept calls going to and from the object, similar to the way COM+ provides component services. What is new with this mechanism is that the runtime allows developers to take part in the interception chain and add powerful services, thus extending existing component services. This in turn decouples the business logic from the system plumbing and simplifies long-term maintenance. Presently, .NET contexts and interception are undocumented aspects of .NET. This article presents ...

Read more!

Popular Articles

Paul DiLascia

MSDN Magazine August 2002

...

Read more!

ここでは、新しい F# 言語の基になるいくつかの概念について説明します。F# 言語は、関数型 .NET 言語とオブジェクト指向 .NET 言語の両方の要素を持っています。また、単純なプログラムを記述する方法についても説明します。

Ted Neward

MSDN Magazine Launch 2008

...

Read more!

サイドバー ガジェットは小さいけれど強力なツールで、驚くほど簡単に作成できます。ここでは、Donavon West がその楽しさを教えてくれます。

Donavon West

MSDN Magazine August 2007

...

Read more!

Kenny Kerr 氏は、Visual C++ を現代的かつ便利に使用できるようになる Visual C++ 2008 Feature Pack を絶賛しています。

Kenny Kerr

MSDN Magazine May 2008

...

Read more!

Chris Tavares は、ASP.NET MVC Framework の Model View Controller パターンが、柔軟性に富み簡単にテストできる Web アプリケーションの作成にどのように役立つかを説明しています。

Chris Tavares

MSDN Magazine March 2008

...

Read more!

基礎
WCF にコード アクセス セキュリティを追加する (第 2 部)
Juval Lowy

コードのダウンロード : Foundations2008_07.exe (497 KB)
オンラインでのコードの参照

2008 年 4 月号のこのコラムでは、Windows® Communication Foundation (WCF) に対するコード アクセス セキュリティ (CAS) のサポートが不十分であること、および WCF に適切な CAS のサポートを追加する動機について説明しました。続いて、セキュリティや WCF プログラミング モデルを損なうことなく、部分的に信頼されたクライアントが WCF サービスを呼び出せるように、CAS のサポートをクライアント側で追加する方法を説明しました (4 月号のコラムは msdn.microsoft.com/magazine/cc500644 を参照してください)。
今回は、部分的に信頼されたサービスと部分的に信頼されたホストを許可してソリューション全体を完成させるために必要なことを説明します。クライアント側については、このソリューションのしくみだけでなく、そこにつながるアプローチや思考過程についても説明します。また、WCF と Microsoft® .NET Framework の高度なプログラミング技法についても説明します。

.NET Framework 3.5 のホスト側 CAS
.NET Framework 3.5 において、WCF では BasicHttpBinding、WSHttpBinding、WebHttpBinding のバインドでサービスをホストするためには部分的に信頼されたコードのみが許可されており、セキュリティはまったく使用できないか、トランスポート セキュリティのみを使用できます。さらに WSHttpBinding では、メッセージ セキュリティ、信頼性の高いメッセージング、トランザクションなどの機能を使用できません。部分的な信頼に対応しているすべてのバインドは、テキスト エンコードを使用する必要があります。部分的な信頼で実行されているサービスでは、診断やパフォーマンス カウンタなどの追加機能を使用することはできません。
サポートされている機能の使用を一部だけに制限することはバインドで行います。HTTP でない各バインドは、サービス ホストに対し完全な信頼を自発的に要求します。許可された HTTP バインド自体は完全な信頼を要求しませんが、図 1 のとおり、使用する状況に応じてアクセス許可を要求します。
シナリオ アクセス許可
セキュリティを使用しないかまたはトランスポート セキュリティを使用する Basic、Web、WS- 実行とインフラストラクチャのセキュリティ アクセス許可、URI への呼び出しを受け入れるための Web アクセス許可
別のアセンブリの内部サービス型を使用する上記のバインド 無制限のリフレクション
認証された呼び出しを行う上記のバインド プリンシパル制御のセキュリティ アクセス許可
許可された HTTP バインドはすべて、エンドポイントに構成された URI における呼び出しを受け入れるためのアクセス許可 (URI の受け入れフラグによる Web アクセス許可) の他に、実行アクセス許可とインフラストラクチャを変更するためのアクセス許可 (インフラストラクチャ フラグによるセキュリティ アクセス許可) を要求します。呼び出しが認証されている場合でも、許可された HTTP バインドはスレッド プリンシパルを制御するためのアクセス許可 (プリンシパル制御フラグによるセキュリティアクセス許可) を要求します。
これは、バインドにかかわらず、WCF では認証後に、呼び出し中に提供されるクライアントの資格情報と同じ ID の新しいプリンシパルを組み込むためです。ホスト コンストラクタに渡されたサービス型が他のアセンブリで内部型として定義されている場合も、ホストはリフレクションを使用してその型をロードできるようにするために、無制限のリフレクション アクセス許可を要求します (インフラストラクチャ アクセス許可を要求するかどうかの判断は、その明白な必要性がないので複雑です)。
構成について考慮が必要な制限は他にもあります。たとえば、構成ファイルには (サービス側の証明書用に) 証明書ストアの参照を含めることができません。証明書ストアにアクセスするには、WCF によって完全な信頼が要求されるためです。管理者は HttpConfig.exe などのツールを使用して、これらの証明書の構成を個別に行う必要があります。
バインドのみに基づいてホスト側のアクセス許可要求を行うことの欠点は、使用する必要がなくても、ホストされたサービス インスタンスにこれらのアクセス許可が暗黙的に与えられることです。たとえば、サービス インスタンスがプリンシパルを制御したり、ホスト マシン上にある別のアセンブリの内部型をリフレクションしたりできるようになります。サービス アクセス許可がホスティング コード アクセス許可のサブセットになるようにする一方で、バインドでアクセス許可を要求しながらも、何らかの形でホストがサービスに異なるアクセス許可を与えられるように設計されていた方が良かった言えます。
WCF の機能を、分散トランザクションから、信頼性の高い呼び出し、さまざまな種類のセキュリティ資格情報、TCP や名前付きパイプなどのプロセス間通信 (IPC) のバインドを使用するイントラネット (または同じマシン上の) アプリケーションに至るまで、最大限に活用できるのが理想です。双方向コールバック、非同期呼び出し、診断やトレース、インストルメンテーション、もちろん Microsoft メッセージ キュー (MSMQ) によってキューに登録された呼び出しは言うに及びません。そして、このすべてを CAS を損なうことなく (つまり完全な信頼を使用せずに) 行うことも理想的です。

部分的に信頼されたサービス
.NET Framework 3.0 では、部分的な信頼でサービスを実行する唯一の方法は、操作に必要なアクセス許可のみを明示的に与え、その結果、他のアクセス許可すべてを暗黙的に拒否することでした。これを実現する方法の 1 つが、アクセス許可属性と SecurityAction.PermitOnly フラグのマッチングを行うことでした。次のサービスについて考えてみましょう。
[SecurityPermission(
  SecurityAction.PermitOnly,
  Execution = true)]
[UIPermission(SecurityAction.PermitOnly,
  Window = 
    UIPermissionWindow.SafeTopLevelWindows)]
class MyService : IMyContract {
  public void MyMethod() {
    Form form = new TestForm();
    form.ShowDialog();
  }
}
このサービスでは実行のアクセス許可が必要で (すべてのマネージ コードで必要)、セーフ ウィンドウを表示するためのアクセス許可も必要です。クラスに PermitOnly のアクセス許可属性を複数設定した場合は、実行時に 1 つのアクセス許可セットになります。
.NET Framework では、上記のアクセス許可のみを与えて他のアクセス許可すべてを積極的に拒否するために、専用のスタック ウォーク修飾子を組み込んでこのしくみを利用しています。その結果、(アプリケーション ドメインと同様に) サービスを含むアセンブリが完全な信頼をサービスに許可していても、他のすべてのアクセス許可が拒否されます。
そのサービスがファイルを開く操作などを行う場合に、セキュリティ例外が発生します。ファイル I/O の要求は、そのアクセス許可を積極的に拒否するスタック ウォーク修飾子に抵触するためです。サービスで可能な操作は、仮想的なサンドボックス内で実行して、セーフ ウィンドウを表示することのみです。.NET Framework では、アプリケーションが部分的に信頼されていることをユーザーに通知するために、図 2 のようなフォームでウィンドウ キャプションを変更し、警告タグを表示します。
図 2 部分的に信頼されたコードから表示されたウィンドウ (クリックすると拡大画像が表示されます)
属性としてアクセス許可を指定する代わりに、図 3 のようにアクセス許可セット XML ファイルに一覧を記述して、PermissionSetAttribute にそのファイル名を指定することもできます。
<!-- MyServicePermissions.xml --> 
<PermissionSet class = "System.Security.PermissionSet">
  <IPermission 
    class = "System.Security.Permissions.SecurityPermission" 
    Flags = "Execution"
  />
  <IPermission 
    class = "System.Security.Permissions.UIPermission" 
    Window = "SafeTopLevelWindows"
  />
</PermissionSet>

[PermissionSet(SecurityAction.PermitOnly,File = 
  "MyServicePermissions.xml")]
class MyService : IMyContract {
  public void MyMethod() {
    Form form = new TestForm();
    form.ShowDialog();
  }
}
アクセス許可設定ファイルはコンパイル時のみに使用され、展開時には使用されないことに注意してください。コンパイラは与えられたアクセス許可をクラスのメタデータに個別属性として結合します。ファイルがない場合は、ビルドが失敗します。

アプリケーション ドメイン ホスト
部分的に信頼されたサービスに対する属性に基づいたアプローチには、いくつかの問題があります。第一に、サービスが行うことは毎回変わり、さらに言えばサービスが使用する下流のクラスでも同様です。属性の組み合わせを修正したり、設定ファイルを変更したりする必要が生じます。これではサービスとクラスが結びつき、保守コストが増大します。
第二に、アクセス許可はサービス定義の一部であるため、サービスをホスティング環境の製品として、異なるアクセス許可で使用する方法がありません。アクセス許可を増やすことも減らすこともできません。
第三にさらに重要なのは、ほとんどのサービスでは、最低限の権限やアクセス許可で実行するために、操作に必要なセキュリティのアクセス許可を分析したり、与えられたアクセス許可を慎重に減らしたりする手間はかけられないということです。あるとすれば、ホストによってロードされるサービスが何に依存しているか考慮することです。それでもホストには、既定ではアクセス許可を変更する機能がありません。WCF の ServiceHost はすべてのサービスを完全な信頼でホストします。
これはサービス自体の保護にも有効ですが、サービスに最適と判断されるアクセス許可をどれでも与えられることはホストにも適しています。以上をふまえて、図 4 で定義されている AppDomainHost クラスを作成しました。
public class AppDomainHost : IDisposable {
  //Create new app domain in full trust
  public AppDomainHost(Type serviceType,params Uri[] baseAddresses);
  public AppDomainHost(Type serviceType,string appDomainName,
    params Uri[] baseAddresses);

  //Create new app domain with specified permission set
  public AppDomainHost(Type serviceType,PermissionSet permissions,
    params Uri[] baseAddresses);
  public AppDomainHost(Type serviceType,PermissionSet permissions,
    string appDomainName,params Uri[] baseAddresses);

  //Additional constructors that take standard permission set, 
  //permission set filename, and an existing app domain  

  public void Open();
  public void Close();
  public void Abort();

  //More members 
}
AppDomainHost クラスは WCF で提供されている ServiceHost と同様に使用できます。
AppDomainHost host = 
  new AppDomainHost(typeof(MyService));
host.Open();
相違点は、AppDomainHost では、指定されたサービス型を呼び出し元のアプリケーション ドメインではなく新しいアプリケーション ドメインでホストするということです。新しいアプリケーション ドメイン名は、既定で "AppDomain Host for" の後ろにサービス型と新しい GUID が付きます。新しいアプリケーション ドメインの名前の指定もできます。
AppDomainHost host = 
  new AppDomainHost(typeof(MyService),"My App Domain");
host.Open();
新しいアプリケーション ドメインは、既定では完全な信頼で作成されます。ただし、部分的に信頼されたサービスを管理するためには、別のアプリケーション ドメインにサービスを配置することが重要です。
AppDomainHost では、新しいアプリケーション ドメインのアクセス許可を指定できます。たとえば図 5 は、あるサービスと、そのサービスの動作に必要十分なアクセス許可のみを与えるホストを示しています。
class MyService : IMyContract {
  public void MyMethod() {
    Form form = new TestForm();
    form.ShowDialog();
  }
}

//Hosting code:
PermissionSet permissions = 
  new PermissionSet(PermissionState.None);
permissions.AddPermission(new SecurityPermission(
  SecurityPermissionFlag.Execution));
permissions.AddPermission(new UIPermission(
  UIPermissionWindow.SafeTopLevelWindows));

AppDomainHost host = 
  new AppDomainHost(typeof(MyService),permissions);

host.Open();
アクセス許可設定ファイルも使用できます。図 3 で示したファイルとは異なり、このファイルが必要なのは実行時のみで、展開後には変更できます。標準の名前付きアクセス許可セットの 1 つを指定することもできることに注目してください。部分的に信頼されたサービスに強制された仮想サンドボックスは、.NET Framework 1.0 から WCF までの何らかの技術によってサンドボックス外側の呼び出しを行う機能を使用する下流のクラスすべてに影響を及ぼします。
図 6 に示すとおり、1 つのホスト プロセスで複数の AppDomainHost インスタンスを作成し、それぞれ同じまたは異なるサービス型をホストし、任意のアクセス許可セットを指定できます (図 5 と同じサービス定義を使用します)。
//Default is service with full trust
AppDomainHost host0 = 
  new AppDomainHost(typeof(MyService),
  "Full Trust App Domain",
  new Uri("net.tcp://localhost:6000"));

host0.Open();

//With just enough permissions to do work
PermissionSet permissions1 = 
  new PermissionSet(PermissionState.None);
permissions1.AddPermission(new SecurityPermission(
  SecurityPermissionFlag.Execution));
permissions1.AddPermission(new UIPermission(
  UIPermissionWindow.SafeTopLevelWindows));

AppDomainHost host1 = 
  new AppDomainHost(typeof(MyService),permissions1,
  "Partial Trust App Domain",
  new Uri("net.tcp://localhost:6001"));

host1.Open();

//With not enough permissions to do work
PermissionSet permissions2 = 
  new PermissionSet(PermissionState.None);
permissions2.AddPermission(new SecurityPermission(
  SecurityPermissionFlag.Execution));

AppDomainHost host2 = 
  new AppDomainHost(typeof(MyService),permissions2,
  "Not enough permissions",new Uri("net.tcp://localhost:6002"));

host2.Open();

//Using one of the named permission sets
AppDomainHost host3 = 
  new AppDomainHost(typeof(MyService),
  StandardPermissionSet.Internet,
  "Named permission set",
  new Uri("net.tcp://localhost:6003"));

host3.Open();

AppDomainHost を実装する
AppDomainHost の実装は 2 つの作業に分かれます。まず、新しいアプリケーション ドメインを作成してサービス ホスト インスタンスを挿入します。その後サービス ホスト (およびサービス インスタンス) を部分的な信頼で実行します。サービス ホスト インスタンスを作成して、別のアプリケーション ドメインでアクティブ化するために、図 7 のように ServiceHostActivator クラスを記述しました。
class ServiceHostActivator : MarshalByRefObject {
  ServiceHost m_Host;

  public void CreateHost(Type serviceType,Uri[] baseAddresses) {
    m_Host = new ServiceHost(serviceType,baseAddresses);
  }
  public void Open() {
    m_Host.Open();
  }   
  public void Close() {
    m_Host.Close();
  }
  public void Abort() {
    m_Host.Abort();
  }

  //Rest of the implementation
}
ServiceHostActivator は WCF で提供される標準の ServiceHost インスタンスの単純なラッパーです。ServiceHostActivator は MarshalByRefObject から派生しているため、アプリケーション ドメインの境界を越えて AppDomainHost から呼び出すことができます。CreateHost メソッドは新しい ServiceHost インスタンスの作成をカプセル化します。ServiceHostActivator の他のメソッドは、基になっているホスト インスタンスにリモート呼び出しを転送するだけです。
AppDomainHost はオーバーロードされたコンストラクタをいくつか提供しています。これらのコンストラクタはすべて互いに呼び出し合って (図 8 参照)、新しいアプリケーション ドメインの作成も行いながら、最終的にはサービス型、新しいアプリケーション ドメイン インスタンス、新しいドメインのアクセス許可セット、およびベース アドレスを指定する protected のコンストラクタを使用します。
public class AppDomainHost : IDisposable {
  ServiceHostActivator m_ServiceHostActivator;

  public AppDomainHost(Type serviceType,
    params Uri[] baseAddresses) :  
    this(serviceType,"AppDomain Host for "+
    serviceType+" "+Guid.NewGuid(),
    baseAddresses) {
  }

  public AppDomainHost(Type serviceType,
    string appDomainName,
    params Uri[] baseAddresses) : this(serviceType,
    new PermissionSet(PermissionState.Unrestricted),
    appDomainName,baseAddresses) {
  }

  public AppDomainHost(Type serviceType,
  PermissionSet permissions,
  string appDomainName,
  params Uri[] baseAddresses) : 
  this(serviceType,AppDomain.CreateDomain(appDomainName),
  permissions,baseAddresses) {
  }

  //More constructors 

  protected AppDomainHost(Type serviceType,
    AppDomain appDomain,
    PermissionSet permissions,Uri[] baseAddresses) {

    string assemblyName = Assembly.GetAssembly(
      typeof(ServiceHostActivator)).FullName;
    m_ServiceHostActivator = appDomain.CreateInstanceAndUnwrap(
      assemblyName,typeof(ServiceHostActivator).ToString()) as 
      ServiceHostActivator;

    CodeAccessSecurityHelper.SetPermissionsSet(appDomain,permissions);

    m_ServiceHostActivator.CreateHost(serviceType,baseAddresses);
  }

  public void Open() {
    m_ServiceHostActivator.Open();
  }
  public void Close() {
    m_ServiceHostActivator.Close();
  }
  public void Abort() {
    m_ServiceHostActivator.Abort();
  }
  void IDisposable.Dispose() {
    Close();
  }
}
AppDomainHost の protected のコンストラクタでは、.NET リモーティングを使用して新しいアプリケーション ドメインに ServiceHostActivator のインスタンスを挿入し、最終的にそのリモート処理プロキシを m_ServiceHostActivator メンバへ格納します。
新しいアプリケーション ドメインは、既定では完全な信頼で作成されます。AppDomainHost では、与えられたアクセス許可のみを認めて他を拒否する新しい CAS ポリシーを新しいアプリケーション ドメインに組み込むために、自作の CodeAccessSecurityHelper クラスの SetPermissionsSet メソッドを使用します。
public static class CodeAccessSecurityHelper {
  public static void SetPermissionsSet(
    AppDomain appDomain,
    PermissionSet permissions) {

    PolicyLevel policy = PolicyLevel.CreateAppDomainLevel();
    policy.RootCodeGroup.PolicyStatement = 
      new PolicyStatement(permissions);
    appDomain.SetAppDomainPolicy(policy);
  }
  //More members
}
これは、アプリケーション ドメイン レベルで新しいセキュリティ ポリシーを作成し、AppDomain クラスの SetAppDomainPolicy メソッドを呼び出すのと同様にシンプルです。
public sealed class AppDomain : 
  MarshalByRefObject,... {

  public void SetAppDomainPolicy(
    PolicyLevel domainPolicy);
  //More members
}
偶然ですが同様の技法が ClickOnce でも使用されており、ClickOnce で展開されたアプリケーションの部分的な信頼での実行を強制します。
Open や Close など、AppDomainHost の他のメソッドを呼び出すと ServiceHostActivator のプロキシを使用して他のアプリケーション ドメインに呼び出しを行い、そのホスト インスタンスのオープンやクローズを行います。サービス インスタンスはホストをオープンしたアプリケーション ドメインで実行されるので、サービスもそのアプリケーション ドメインのセキュリティ ポリシー下で実行されます。

部分的に信頼されたホスト
ここまでご紹介してきた部分的に信頼されたサービスに対するアプローチは、完全な信頼で実行される AppDomainHost を使用するコードを作成することを前提としていました。これは、図 1 に記載されていないホスティングのシナリオでは、ServiceHost とバインドが完全な信頼を要求するためです。
では、ホストを起動するコードが部分的にしか信頼されていない場合はどうなるでしょうか。AppDomainHost と ServiceHostActivator を完全に信頼されたアセンブリに配置し、部分的に信頼された呼び出し元を許可して、両方に完全な信頼をアサートすることは可能です。
[PermissionSet(SecurityAction.Assert,Unrestricted = true)]
public class AppDomainHost : IDisposable
{...}

[PermissionSet(SecurityAction.Assert,Unrestricted = true)]
class ServiceHostActivator : MarshalByRefObject
{...}
このアプローチでも動作しますが、CAS を回避して重要なセキュリティ メカニズムを無効化するため、2 つのセキュリティ上の問題を招くことになります。第一に、コードをオープンするホストに、トランスポート チャネルの呼び出しを受け入れたり、分散トランザクションなどの WCF アクティビティに参加したりするためのアクセス許可があることを前提にするのは誤りです。第二に、完全な信頼をアサートしてスタック ウォークを抑制することにより、ホストを起動した部分的に信頼されたコードは、サービスをより高い権限で作成してホストに対する不正行為をするために利用される可能性があるということです。たとえば、ホストするコードにはファイル I/O のアクセス許可がなくても、ファイル I/O のアクセス許可があるサービスに対する呼び出しを受け入れるために AppDomainHost が使用される可能性があります。
この解決方法は単純です。AppDomainHost と ServiceHostActivator では、完全な信頼の無条件なアサートを行わずに、必要なときのみローカルにアサートを行い、その後は元のアクセス許可に戻す必要があります。さらに、AppDomainHost では使用する側のコードを疑い、適切なホスティングのアクセス許可 (呼び出しの受け入れなど) を要求する必要があります。ホストするコードに、サービスを実行するために最低限必要なアクセス許可があるかどうかも、AppDomainHost では確認する必要があります。これにより、別のアプリケーション ドメイン内でホストされたサービスを、制限されている部分的に信頼されたコードが高いアクセス許可で使用できないようにします。最後に残ったのは、構造化されたホスト側セキュリティ要求です。

構造化されたホスト側セキュリティ要求
部分的に信頼された呼び出し元に合わせて書き直した ServiceHostActivator を図 9 に示します。ServiceHostActivator の CreateHost メソッドは、宣言で完全な信頼をアサートすることはできません。これにより、呼び出し元コードの適切なホスト アクセス許可を要求できなくなるためです。代わりに、プログラムで CodeAccessSecurityHelper を使用して完全な信頼のアクセス許可セットを作成してアサートし、それからホストを作成します。WCF が完全な信頼を要求する場合、アサートによってその要求がコール スタックに到達できないようにします。
class ServiceHostActivator : MarshalByRefObject {
  public void CreateHost(Type serviceType,Uri[] baseAddresses) {
    CodeAccessSecurityHelper.PermissionSetFromStandardSet(
    StandardPermissionSet.FullTrust).Assert();

    m_Host = new ServiceHost(serviceType,baseAddresses);

    PermissionSet.RevertAssert();

    m_Host.DemandHostPermissions();
  }

  //Behavior demands happen here, must assert 
  [PermissionSet(SecurityAction.Assert,Unrestricted = true)]
  public void Open() {
    m_Host.Open();
  }   
  ...
}
ホストの作成後、CreateHost は明示的にアサートを元に戻します。その後 CreateHost はホスティングのアクセス許可を要求するために、作成したばかりのホスト インスタンス上で CodeAccessSecurityHelper の拡張ヘルパ メソッド DemandHostPermissions を呼び出します。DemandHostPermissions はホスト オブジェクトを検証し、適切なアクセス許可を要求します。
たとえば、ホストが TCP エンドポイントをサポートしている場合、DemandHostPermissions は実行のアクセス許可と、エンドポイント URI における TCP 呼び出し受け入れのアクセス許可を要求します。TCP で信頼性の高いメッセージングが使用されている場合、DemandHostPermissions はポリシー制御のアクセス許可を要求します。図 10 のとおり、他にも考えられる要求は数多くあります。
シナリオ アクセス許可
TCP 実行のセキュリティ アクセス許可、URI における TCP 呼び出しを受け入れるための Socket アクセス許可
IPC 実行、ポリシー制御、証拠制御のセキュリティ アクセス許可
MSMQ 実行のセキュリティ アクセス許可、キューから読み取るための MSMQ アクセス許可
WS、WS-Dual、Basic、Web 実行のセキュリティ許可、URI における呼び出しを受け入れるための Web アクセス許可
RM over TCP ポリシー制御のセキュリティ アクセス許可
認証された呼び出し プリンシパル制御のセキュリティ アクセス許可
トランザクション伝達 無制限の分散トランザクション アクセス許可
メッセージ セキュリティを使用したユーザー名の資格情報、検証された証明書の資格情報、サービス証明書 ストアの列挙、ストアのオープン、証明書の列挙をするためのストア アクセス許可
診断トレース COMPUTERNAME を読み取るための環境アクセス許可、パス検出およびログ ファイルへの追加と書き込みのためのファイル I/O アクセス許可
Windows サービス パフォーマンス カウンタ サービス カウンタへの書き込み、エンドポイント カウンタへの書き込み、操作カウンタへの書き込みをするためのパフォーマンス カウンタ アクセス許可
すべての WCF パフォーマンス カウンタ サービス カウンタへの書き込み、エンドポイント カウンタへの書き込み、操作カウンタへの書き込み、ホスト カウンタへの書き込みをするためのパフォーマンス カウンタ アクセス許可
ASP.NET プロバイダ 最低限の ASP.NET ホスティング アクセス許可
IPC バインドを使用する場合、ポリシーと証拠を制御するためのセキュリティ アクセス許可を要求します。MSMQ バインドを使用する場合、エンドポイント キューから読み取るためのアクセス許可を要求します。HTTP バインドのいずれかを使用する場合、エンドポイント アドレスにおける呼び出しの受け入れを要求します。認証された呼び出しをバインドで使用する場合、DemandHostPermissions はプリンシパル制御のためのアクセス許可を要求します。
トランザクション対応のバインドを使用し、トランザクション フローを有効化して、トランザクション フローが許可されている (または必須となっている) エンドポイントのコントラクトで少なくとも 1 つの操作が行われる場合、無制限の分散トランザクション アクセス許可を要求します。
メッセージ セキュリティ、クライアント証明書の検証、メッセージ保護を目的としたサービス証明書の使用など、証明書ストアへのアクセスはすべて、ストアを開いて証明書を列挙するために、ストアの列挙を要求します。
サービスが診断を行う場合、DemandHostPermissions はコンピュータ名の環境変数へのアクセスと対象ファイルに対するファイル I/O のアクセス許可を要求します。ホストが WCF パフォーマンス カウンタを使用する場合、DemandHostPermissions はカウンタに報告されたレベルで書き込むためのアクセス許可を要求します。
呼び出し元の認証やロール メンバシップにおいてホストが ASP.NET プロバイダに依存している場合、DemandHostPermissions は最低限の ASP.NET ホスティング アクセス許可を要求します (このアクセス許可はすべてのプロバイダで必要です)。WS-Dual バインドによるコールバックをする場合、バインド自体がコールバック エンドポイントに接続するための Web アクセス許可を要求します。そのため、ホストの起動時に AppDomainHost で明示的に要求する必要はありません。

構造化されたホスト要求を実装する
図 11 に DemandHostPermissions の実装の一部を示します。まずエンドポイント固有の要求を行います。サービスのエンドポイントのコレクションを反復処理して、各エンドポイントにそれぞれの接続、セキュリティ、トランザクションのアクセス許可を要求します。
public static class CodeAccessSecurityHelper {
  internal static void DemandHostPermissions(this ServiceHost host) {
    foreach(ServiceEndpoint endpoint in host.Description.Endpoints) {
      DemandHostConnectionPermissions(endpoint);
      DemandHostSecurityPermissions(endpoint);
      using(TransactionScope scope = new TransactionScope()) {
        DemandTransactionPermissions(endpoint);
      }
    }
    DemandHostStorePermissions(host);
    DemandPerformanceCounterPermissions();
    DemandTracingPermissions();
    DemanAspNetProvidersPermissions(host);
  }
  internal static void DemandHostConnectionPermissions(
    ServiceEndpoint endpoint) {

    PermissionSet connectionSet = 
      new PermissionSet(PermissionState.None);
    if(endpoint.Binding is NetTcpBinding) {
      connectionSet.AddPermission(new SocketPermission(
        NetworkAccess.Accept, TransportType.Tcp,
        endpoint.Address.Uri.Host,endpoint.Address.Uri.Port));
    }

    /* Checking the other bindings */

    connectionSet.Demand();
  }

  static void DemanAspNetProvidersPermissions(ServiceHost host) {
    bool demand = false;

    foreach(IServiceBehavior behavior in host.Description.Behaviors) {
      if(behavior is ServiceCredentials) {
        ServiceCredentials credentialsBehavior = 
          behavior as ServiceCredentials;

        if(credentialsBehavior.UserNameAuthentication.
          UserNamePasswordValidationMode == 
          UserNamePasswordValidationMode.MembershipProvider)  {

          demand = true;
          break;
        }
      }
      if(behavior is ServiceAuthorizationBehavior) {
        ServiceAuthorizationBehavior serviceAuthorization = 
          behavior as ServiceAuthorizationBehavior;
        if(serviceAuthorization.PrincipalPermissionMode == 
          PrincipalPermissionMode.UseAspNetRoles && 
          Roles.Enabled) {

          demand = true;
          break;
        }
      }
    }
    if(demand) {
      IPermission permission = 
        new AspNetHostingPermission(
        AspNetHostingPermissionLevel.Minimal);
      permission.Demand();
    }
  }
  //Rest of the implementation 
}
トランザクション要求については、トランザクション スコープを使用して一時的なアンビエント トランザクションを作成します。これにより、クライアント側でも同じメソッドが使用可能になります。このメソッドは呼び出し時にアンビエント トランザクションの存在に基づいてアクセス許可を要求します。
エンドポイント要求後に、DemandHostPermissions はサービス ホスト構成の生成物であるアクセス許可を要求します。具体的には証明書ストアへのアクセス許可、パフォーマンス カウンタ アクセス許可、トレース アクセス許可、ASP.NET プロバイダ アクセス許可です。たとえば、ASP.NET プロバイダ アクセス許可を要求するために、サービス動作のコレクションを取得し、ホストのサービス資格情報の動作がメンバシップ プロバイダを使用していないか、またはホストのサービス認証動作がロール プロバイダを使用していないか確認し、使用している場合には ASP.NET ホスティング アクセス許可を要求します。
部分的に信頼された環境で構造化要求をサポートするには、AppDomainHost も以下のように一部書き直す必要があります。
[SecurityPermission(
  SecurityAction.Assert,ControlAppDomain = true)]
[ReflectionPermission(
  SecurityAction.Assert,Unrestricted = true)]
public class AppDomainHost : IDisposable {
  protected AppDomainHost(Type serviceType,
    AppDomain appDomain,
    PermissionSet permissions,
    params Uri[] baseAddresses) {
    //Cannot grant service permissions 
    // the host does not have
    permissions.Demand();

    //Rest of constructor 
    }
}
AppDomainHost は新しいアプリケーション ドメインを作成するため、そのアプリケーション ドメインを制御するためのアクセス許可をアサートします。他のアプリケーション ドメインにホストを挿入するのにリフレクションを使用するため、無制限のリフレクション アクセス許可もアサートします。重要なのは、新しいアプリケーション ドメインにサービス ホストを作成する前に、呼び出し元のコードのサービスで求められたアクセス許可を AppDomainHost が要求するということです。これにより部分的に信頼されたコードでは、呼び出し元のコードでの許可を超えることなく、まったく同じだけのことを許可されたサービスのみを開始およびホストできるようになります。

AppDomainHost のその他の特徴
AppDomainHost は ICommunicationObject をサポートしていませんが、そのイベント モデルやステート マシン管理は提供しています。また、AppDomainHost はいくつかの静的変数を呼び出し元のアプリケーション ドメインから、作成した新しいアプリケーション ドメインへコピーします。具体的には、ServiceHostActivator を使用して別のアプリケーション ドメインに ASP.NET プロバイダ名をコピーします。AppDomainHost は一時的に ASP.NET ホスティング アクセス許可をアサートし、プロバイダへアクセスして、メンバシップおよびロール アプリケーション名の値をコピーした後、アサートを元に戻します。

Juval Lowy は、WCF のトレーニングとアーキテクチャ コンサルティングを行う IDesign 社に所属するソフトウェア アーキテクトです。最近の著書としては、『Programming WCF Services』があります。彼は、シリコン バレー地域の Microsoft Regional Director も務めています。連絡先は www.idesign.net です。

Page view tracker