セキュリティポリシーの運用
さて前置きはこれまでとし、次にセキュリティポリシーの実際の運用方法を見てみましょう。ここではサンプルコードも紹介します。これを実際に試してみれば、セキュリティポリシーを変更したときに、どのような効果が現れるかが分かるでしょう。
Enterprise、Machine、Userの3レベルのセキュリティポリシーを定義しているXMLファイルを編集するには、コマンドラインツールの「caspol.exe」か、MMCスナップインの「mscorcfg.msc」を使います。多数のコンピュータのセキュリティポリシーを変更するスクリプトを作成するのであれば、使用するツールとしては「caspol.exe」の方が向いているでしょう。以下、本稿ではMMCスナップインを使うものとします。このスナップインを実行するには、コマンドプロンプトで次のように入力します。
''%Systemroot%''\Microsoft.NET\Framework\''version''\Mscorcfg.msc
UIが表示されたら、[ランタイムセキュリティポリシー\コンピュータ\コードグループ\All_Code]を展開します。現在のコンピュータで定義されているMachineレベルのコードグループの階層構造(図4)が表示されます。

既定のEnterpriseおよびUserレベルのコードグループ階層はAll_Codeコードグループしか持たないので、あまり面白くないでしょう。展開可能なコードグループのノードをすべて展開すれば、階層内のすべてのコードグループを表示できます。[LocalIntranet_Zone]コードグループをクリックし、右側のパネルの[コード グループ プロパティの編集]リンクをクリックしてください。[LocalIntranet_Zoneのプロパティ]ダイアログボックスが表示されたら、[アクセス許可セット]タブをクリックします。LocalIntranet_Zoneコードグループに付与されているアクセス許可の一覧が表示されます。

