解説インサイド .NET Framework第13回 コード・アクセス・セキュリティ(その4 後編) インフォテリア株式会社 |
JITコンパイル時のチェック
LinkDemandは、メソッドがJITコンパイルされるタイミングでアクセス許可チェックを行う仕組みだ。この仕組みはセキュリティ機能を向上させることはない。逆に、安全性が確認できる環境において、Demandに伴うパフォーマンスの低下を避けるのが、LinkDemandの目的だ。また、メソッドのJITコンパイルは、そのメソッドが最初に呼び出されようとするときに行われるので、InheritanceDemandと同様にLinkDemandも属性に設定することしかできない。
Demandでは、メソッドが実行されるたびに毎回コールスタックをすべてたどる必要がある。これはセキュリティを確保するためには必要な作業だが、パフォーマンスには悪影響を与える。アクセス許可の性質上、毎回チェックするのは明らかに冗長であり、最初に1回だけチェックすれば事足りる場合は、パフォーマンスを優先するためにLinkDemandを利用することができる。LinkDemandが指定されたアクセス許可は、JITコンパイル時にだけチェックされて、2回目以降の呼び出しでコンパイル済みのコードが実行されるときには行われない。また、LinkDemandのチェック対象は直接の呼び出し元だけに限定される。Demandではコールスタックをたどってすべてのアセンブリをチェックするが、LinkDemandでは直前のアセンブリしかチェックしない。
LinkDemandの有効な利用法の1つは、publicと指定されたクラスやメソッドの呼び出しを制限することだ。いまや、ほとんどの業務システムはチームで開発されている。.NET Frameworkはコンポーネント指向の環境といわれるとおり、アセンブリというコンポーネント単位で開発を進めることができる。だが、アセンブリは同時にアクセス境界でもあるため、アセンブリから見ると「チーム内」のコードからのアクセスなのか、全くの「部外者」からのアクセスなのかは識別できない。アセンブリの中でpublic宣言されているクラスは、チーム内からも部外者からもアクセス可能であり、internalと宣言されているクラスはチーム内からも部外者からもアクセスできない。つまりアセンブリを分割しておいて、しかも同じ開発プロジェクトからだけアクセスできるようにする方法はないように見える。
ここでLinkDemandを利用するのだ。例えば、次のようにpublicなクラスにLinkDemandが設定されたアクセス許可オブジェクトを属性として付ければ、指定された厳密名を持つアセンブリ(の中のコード)からのアクセスは認められるが、その厳密名を持たないアセンブリからのアクセスはセキュリティ違反となって拒否されることになるのだ。開発プロジェクトで同じキー・ペアを使っていれば、厳密名の公開キーはそれぞれのアセンブリで同じになるので、擬似的に「チーム内」のアクセスかどうかが識別できる。
|
|
LinkDemandが設定された属性により、publicなクラスにアクセス許可を設定した例 |
LinkDemandはJITコンパイル時に1回だけチェックされる。クラスやメソッドが初めてほかのコードからアクセスされたときに、コードはJITコンパイルされて実行可能になる。JITコンパイル後のコードには、Demandのような作業を伴うコードは残らないので、2回目以降の実行に影響することはない。
LinkDemandは、セキュリティよりもパフォーマンスを優先するための仕組みにほかならない。そして、セキュリティよりもパフォーマンスを優先してもよいシステムはそれほど多くない。従って、LinkDemandはできるだけ使わない方がよい。ほとんどの場合で、LinkDemandはセキュリティ上の効果を発揮することはできない。
Demandの動作を変更する
前回解説したとおり、Demandメソッドを呼び出すと、共通言語ランタイムはメソッドのコールスタックをたどり、呼び出しの連鎖に含まれているすべてのアセンブリをチェックする。これによって、アクセス許可のないコードがリソースにアクセスするのを防ぐことができる。
デフォルトでは、アクセス許可の有無は、ポリシーがアセンブリのエビデンスを処理した結果、出力したアクセス許可セットの中身によって確認される。しかし場合によっては、ポリシーでは許可されていても、特定の条件下ではアクセス許可を与えたくない場合がある。.NET Frameworkには、Demandの動作を変更して、アクセス許可のチェックをより厳しくする方法がある。それがPermitOnlyとDenyの役割だ。両方ともDemandと同様に、属性としてクラスやメソッドに指定することもできるし、コードの中で明示的に呼び出すこともできる。
例えば次のようなコードを実行すると、このコード以下のコードでは、たとえFullTrustのアクセス許可セットが割り当てられていたとしても、印刷操作を行うことができなくなる。
using System.Drawing.Printing;
using System.Security.Permissions;
PrintingPermission canPrint
= new PrintingPermission(PermissionState.Unrestricted);
canPrint.Deny();
Denyによるアクセス禁止が一時的なものである場合は、CodeAccessPermissionクラスのRevertDenyメソッドでDenyを取り消すことができる。このメソッドはstaticなので、クラスに対して直接呼び出す。
PrintingPermission.RevertDeny();
メソッド全体で印刷操作ができないようにする場合は、次のように属性で指定することもできる。
[PrintingPermissionAttribute(SecurityAction.Deny, Level=PrintingPermissionLevel.AllPrinting)]
Denyを利用すると、指定したアクセス許可(上記の場合は印刷)だけが与えられなくなり、指定されていないアクセス許可についてはポリシーでの解釈が有効になる。PermitOnlyを利用すると、反対に指定したアクセス許可だけが与えられるようになり、指定されていないアクセス許可はすべて与えられない状態になる。例えば、次のようにPermitOnlyメソッドを呼び出すと、プログラムから直接印刷できるのはデフォルトのプリンタだけになる。
PrintingPermission canPrint
= new PrintingPermission(PrintingPermissionLevel.DefaultPrinting);
canPrint.PermitOnly();
次のように属性で指定することもできる。
[PrintingPermissionAttribute(SecurityAction.PermitOnly, Level=PrintingPermissionLevel.DefaultPrinting)]
DenyとPermitOnlyは通常の開発で利用することはあまり多くはないと思われるものの、このような機能があることは知っておいてもいいだろう。
INDEX | ||
解説 インサイド .NET Framework | ||
第13回 コード・アクセス・セキュリティ(その4 後編) | ||
1.型のロード時におけるチェック | ||
2.JITコンパイル時のチェック | ||
3.アクセス許可を取り戻す | ||
4.アセンブリ・レベルでの宣言セキュリティ | ||
「解説:インサイド .NET Framework 」 |
- 第2回 簡潔なコーディングのために (2017/7/26)
ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている - 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう - 第1回 明瞭なコーディングのために (2017/7/19)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える - Presentation Translator (2017/7/18)
Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|