Last Updated 2011/09/21
Visual Studio のヘルプの中に表題と同じタイトルの項目があります。
開発ツールと言語ドキュメント . Visual Studio ドキュメント ... Visual Studio での .NET Framework プログラミング ..... ネイティブコードと .Net Framework コードのセキュリティ ....... .Net Framework におけるセキュリティ
この記事を書いた人は .Net Framework におけるセキュリティの全体像をなんとか書こうとしたのでしょうが、結局項目の羅列に終わってしまったようです。
セキュリティ関係の情報を得ようと WEB サイトをあたると、セキュリティの設定はこうするなどのコーディング法を解説する記事はたくさんありますが、そもそも .Net Framework におけるセキュリティとはどういうものなのかを解説する記事はありません。
そうこうしているうちに、ヤットというか、最近見つけたので、紹介します。このサイトは海外の多くのサイトでも引用しているようです。
http://codezine.jp/article/detail/361?p=1
残念ながら日本人の手になるものではありませんが、日本語訳がしっかりしているので、読む時にイライラすることはないでしょう。ただし、このページのタイトルが「コードアクセスセキュリティの仕組みの設定方法」であることから分かるように、セキュリティの全体像ではなく、コードアクセス関係に焦点をあてたものです。
そこで、Visual Studio のヘルプの記事とこのサイトの記事とを合わせて、.Net Framework における セキュリティの全体像(チョットあやしいですが)にできるだけせまりたいと考えています。
なお、私が公開している .Net Framework クラスライブラリリファレンスを見ながらサンプルコードを見ると、より分かりやすいと思います。
暗号化サービスは広い意味でのセキュリティであって、ここで議論しているセキュリティとは直接関係ないので、説明を省略します。レジストリアクセスセキュリティについてはアクセス制御リストの中で解説します。システムのアカウントコントロールのシステムは OS としての Windows を、またアカウントは Windows ユーザーアカウントをさしますが、これは Windows の問題なので、ここでは直接取り上げることはしません。
.Net Framework 2.0 になって、いくつかのクラスに GetAccessControl および SetAccessControl メソッドが追加されました。以下に、カテゴリ別のセキュリティクラスと規則クラスとをリストアップします。
カテゴリ | 〜 Security クラス | 規則クラス |
---|---|---|
暗号化キー | CryptoKeySecurity | CryptoKeyAccessRule, CryptoKeyAuditRule |
ディレクトリ | DirectorySecurity | FileSystemAccessRule, FileSystemAuditRule |
イベント待機ハンドル | EventWaitHandleSecurity | EventWaitHandleAccessRule, EventWaitHandleAuditRule |
ファイル | FileSecurity | FileSystemAccessRule, FileSystemAuditRule |
ミューテックス | MutexSecurity | MutexAccessRule, MutexAuditRule |
レジストリキー | RegistrySecurity | RegistryAccessRule, RegistryAuditRule |
セマフォ | SemaphoreSecurity | SemaphoreAccessRule, SemaphoreAuditRule |
ディレクトリ関係についていえば、Direcotory クラスと DirectoryInfo クラスに GetAccessControl および SetAccessControl メソッドが追加されました。
アクセス制御の一般的な手順は、GetAccessControl メソッドで現在の セキュリティオブジェクトを取得し、必要な変更を行ったあとで、SetAccesssControl メソッドを使って設定する、ということになります。
このページではファイル、ディレクトリ、レジストリキーの 3 つのカテゴリについて解説します。
なお、アクセス制御リストの「リスト」が何をさすのかが分からないのですが、Windows の内部的にはリスト形式でデータを保持しているのだろうと思います。要するに、ユーザーには関係ない話と理解しておけばいいと思います。
File クラスと FileInfo クラスに GetAccessControl および SetAccessControl メソッドがあります。まず、GetAccessControl メソッドを使ってファイルのアクセス制御オブジェクトを取得します。Windows に付属の Explorer.exe を例にとって、どんな情報が得られるかをチェックしてみましょう。
private void button1_Click(object sender, EventArgs e) { string fileName = @"c:\Windows\Explorer.exe"; FileSecurity security = File.GetAccessControl(fileName); AuthorizationRuleCollection col = security.GetAccessRules(true, true, typeof(SecurityIdentifier)); for (int i = 0; i < col.Count; ++i) { AuthorizationRule rule = col[i]; textBox1.Text += String.Format("( {0} )\r\n", i + 1); textBox1.Text += String.Format("IdentityReference Value: {0}\r\n", rule.IdentityReference.Value); textBox1.Text += String.Format("InheritanceFlags: {0}\r\n", rule.InheritanceFlags); textBox1.Text += String.Format("IsInherited: {0}\r\n", rule.IsInherited); textBox1.Text += String.Format("PropagationFlags: {0}\r\n", rule.PropagationFlags); } }
実行結果:
( 1 ) IdentityReference Value: S-1-5-18 InheritanceFlags: None IsInherited: False PropagationFlags: None ( 2 ) IdentityReference Value: S-1-5-32-544 InheritanceFlags: None IsInherited: False PropagationFlags: None ( 3 ) IdentityReference Value: S-1-5-32-545 InheritanceFlags: None IsInherited: False PropagationFlags: None ( 4 ) IdentityReference Value: S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464 InheritanceFlags: None IsInherited: False PropagationFlags: None
しかし、これでは何がどうなっているのかが分かりません。そこで、実際に作成したファイルにアクセス制御を付加する手順を説明します。
private void button1_Click(object sender, EventArgs e) { string fileName = "test.txt"; string account = Environment.UserDomainName + @"\" + Environment.UserName; StreamWriter sw = null; try { sw = new StreamWriter(fileName, false, System.Text.Encoding.GetEncoding("shift-jis")); sw.WriteLine("テスト"); sw.Close(); // アクセス制御を取得する FileSecurity security = File.GetAccessControl(fileName); // ファイルの内容を読み込むことは許可するが、編集は拒否する security.AddAccessRule(new FileSystemAccessRule(account, FileSystemRights.ReadData, AccessControlType.Allow)); security.AddAccessRule(new FileSystemAccessRule(account, FileSystemRights.WriteData, AccessControlType.Deny)); // アクセス制御を設定する File.SetAccessControl(fileName, security); } catch (Exception ex) { MessageBox.Show(ex.Message); } } // button1_Click で設定した書き込み拒否を削除する private void button2_Click(object sender, EventArgs e) { string fileName = "test.txt"; string account = Environment.UserDomainName + @"\" + Environment.UserName; FileSecurity security = File.GetAccessControl(fileName); security.RemoveAccessRule(new FileSystemAccessRule(account, FileSystemRights.WriteData, AccessControlType.Deny)); File.SetAccessControl(fileName, security); }
button1 をクリックして作成したファイルは結果的に上書き禁止になりますが、ファイルの属性が ReadOnly になるわけではありません。つまり、Windows が管理するファイルのセキュリティ上、ReadOnly になっているということになります。
ファイルシステム上、ファイルとディレクトリは同じ扱いですので、「ファイルのアクセス制御」の解説と基本的には同じです。ただし、Directory クラスと DirectoryInfo クラスとに読み替えてください。
「ファイルのアクセス制御」と基本的な手順は同じです。そこで、実際にレジストリキーを作成し、それにアクセス制御を追加してみましょう。
private void button1_Click(object sender, EventArgs e) { RegistryKey regKey = null; string userName = Environment.UserDomainName + @"\" + Environment.UserName; try { regKey = Registry.CurrentUser.CreateSubKey(@"software\NETClass\AclTest"); regKey.SetValue("Test", "テスト"); // アクセス制御オブジェクトを取得する RegistrySecurity security = regKey.GetAccessControl(); // レジストリキーの内容を読み込むことは許可する security.AddAccessRule(new RegistryAccessRule(userName, RegistryRights.QueryValues | RegistryRights.Delete | RegistryRights.ChangePermissions, AccessControlType.Allow)); // 値の設定は拒否する security.AddAccessRule(new RegistryAccessRule(userName, RegistryRights.SetValue, AccessControlType.Deny)); // アクセス制御を設定する regKey.SetAccessControl(security); } finally { regKey.Close(); } } // button1_Click で設定したアクセス制御を削除する private void button2_Click(object sender, EventArgs e) { RegistryKey regKey = null; string userName = Environment.UserDomainName + @"\" + Environment.UserName; try { regKey = Registry.CurrentUser.OpenSubKey(@"software\NETClass\AclTest"); if (regKey == null) return; // アクセス制御を取得する RegistrySecurity security = regKey.GetAccessControl(); // レジストリの値の編集拒否を削除する security.RemoveAccessRule(new RegistryAccessRule(userName, RegistryRights.SetValue, AccessControlType.Deny)); regKey.SetAccessControl(security); // アクセス制御を設定する } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { regKey.Close(); } }
さて、button1_Click を選択したあと、実行結果を確認するには、Windows 付属の Regedit.exe を使うのが簡単です。Regedit.exe を実行し、作成したキーの値を変更してみてください。「レジストリに書き込めません」エラーとなるはずです。
次に、button2_Click を選択すると同じエラーになります。いろいろと試してみましたが、うまくいきません。.Net Framework のヘルプの RegistryRights enum 型の SetValue の項の説明は、「レジストリキー内の名前/値ペアを作成、削除、または設定する権限」となっています。「設定する権限」は、「名前/値ペアを設定する権限」と解釈してよさそうですが、アクセス制御を含むのかもしれません。なお、作成したレジストリキーは削除できます。
業務関係のアプリケーションでは、ユーザーの資格情報に基づいて、データやリソースへのアクセスを制限することがよくあります。このようなアプリケーションでは、通常、ユーザーのロールを調べて、そのロールに基づいてアクセス可能なリソースの範囲などを決定します。
ロール "role" の英語としての意味は、「役割」ですが、「担当分野」あるいは「業務上の権限」のほうが分かりやすいかもしれません。たとえば、平社員には人事情報へのアクセスを禁止するが、課長以上になるとアクセス可能になるといった具合です。このように、ロール(適訳がないので、そのまま表記します)に基づくセキュリティをロールベースセキュリティ "Role-Base Security" と呼びます。ロールベースセキュリティは、個々のユーザーの ID に基づくプリンシパル "principal" を使います。プリンシパルはコードを実行する時のセキュリティコンテキストです。
システム "system" もそうですが、コンテキスト "context" という言葉も直感的には分かりにくい言葉ですね。ここでは「条件」ぐらいの解釈でいいと思います。
Windows 環境における個々のユーザーの ID は、Windows ユーザーアカウントに基づく ID で、WindowsIdentity クラスの GetCurrent メソッドを使うと、現在の Windows ユーザーをあらわす ID を取得できます。
WindowsIdentity id = WindowsIdentity.GetCurrent();
WindowsIdentity オブジェクトは、ユーザーの名前(Name プロパティ)と認証の種類(AuthenticationType プロパティ)を持ちます。ユーザーの名前は、Windows ユーザーアカウント名です。認証の種類はログオン時のプロトコルをあらわしますが、Windows 環境では "NTLM" です。
次に、WindowsIdentity オブジェクトへの参照をふくむ IPrincipal インターフェースを継承するオブジェクト(WindowsPrincipal オブジェクト)を通じて、ユーザーの ID およびロールを調べることができます。
以下に、WindowsPrincipal オブジェクトを取得するコードを示します。この方法は通常、検証を 1 回だけ実行する場合に使います。
WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
現在のスレッドに対するプリンシパルは以下のコードで取得できます。この方法は通常、検証を繰り返し実行する場合に使います。
// 現在のドメインのプリンシパルポリシーを WindowsPrincipal オブジェクトに設定する AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); WindowsPrincipal principal = (WindowsPrincipal)System.Threading.Thread.CurrentPrincipal;
WindowsPrincipal クラスの Identity プロパティを使うと、関連付けた WindowsIdentity オブジェクトを取得できます。また、IsInRole メソッドを使うことで、ユーザーがどのロールに属するかを調べることができます。
さて、WindowsIdentity オブジェクトと WindowsPrincipal オブジェクトとを作成したところで、ロールベースセキュリティをチェックする手順を説明します。チェックする手順には次の 3 つの方法があります。
強制セキュリティチェックまたは宣言セキュリティチェックをする場合は、PrincipalPermission クラスを使います。
PrincipalPermission オブジェクトの Demand メソッドを呼び出し、現在のスレッドのプリンシパルの ID およびロールをチェックします。
private void Form1_Load(object sender, System.EventArgs e) { GenericIdentity identity = new GenericIdentity("MyUser"); string[] roles = { "Administrator", "User" }; GenericPrincipal principal = new GenericPrincipal(identity, roles); // 現在のスレッドのプリンシパルを設定する Thread.CurrentPrincipal = principal; } private void button1_Click(object sender, EventArgs e) { this.ShowMessage("MyUser"); ← ここを別の文字列に変更して実行すること } // セキュリティの対象となるメソッド private void ShowMessage(string userName) { try { PrincipalPermission perm = new PrincipalPermission(userName, "Administrator"); perm.Demand(); // 正常に動作する時 MessageBox.Show("yes"); } catch (SecurityException ex) { MessageBox.Show(ex.Message); } }
セキュリティチェックを必要とするコード(通常はメソッド)に PrincipalPermissionAttribute 属性を付加する方法です。簡単な例を示しましょう。
private void button1_Click(object sender, EventArgs e) { MessageBox.Show("yes"); } [PrincipalPermission(SecurityAction.Demand, Name = "BadBoy", Role = "User")] private void button2_Click(object sender, EventArgs e) { MessageBox.Show("no"); }
button1 をクリックすると正常に動作しますが、button2 をクリックすると例外が発生します。つまり、現在認証されているプリンシパルの名前とは異なる "BadBoy" に設定しているからです。ただし、この方法は button2_Click イベントハンドラの内部で例外が発生するわけではないので、アプリケーションレベルで例外を捕捉しない限り異常終了してしまいます。異常終了を避けたい場合は、Application クラスの ThreadException イベントを利用します。
ロールベースセキュリティは通常、強制チェックか宣言チェックを使いますが、Principal オブジェクトに直接アクセスする方法もあります。この場合は、プリンシパルオブジェクトの Identity プロパティで取得したオブジェクトに直接アクセスします。
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); WindowsPrincipal principal = (WindowsPrincipal)Thread.CurrentPrincipal; if (principal.Identity.Name.Equals(@"MYDOMAIN\myuser") && principal.Identity.IsAuthenticated.Equals(true)) textBox1.Text += String.Format("認証 {0}\r\n", principal.Identity.Name); else textBox1.Text += "認証不可\r\n";
コードアクセスセキュリティに関してはこの記事を書いている時点でなお迷っています。というのは、コードアクセスセキュリティは事実上、ネットワーク上のコードを実行する時のセキュリティチェックに意味があるからです。インターネットエクスプローラ(以後、IE)を通じる場合、IE 自体にセキュリティを設定する機能があり、.Net Framework のコードを使う必要はありません。イントラネットの場合は、ロールベースセキュリティのほうが現実的なような気がします。
私の環境ではネットワークを想定するコードのテストにも制約があり、解説の正否を確認する手段が限られます。そこで、実務的な解説とはほどとおいと思いますが、私が知りえた情報をできるだけ公開したいと思います。
コードアクセスセキュリティは証拠に基づいて動作しますから、まず証拠の中身を見て見ましょう。証拠は Evidence クラスが担当します。以下のコードは、現在のアプリケーションドメインにデフォルトで設定されている証拠を取得します。
private void button1_Click(object sender, EventArgs e) { System.Security.Policy.Evidence evidence = AppDomain.CurrentDomain.Evidence; foreach (object obj in evidence) { textBox1.Text += obj.ToString() + "\r\n"; } }
実行結果:
<System.Security.Policy.Zone version="1"> <Zone>MyComputer</Zone> </System.Security.Policy.Zone> <System.Security.Policy.Url version="1"> <Url>file:///D:/NET Framework/Execute/ACL/ACLTest/bin/Debug/ACLTest.EXE</Url> </System.Security.Policy.Url> <System.Security.Policy.Hash version="1"> <RawData>4D5A90000300000004000000FFFF0000B8000000000000004000000000000000000000 ... .... .... <RawData> </System.Security.Policy.Hash>
3 つの証拠が含まれていますが、それぞれ System.Security.Policy.Zone、System.Security.Policy.Url、System.Security.Policy.Hash が XML 要素のルートになっているようです。これらはメンバーシップ条件クラス名です(メンバーシップ条件については後述)。その下層の要素として、それぞれ Zone、Url、RawData が設定されています。Zone は System.Security.Policy.ZoneMembershipCondition クラスの SecurityZone プロパティをあらわし、要素の内容は SecurithZone enum 型です。Url は System.Security.Policy.UrlMembershipCondition クラスの Url プロパティ、RawData は System.Security.Policy.HashMembershipCondition クラスの MD5 プロパティまたは SHA1 プロパティの生データをあらわします。
ID アクセス許可のための証拠オブジェクトを作成する方法は以下のとおり。
証拠の種類 | クラス | 説明 |
---|---|---|
ディレクトリ名 | - | アプリケーションのインストール先のディレクトリ |
ハッシュ | System.Security.Policy.Hash | SHA1 などの暗号ハッシュ |
発行元 | System.Security.Policy.Publisher | ソフトウェア発行元の署名(コードの Authenticode 署名者) |
サイト | System.Security.Policy.Site | "http://www.microsoft.com/japan" などコードの発生元のサイト |
厳密な名前 | System.Security.Policy.StrongName | アセンブリの厳密名 |
URL | System.Security.Policy.Url | コードの発生元の URL |
ゾーン | System.Security.Policy.Zone | Internet ゾーンなどコードの発生元のゾーン |
上記のほかに、アプリケーション定義の証拠やシステム定義の証拠も作成可能です。
そのものズバリ、Evidence クラスがあります。実際の証拠は、「証拠オブジェクトの作成」の項で説明した個々の証拠オブジェクトの組み合わせとなります。複数の証拠を Evidence クラスの Merge メソッドで 1 つの証拠オブジェクトに合成することができます。実際の手順は以下のコードを見てください。
private void button1_Click(object sender, EventArgs e) { Evidence evidence = new Evidence(); Url url = new Url("http://www.kanazawa-net.ne.jp"); evidence.AddHost(url); Evidence evidence2 = new Evidence(); string publicKeyBlob = "00240000048000009400000006020000" + "00240000525341310004000001000100" + "19390E945A40FB5730204A25FA5DC4DA" + "B18688B412CB0EDB87A6EFC50E2796C9" + "B41AD3040A7E46E4A02516C598678636" + "44A0F74C39B7AB9C38C01F10AF4A5752" + "BFBCDF7E6DD826676AD031E7BCE63393" + "495BAD2CA4BE03B529A73C95E5B06BE7" + "35CA0F622C63E8F54171BD73E4C8F193" + "CB2664163719CA41F8159B8AC88F8CD3"; byte[] publicKey = this.HexsToArray(publicKeyBlob); // 厳密名を作成する StrongName strongName = new StrongName(new StrongNamePublicKeyBlob(publicKey), "SN01", new Version("1.2.3.4")); // アセンブリ情報を証拠に追加する evidence2.AddAssembly("SN01"); evidence2.AddAssembly(new Version("1.2.3.4")); evidence2.AddAssembly(strongName); // evidence2 をマージする evidence.Merge(evidence2); Text = evidence.Count.ToString(); // ← ここは 4 と表示される IEnumerator enumerator = evidence.GetEnumerator(); while (enumerator.MoveNext()) { textBox1.Text += String.Format("{0}\r\n", enumerator.Current); } } //-------------------------------------------------------------------------------- // 16 進数表記の文字列をバイト配列に変換する private byte[] HexsToArray(string s) { byte[] array = new byte[s.Length / 2]; for (int i = 0; i < s.Length; i += 2) { array[i / 2] = Byte.Parse(s.Substring(i, 2), NumberStyles.HexNumber); } return array; }
実行結果:
<System.Security.Policy.Url version="1"> <Url>http://www.kanazawa-net.ne.jp</Url> </System.Security.Policy.Url> SN01 1.2.3.4 <StrongName version="1" Key="002400000480000094000000060200000024000052534131000400000100010019390E945A40FB5730204A25FA5DC4DAB18688B412CB0EDB87A6EFC50E2796C9B41AD3040A7E46E4A02516C59867863644A0F74C39B7AB9C38C01F10AF4A5752BFBCDF7E6DD826676AD031E7BCE63393495BAD2CA4BE03B529A73C95E5B06BE735CA0F622C63E8F54171BD73E4C8F193CB2664163719CA41F8159B8AC88F8CD3" Name="SN01" Version="1.2.3.4"/>
「証拠」の項で触れましたが、証拠を構成するカテゴリ(分類)をあらわします。メンバーシップ条件の種類とそれに関係するクラスを以下にリストアップします。
条件の種類 | クラス |
---|---|
All | System.Security.Policy.AllMembershipCondition |
Application | System.Security.Policy.ApplicationDirectoryMembershipCondition |
GAC | System.Security.Policy.GacMembershipCondition |
Hash | System.Security.Policy.HashMembershipCondition |
Publisher | System.Security.Policy.PublisherMembershipCondition |
Site | System.Security.Policy.SiteMembershipCondition |
Strong Name | System.Security.Policy.StrongNameMembershipCondition |
URL | System.Security.Policy.UrlMembershipCondition |
Zone | System.Security.Policy.ZoneMembershipCondition |
ゾーン | 日本語表記 | 説明 |
---|---|---|
MyComputer | マイコンピュータ | ローカルコンピュータ |
LocalIntranet | イントラネット | ローカルネットワーク |
Internet | インターネット | インターネット |
Trusted | 信頼済みサイト | インターネットエクスプローラで完全に信頼できるとしたサイト |
Restricted | 信頼されないサイト | 同、制限付きで信頼できるサイト |
メンバーシップ条件は、条件の種類とゾーンとの組み合わせです。たとえば、条件の種類が Zone で、ゾーンが MyComputer といった具合です。
.Net Framework SDK の中に、「ID アクセス許可」が出てきます。しかし、この言葉は Microsoft の開発者にしか理解でない言葉です。この場合、ID とは何かについての説明がないからです。まず、ID アクセス許可クラスと呼んでいるものを以下にリストアップしましょう。
クラス名を見るとメンバーシップ条件の名前と一致していることが分かります。たとえば、ZoneIdentityPermission クラスの "Zone" です。つまり、ID とはメンバーシップ条件をあらわすのだと思います。.Net Framework の内部で何らかの ID を使っているので、ID アクセス許可と名付けたのだろうと想像します。
コードアクセス許可クラスは、CodeAccessPermission クラスから派生します。その主なクラスは以下のとおり(すべてではありません)。「ID アクセス許可」でリストアップしたクラスも CodeAccessPermission クラスから派生していますが、ここでは省略します。
証拠の内容に応じて与えられるデフォルトの名前付きアクセス許可セットが用意されています。「許可のセット」とはいろいろな許可ひとまとめに(セット)にしたものです。たとえば、"Internet" であれば、FileDialogPermission、PrintingPermission、UIPermission などのコードアクセス許可オブジェクトを持ちます。
アクセス許可セット名 | 説明 |
---|---|
Everything | 規定のすべての権限を許可 |
Execution | 実行を許可(保護されているリソースの使用は許可しない) |
FullTrust | すべてのリソースへの完全なアクセス許可 |
Internet | インターネットからのアプリケーションに適したデフォルトの許可 |
LocalIntranet | エンタープライズ(ローカルイントラネット)内のデフォルトの許可 |
Nothing | アクセス許可なし(コードは実行できない) |
SkipVerification | セキュリティのチェックを省略 |
私の環境(Windows Vista)におけるアクセス許可セットに実際に含まれるアクセス許可の種類をリストアップします。
Everything | 環境変数 ファイルダイアログ ファイル IO 分離ストレージファイル リフレクション レジストリ セキュリティ ユーザーインターフェース カスタムのアクセス許可(System.Security.Permissions.KeyContainerPermission) DNS 印刷 ソケットアクセス Web アクセス イベントログ カスタムのアクセス許可(System.Security.Permissions.StorePermission) パフォーマンスカウンタ SQL クライアント カスタムのアクセス許可(System.Security.Permissions.DataProtectionPermission |
Execution | セキュリティ |
FullTrust | なし(制限なしの意味) |
Internet | ファイルダイアログ 分離ストレージファイル セキュリティ ユーザーインターフェース 印刷 |
LocalIntranet | 環境変数 ファイルダイアログ 分離ストレージファイル リフレクション セキュリティ ユーザーインターフェース DNS 印刷 |
Noting | なし(すべて制限する) |
SkipVerification | セキュリティ |
ポリシー "policy" は「方針」あるいは「指針」でいいと思います。セキュリティポリシーとは、.Net Framework がコードに与えるアクセス許可を決定するための方針のことです。
セキュリティポリシーは次の要素で構成されています。
ただし、独自のセキュリティポリシーを作成しなくても多くの場合、デフォルトのセキュリティポリシーを使うことができます。必要なら、独自のセキュリティポリシーを設定することもできます。
.Net Framework は証拠に基づいて実行コードがどのコードグループに属するかを調べて、コードに与えるアクセス許可を決定します。
.Net Framework は、セキュリティポリシーに基づいて、アセンブリとアプリケーションドメインの両方にアクセス許可を与えます。
コードグループというと、ある条件が一致するコードの集まりと解釈できます。しかし、コードの集まりという点にひっかかりを感じますね。セキュリティ関係の仕組みを明らかにできないことは理解できますが、.Net Framework が内部的にどのような処理をしているかがブラックボックスになっていますので、ハッキリとは分かりません。
ともあれ、コードグループは、1 つの証拠オブジェクトとアクセス許可セットとの対応を設定したものです。各コードグループは、.Net Framework 標準の名前付きアクセス許可セットと関連付けられていますので、たいていの場合はそれで間に合うと思います。詳しくは、次の項目の「デフォルトのセキュリティポリシー」を参照してください。
デフォルトのコードグループ名 | デフォルトの名前付きアクセス許可セット |
---|---|
MyComputer(ローカルコンピュータ上のコード) | FullTrust |
Microsoft StrongName(Microsoft 厳密な名前で署名されたコード) | FullTrust |
ECMA StrongName(ECMA 厳密な名前で署名されたコード) | FullTrust |
LocalIntranet(ローカルネットワークからのコード) | LocalIntranet |
Internet(インターネットからのコード) | Internet |
Trusted(信頼済みサイトからのコード) | Internet |
AllCode(すべてのマネージコード) | Nothing |
Restricted(制限付きサイトからのコード) | Nothing |
.Net Framework には 4 つのセキュリティポリシーのレベルが用意されていて、PolicyLevelType enum 型で指定します。
PolicyLevelType enum 型
定数 | 値 | ポリシーレベルの種類 |
---|---|---|
AppDomain | 3 | アプリケーション内のすべてのマネージコードに適用 |
Enterprise | 2 | エンタープライズ内(Active Directoryドメインに参加しているマシン)のすべてのマネージコードに適用 |
Machine | 1 | コンピュータ上で実行されるすべてのマネージコードに適用 |
User | 0 | ユーザーが実行するすべてのマネージコードに適用 |
Enterprise、Machine、User の場合、セキュリティポリシーは XML 形式のファイルから読み込まれます。AppDomain の場合は、プログラム内で明示的に設定しなければなりません。
Enterprise および User レベルには、1 つのコードグループがあり、すべてのアセンブリは AllCode ゾーンに割り当てられ、すべてのコードがすべてのリソースに制限なくアクセスできます。Machine レベルの場合、階層化したコードグループがあり、各コードグループごとにアクセス許可が与えられます。
CodeAccessSecurityAttribute クラスはコードアクセス関係の属性クラスの基本クラスです。
EnvironmentPermissionAttribute クラスは、環境変数へのアクセス許可属性を指定します。
FileIOPermissionAttribute クラスは、ファイルの入出力へのアクセス許可属性を指定します。
HostProtectionAttribute クラスは、SQL サーバーのように共通言語ランタイムをホストするホストへの許可属性を指定します。
PermissionSetAttribute クラスは、PermissionSet クラスを使用する代わりに、それと同じセキュリティ動作を指定する属性です。
PrincipalPermissionAttribute クラスは、プリンシパルへのアクセス許可属性を指定します。
SecurityAttribute クラスは、セキュリティ関係の属性クラスの基本クラスで、CodeAccessSecurityAttribute クラスは SecurityAttribute クラスから派生しています。
SecurityPermissionAttribute クラスは、SecurityPermission クラスを使用する代わりに、それと同じセキュリティ動作を指定する属性です。
UIPermissionAttribute クラスは、ユーザーインターフェースへのアクセス許可属性を指定します。
System.Security 名前空間に含まれる例外クラスを以下にリストアップします。その中心に位置するクラスは、SecurityException クラスです。
System.Security.Authentication.AuthenticationException System.Security.Authentication.InvalidCredentialException System.Security.Cryptography.CryptographicException System.Security.Cryptography.CryptographicUnexpectedOperationException System.Security.HostProtectionException System.Security.Policy.PolicyException System.Security.Principal.IdentityNotMappedException System.Security.SecurityException System.Security.VerificationException System.Security.XmlSyntaxException
System.UnauthorizedAccessException クラス
Visual Studio にはセキュリティ関係のツールが付属していますので、まず、それらをリストアップします。なお、.Net Framework 1.0/1.1 のときにはあったものが、.Net Framework 2.0 ではなくなったものもあります。ここでは .Net Framework 2.0 のものをリストアップします。
ツール名 | 場所 | 説明 | |
---|---|---|---|
Caspol.exe | 1 | "Code Access Security Policy" | セキュリティポリシーを見る、変更、設定する |
Cert2spc.exe | 2 | "Certificate to Software Publisher Certificate" | X.509 証明書から SPC (*1) を作成する |
Certmgr.exe | 2 | "Certificate Manager" | 証明書を CTL (*2) に組み込む |
Makecert.exe | 2 | "Make Certificate" | テスト用の X.5.9 証明書を作成する |
Permcalc.exe | 2 | "Permission Calculate" | アセンブリの実行に最小限必要なアクセス許可セットを計算する |
Peverify.exe | 2 | "PE Verify" | 実行ファイルのコードがタイプセーフかどうかをチェックする |
Secutil.exe | 2 | "Security Utility" | 証明書から公開キーを取り出し、書式化する |
Signtool.exe | 2 | "Sign Tool" | 実行ファイルにディジタル署名する |
Sn.exe | 2 | "Strong Name" | アセンブリに厳密名、ディジタル署名、名前空間名、バージョン情報を設定する |
mscorcfg.msc | 2 | - | .Net Framework 2.0 Configuration を表示・編集する |
*1 : SPC = "Software Publisher Certificate"
*2 : CTL = "Certificate Trust List"
[場所] はファイルを格納するディレクトリです。以下は .Net Framework 2.0 を標準の設定でインストールしている場合です。
1 = C:\Windows\Microsoft.NET\Framework\v2.0.50727 2 = C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin
セキュリティポリシーを見る、変更、設定するツールですが、多くの(多すぎる?)オプション設定が可能です。そこで、セキュリティに関係する主な機能について説明します。
構文:caspol -listdescription
出力結果の一部:
レベル = Machine コード グループ: 1. All_Code: このコード グループはアクセス許可を与えずに、コード グループ ツリー のルートを作成します。 1.1. My_Computer_Zone: コード グループにより、ローカル コンピュータで作成され るすべてのコードに完全信頼が与えられます。 1.1.1. Microsoft_Strong_Name: コード グループは、Microsoft strong name で 署名されたコードに完全信頼を与えます。 1.1.2. ECMA_Strong_Name: コード グループは、ECMA 準拠の厳密な名前で署名さ れたコードに完全信頼を与えます。 1.2. LocalIntranet_Zone: このコード グループにより、イントラネット アクセス許 可セットがイントラネット ゾーンのコードに与えられます。このアクセス許可セットに よって、イントラネット コードは、分離ストレージ、ユーザー インターフェイスへの完 全なアクセス、リフレクション機能の使用権、および一部の環境変数へのアクセスを与え られます。 .... ....
構文: caspol -listgroup
出力結果:
コード グループ: 1. All Code: Nothing 1.1. ゾーン - MyComputer: FullTrust 1.1.1. 厳密な名前 - 00240000048000009400000006020000002400005253413100040 0000100010007D1FA57C4AED9F0A32E84AA0FAEFD0DE9E8FD6AEC8F87FB03766C834C99921EB23BE 79AD9D5DCC1DD9AD236132102900B723CF980957FC4E177108FC607774F29E8320E92EA05ECE4E82 1C0A5EFE8F1645C4C0C93C1AB99285D622CAA652C1DFAD63D745D6F2DE5F17E5EAF0FC4963D261C8 A12436518206DC093344D5AD293: FullTrust 1.1.2. 厳密な名前 - 00000000000000000400000000000000: FullTrust 1.2. ゾーン - Intranet: LocalIntranet 1.2.1. All Code: 不明なオプションです: /{0} 1.2.2. All Code: 同じディレクトリ FileIO - 'Read, PathDiscovery' 1.3. ゾーン - Internet: Internet 1.3.1. All Code: 不明なオプションです: /{0} 1.4. ゾーン - Untrusted: Nothing 1.5. ゾーン - Trusted: Internet 1.5.1. All Code: 不明なオプションです: /{0}
構文: caspol -machine -listpset
出力結果の一部:
1. FullTrust (フル アクセスをすべてのリソースに許可します。) = <PermissionSet class="System.Security.NamedPermissionSet" version="1" Unrestrict ed="true" Name="FullTrust" Description="フル アクセスをすべてのリソースに許可し ます。" /> 2. SkipVerification (確認の省略を許可します。) = <PermissionSet class="System.Security.NamedPermissionSet" version="1" Name="Skip Verification" Description="確認の省略を許可します。"> <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib , Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SkipVerification" /> </PermissionSet> .... ....
構文: caspol -list
出力結果は量が多いので、省略する。
テスト用の X.509 証明書を作成するツールです。
Makecert.exeを使って証明書を作る時のもっとも簡単な構文は以下のとおり。
構文: makecert -n CN=Test test.cer
この構文で作成した内容をチェックしてみましょう。
private void button1_Click(object sender, EventArgs e) { X509Certificate2 cer = new X509Certificate2("test.cer"); textBox1.Text += String.Format("Archived: {0}\r\n", cer.Archived); textBox1.Text += String.Format("FriendlyName: {0}\r\n", cer.FriendlyName); textBox1.Text += String.Format("HasPrivateKey: {0}\r\n", cer.HasPrivateKey); textBox1.Text += String.Format("Issuer: {0}\r\n", cer.Issuer); textBox1.Text += String.Format("NotAfter: {0}\r\n", cer.NotAfter); textBox1.Text += String.Format("NotBefore: {0}\r\n", cer.NotBefore); textBox1.Text += String.Format("SerialNumber: {0}\r\n", cer.SerialNumber); textBox1.Text += String.Format("SignatureAlgorithm: {0}\r\n", cer.SignatureAlgorithm); textBox1.Text += String.Format("Subject: {0}\r\n", cer.Subject); textBox1.Text += String.Format("Thumbprint: {0}\r\n", cer.Thumbprint); textBox1.Text += String.Format("Version: {0}\r\n", cer.Version); }
実行結果:
Archived: False FriendlyName: HasPrivateKey: False Issuer: CN=Root Agency NotAfter: 2040/01/01 8:59:59 NotBefore: 2009/02/19 13:49:09 SerialNumber: F6D6F55065A8FAA94783BE07BEC10024 SignatureAlgorithm: System.Security.Cryptography.Oid Subject: CN=Test Thumbprint: 81DBAE1CE836F1AA5F78A6F6BA8D6069D95EAB45 Version: 3
.Net Framework 2.0 Configuration を表示・編集するツールで、ツールの中で数少ないビジュアルインターフェースを持つアプリケーションです。
以下は、[Tasks] の [コードグループプロパティの編集] を選択し、[メンバーシップ条件] のページを開いたところです。
見てのとおり、各項目には説明文が付いていますので、.Net Framework の解説よりも理解しやすいかもしれません。
厳密な名前でアセンブリに署名する場合、暗号化した公開キーと秘密キーとのペアが必要です。Sn は "Strong Name" の略で、公開キーと秘密キーとのペアを作成したあと、アセンブリに厳密名、デジタル署名、名前空間名、バージョン情報を設定します。
構文: sn -k test.snk
この構文で作成した test.snk のファイルのサイズは 596 バイトで、公開キー部分の 160 バイトと秘密キー部分の 436 バイトとで構成されています。秘密キー部分には秘密キーそのものと公開キーと関連付けるためのデータが格納されていると考えられますが、当然ながらどういうものかを知ることはできません。
構文: sn -p test.snk publickey.dat
公開キーのサイズはデフォルトで 128 バイト(1,024 ビット)です。以下は、このツールを使って取得した公開キー部分ですが、160 バイトのうち、最初の 32 バイトはどの公開キーでも共通のようです。それに続く 128 バイトが公開キーです。
00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00 00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00 0B EB 93 62 79 27 07 D0 7F EE B4 4C 2E 22 77 3A D7 B6 BD 15 A0 A6 F0 FB 48 C8 17 A1 E2 FF 9B 8F D8 6C 54 93 01 76 0F 96 9E 96 14 66 A5 51 31 3E 9F 44 A9 7C 53 F5 2F 0B 52 BB AC FB D8 90 5B F7 5E E6 26 B0 D3 F7 43 1D 41 C9 03 A9 F2 25 C4 2B DD 77 D0 34 EB 09 1D 26 C3 7E A3 62 69 E7 6F 9B F7 86 80 9E 80 C6 7B 23 A1 C4 3D 2C 1F AB 19 A8 5F 15 0B F8 0F FF 0D CD 97 95 32 F5 13 AA D6 E3
これは Visual Studio の機能を使えば簡単です。プロジェクトの [プロパティ]-[署名] のページで、[アセンブリの署名] にチェックを入れます。[厳密な名前のキーファイルを選択してください] のコンボボックスを開くと、[<新規作成...>] と [<参照...>] とがリストアップされます。既存のファイル(.snk ファイル)がない場合は、[<新規作成...>] を選択すると、下図で示すような別のダイアログボックスが表示されます。
キーファイルに .snk ファイル名を入力します。また、[キーファイルをパスワードで保護する] 場合は、パスワードを入力します。パスワードを設定する場合のキーファイル名の拡張子は .pfx となります。ちなみに、snk は "strong name key" の略だと思います。
.Net Framework の中では地味な扱いですが、結構便利なクラスかもしれません。というのは、.Net Framework のこのクラスの項にあるサンプルコードは独自のコードグループを作成する具体的な手順の解説にもなっています。
このサンプルコードは以下に示すような機能を含みます。
このサンプルコードは私が作ったコードではないので、日本化しただけですが、Windows フォームアプリケーションとして作り直してみました。以下をダウンロードして試してください。
SecurityManagerTest.lzh (48,398 bytes)
−以上−