既定でLocalIntranet_Zoneコードグループには複数のリソース(レジストリ、ファイルシステムなど)のアクセス許可は設定されていません。ここをクリックすると、.NET Frameworkコードアクセスセキュリティのすべてのアクセス許可が表示されます。
[メンバシップ条件]タブをクリックすると、メンバシップ条件の種類は[ゾーン]で、[イントラネット]が特定ゾーンとなっていることが分かります。既定では、組織のイントラネットに属するアセンブリと、UNCパスから読み込まれたアセンブリが、このメンバシップ条件を満たします。[インターネット]、[ローカルイントラネット]、[信頼されているサイト]、[制限されているサイト]の4つのゾーンをInternet Explorerの[インターネットオプション]ダイアログで設定することができます。
次のサンプルコードは、コードアクセスセキュリティのアクセス許可がマネージコードの実行にどう作用するかを示すものです。このコンソールアプリケーションは、レジストリキーHKLM\Software\Microsoft\.NetFrameworkの下のレジストリサブキーを読み込み、そのキーの名前をコンソールに表示します。
using System; namespace ConsoleReadRegistry { /// <summary> /// Summary description for Class1. /// </summary> class Class1 { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main(string[] args) { Microsoft.Win32.RegistryKey rk; try { rk = Microsoft.Win32.Registry.LocalMachine.OpenSubKey( "Software\\Microsoft\\.NetFramework",false); string[] skNames = rk.GetSubKeyNames(); for (int i=0;i<skNames.Length;++i) { Console.WriteLine("Registry Key: {0}", skNames[i]); } rk.Close(); } catch(System.Security.SecurityException e) { Console.WriteLine("Security Exception Encountered: {0}", e.Message); } } } }
このアプリケーションをコンパイルして実行すると、.NetFrameworkキーの下にあるレジストリキーの名前が表示されます。つまり、このアセンブリにはレジストリの実行と読み込みのアクセス許可が付与されているわけです。

何が起きたか振り返ってみましょう。このコードをコンパイルすると、アセンブリがハードディスク上に生成されます。UserおよびEnterpriseセキュリティポリシーレベルに既定のセキュリティポリシーが設定されていて、このアセンブリはそこでAll_Codeコードグループに割り当てられるものと仮定します。この2つのポリシーレベルでは、All_Codeコードグループに割り当てられるすべてのアセンブリに「FullTrust」アクセス許可が付与されます。
このアセンブリはローカルハードディスクから読み込まれたので、MachineポリシーレベルでAll_CodeおよびMy_Computer_Zoneコードグループのメンバシップ条件を満たします。従って、このポリシーレベルではアセンブリに2つのコードグループのアクセス許可の和集合が付与されます。図1から分かるように、MachineポリシーレベルのAll_Codeコードグループには対応するアクセス許可はなく、My_Computer_Zoneコードグループに対応するアクセス許可は「FullTrust」(無制限)です。つまり、アセンブリには、Machineポリシーレベルで「FullTrust」アクセス許可が付与されることになります。
Enterprise、Machine、Userの3つのセキュリティポリシーレベルでは、収集された証拠に基づいてアクセス許可セットが付与されます。各ポリシーレベルで付与されるアクセス許可の積集合によって、アセンブリに付与されるアクセス許可が決まります(図3を参照)。この例ではAppDomainポリシーレベルをプログラムで指定していないので、このレベルは適用されません(AppDomainポリシーレベルを有効にする方法については後述)。以上のシナリオにより、アセンブリにはすべてのレベルで「FullTrust」アクセス許可が付与され、ローカルリソースを無制限にアクセスできるようになります。
同じアセンブリを共有フォルダにコピーし、コマンドラインからUNCパスを入力してアプリケーションを実行した場合は、図6とは異なる結果が得られます。例えば、コマンドプロンプトで\\MyMachine\MyShare\ConsoleReadRegistry.exeと入力してアプリケーションを開始すると、次のように表示されます。

先ほどの例との違いは、アセンブリがUNCパスで読み込まれることです。既定の[ローカルイントラネットゾーン]設定では、UNCパスで読み込まれたすべてのアセンブリをLocalIntranet_Zoneコードグループに割り当てるものと規定されています。仮にファイル共有がローカルコンピュータ上に置かれていたとしても、このアセンブリはMy_Computer_Zoneコードグループには割り当てられません。つまり、Machineポリシーレベルでは、アセンブリに図5のアクセス許可(「Registry」アクセス許可は含まれない)が付与されることになります。従って、次のメソッド呼び出しはSecurityExceptionを発生させます。
rk = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
"Software\\Microsoft\\.NetFramework",false);
特定のファイル共有に置かれたアセンブリにレジストリ読み込みのアクセス許可を付与するには、どうすればよいでしょう。簡単なのは、設定ツールでLocalIntranet_Zoneコードグループに「FullTrust」または「Everything」アクセス許可セットを関連付けることですが、これだとコンピュータが攻撃されやすくなるので最善の方法とはいえません。それよりも、「Registry」アクセス許可を持つカスタムコードグループをMachineポリシーレベルに追加する方がよいでしょう。このコードグループのURLメンバシップ条件で、ファイル共有のUNCパスを指定するわけです。では、設定ツールを使用してカスタムコードグループをLocalIntranet_Zoneコードグループの子として追加してみましょう。
まず、レジストリのアクセス許可を持つカスタムアクセス許可セットを作成します。MMCスナップインを使用し、[ランタイムセキュリティポリシー\コンピュータ\アクセス許可セット]ノードを右クリックし、ポップアップメニューから[新規作成]を選択します。[アクセス許可セットの作成]ダイアログが表示されたら、[アクセス許可セットの新規作成]ラジオボタンを選択し、新しいアクセス許可セットの名前と説明を入力します。

[次へ]をクリックします。次のダイアログが表示されたら、リストから[レジストリ]アクセス許可を選択し、[追加]をクリックします。[アクセス許可の設定]ダイアログが表示されたら、[アセンブリにレジストリへの無制限のアクセスを許可する]ラジオボタンを選択します。[OK]をクリックし、[完了]をクリックします。
新規のコードグループを作成するには、[ランタイムセキュリティポリシー\コンピュータ\コードグループ\All_Code\LocalIntranet_Zone]ノードを右クリックし、ポップアップメニューから[新規作成]を選択します。新しいコードグループの名前と説明を入力し、[次へ]をクリックします。[条件の種類を選択する]ダイアログが表示されたら、ドロップダウンリストから条件の種類として[URL]を選択し、当該アセンブリのUNCパスを入力します。[次へ]をクリックします。

[コードグループにアクセス許可セットを割り当てる]ダイアログが表示されたら、新しく作成したアクセス許可セットをドロップダウンリストから選択します。[次へ]と[完了]をクリックします。
この段階で、当該アセンブリは、All_Code、LocalIntranet_Zone、および新しく作成されたコードグループのメンバシップ条件を満たします。また、LocalIntranet_Zoneによるアクセス許可セットと新しいコードグループで付与されたレジストリアクセス許可との和が、Machineポリシーレベルで付与されるアクセス許可となります。UNCパスを入力してアプリケーションを再度実行すると、今度はSecurityExceptionは発生せず、当該コードはレジストリの読み込みを許可されます。
