Assert メソッドの使用
Assert は、コード アクセス許可クラスおよび PermissionSet クラスに対して呼び出すことのできるメソッドです。 Assert を使用すると、コードには許可されているが、そのコードの呼び出し元には許可されていないアクションを、そのコード (およびその下位の呼び出し元) で実行できるようになります。 セキュリティ アサーションを行うと、ランタイムがセキュリティ チェック時に実行する通常の処理を変更できます。 アクセス許可をアサートすることにより、セキュリティ システムに対して、アサートされたアクセス許可についてはコードの呼び出し元チェックを実行しないように通知できます。
注意 |
---|
アサーションはセキュリティ ホールを作り出したり、セキュリティ上の制限を強制適用するためのランタイムの機構を弱体化させたりする可能性を伴うため、慎重に使用する必要があります。 |
アサーションは、ライブラリがアンマネージ コードを呼び出したり、そのライブラリ本来の用途とは関係のないアクセス許可を必要とする呼び出しを実行したりする場合に役立ちます。 たとえば、アンマネージ コードを呼び出すすべてのマネージ コードは、UnmanagedCode フラグを指定した SecurityPermission を設定する必要があります。 ローカル イントラネットからダウンロードしたコードなど、ローカル コンピューターから取得していないコードには、既定ではこのアクセス許可は付与されません。 そのため、ローカル イントラネットからダウンロードされた、アンマネージ コードを使用するライブラリを呼び出すことができるようにするには、そのコードにライブラリによってアサートされたアクセス許可を与える必要があります。 また、ライブラリの中には、呼び出し元にとっては未知であり、特別なアクセス許可を必要とする呼び出しを行うものもあります。
アサーションは、コードが呼び出し元には完全にわからない方法でリソースにアクセスするような状況にも使用できます。 たとえば、作成したライブラリがデータベースから情報を取得し、その過程でコンピューターのレジストリからも情報を読み取るとします。 そのライブラリを使用する開発者はソース コードへのアクセス権を持っていないため、そのライブラリを利用するコードで RegistryPermission を要求する必要があるかどうかはわかりません。 このような場合、ライブラリの呼び出し元がレジストリへのアクセス許可を持つことを要求することが妥当ではない、またはその必要はないと判断した場合は、レジストリを読み取るために必要なアクセス許可をアサートできます。 この状況では、このアクセス許可をライブラリでアサートすることによって、RegistryPermission を与えられていない呼び出し元でもライブラリを使用できるようにした方が適切です。
アサーションがスタック ウォークに影響するのは、アサートされたアクセス許可と、下位の呼び出し元によって要求されるアクセス許可とが同じ種類であり、さらに要求されたアクセス許可が、アサートされたアクセス許可のサブセットである場合に限られます。 たとえば、C ドライブ上のすべてのファイルを読み取れるように FileIOPermission をアサートした場合に、下位で C:\Temp のファイルを読み取るために FileIOPermission が要求されたときには、このアサーションがスタック ウォークに影響を与える可能性がありますが、FileIOPermission の要求が C ドライブに書き込むために行われる場合には、このアサーションによる影響はありません。
アサーションを実行するには、アサート対象のアクセス許可と、アサーションを行う権限を表す SecurityPermission の両方をコードに与える必要があります。 コードに与えられていないアクセス許可をアサートすることもできますが、セキュリティ チェックがアサーションによって成功に至る前に失敗してしまうため、このアサーションは無効になります。
Assert を使用した場合に実行される処理を次の図に示します。 この図では、アセンブリ A、B、C、E、F、および 2 つのアクセス許可 P1 および P1A について、次のことが当てはまります。
P1A は、C ドライブ上の .txt ファイルを読み取る権限を表しています。
P1 は、C ドライブ上のすべてのファイルを読み取る権限を表しています。
P1A と P1 は両方とも FileIOPermission 型であり、P1A は P1 のサブセットです。
アセンブリ E および F には、アクセス許可 P1A が与えられています。
アセンブリ C には、アクセス許可 P1 が与えられています。
アセンブリ A および B には、アクセス許可 P1 も P1A も与えられていません。
メソッド A はアセンブリ A に含まれており、メソッド B はアセンブリ B に含まれています。メソッド C 以下も同様です。
Assert の使い方
このシナリオでは、メソッド A が B を呼び出し、B が C を呼び出し、C が E を呼び出し、E が F を呼び出します。 メソッド C は C ドライブ上のファイルを読み取る権限 (アクセス許可 P1) をアサートし、メソッド E は C ドライブ上の .txt ファイルを読み取る権限 (アクセス許可 P1A) を要求します。 実行時に F で要求が検出されると、スタック ウォークが実行されて F のすべての呼び出し元のアクセス許可が E から順にチェックされます。 E にはアクセス許可 P1A が与えられているため、スタック ウォークは次に C のアクセス許可をチェックし、C のアサーションを検出します。 要求されたアクセス許可 (P1A) はアサートされたアクセス許可 (P1) のサブセットなので、スタック ウォークは中止され、セキュリティ チェックはそのまま成功します。 アセンブリ A および B にアクセス許可 P1A が与えられていないことは、問題にはなりません。 P1 をアサートすることによって、メソッド C は P1 によって保護されているリソースへのアクセス権が呼び出し元に与えられていない場合でも、そのリソースにそれらの呼び出し元からアクセスできるようにしています。
保護されているリソースにアクセスするクラス ライブラリおよびクラスをデザインする場合は、多くの場合、クラスの呼び出し元が適切なアクセス許可を持っていることを要求するために、セキュリティ確認要求を実行する必要があります。 また、クラスが実行する操作について、呼び出し元のほとんどがアクセス許可を持っていないことがわかっていても、それらの呼び出し元がコードを呼び出すことができるようにする場合は、そのコードが実行する操作を表すアクセス許可オブジェクトに対して Assert メソッドを呼び出し、アクセス許可をアサートします。 この方法で Assert を使用すると、通常ならコードを呼び出せない呼び出し元が、そのコードを呼び出すことができるようになります。 そのため、アクセス許可をアサートする場合は、コンポーネントが悪用されることを防ぐため、事前に適切なセキュリティ チェックを実行する必要があります。
たとえば、高いレベルで信頼されたライブラリ クラスに、ファイルを削除するメソッドが含まれているとします。 このメソッドは、アンマネージ Win32 関数を呼び出してファイルにアクセスします。 このメソッドの呼び出し元は、削除するファイルの名前として、たとえば C:\Test.txt を渡して、コード内の Delete メソッドを呼び出します。 Delete メソッドの内部では、C:\Test.txt への書き込みアクセス権を表す FileIOPermission オブジェクトが作成されます。 書き込みアクセス権はファイルを削除するために必要です。 次に、FileIOPermission オブジェクトの Demand メソッドが呼び出され、強制セキュリティ チェックが実行されます。 コール スタック内に、このアクセス許可を持っていない呼び出し元があった場合は、SecurityException がスローされます。 例外がスローされない場合は、すべての呼び出し元が C:\Test.txt へのアクセス権を持っていることがわかります。 呼び出し元のほとんどがアンマネージ コードへのアクセス許可を持っていないことが想定されるため、コードはアンマネージ コードを呼び出す権限を表す SecurityPermission オブジェクトを作成し、そのオブジェクトの Assert メソッドを呼び出します。 最後に、アンマネージ Win32 関数が呼び出されて C:\Text.txt を削除し、呼び出し元に制御が返されます。
注意 |
---|
作成したコードが、アクセス許可によって保護されているリソースにアクセスするために他のコードから利用される可能性がある場合には、コードではそのアクセス許可をアサートしないようにする必要があります。たとえば、呼び出し元がパラメーターで名前を指定したファイルに書き込みを行うコードでは、そのコードが第三者によって悪用されることを防ぐため、ファイルに書き込むための FileIOPermission をアサートすることは避ける必要があります。 |
強制セキュリティ構文を使用する場合、同じメソッド内で複数のアクセス許可に対して Assert メソッドを呼び出すと、セキュリティ例外がスローされます。 代わりに、PermissionSet オブジェクトを作成し、呼び出す対象の個々のアクセス許可を渡してから、その PermissionSet オブジェクトに対して Assert メソッドを呼び出します。 宣言セキュリティ構文を使用する場合は、Assert メソッドを複数回呼び出すことができます。
Assert メソッドを使用してセキュリティ チェックをオーバーライドするための宣言構文の例を次に示します。 FileIOPermissionAttribute の構文は、SecurityAction 列挙値と、アクセス許可を与えるファイルまたはディレクトリの場所という 2 つの値を受け取っています。 Assert を呼び出すと、C:\Log.txt へのアクセス許可の有無について呼び出し元がチェックされなくても、このファイルへのアクセス許可に対する確認要求が成功するようになります。
[Visual Basic]
Option Explicit
Option Strict
Imports System
Imports System.IO
Imports System.Security.Permissions
Namespace LogUtil
Public Class Log
Public Sub New()
End Sub
<FileIOPermission(SecurityAction.Assert, All := "C:\Log.txt")> Public Sub
MakeLog()
Dim TextStream As New StreamWriter("C:\Log.txt")
TextStream.WriteLine("This Log was created on {0}", DateTime.Now) '
TextStream.Close()
End Sub
End Class
End Namespace
namespace LogUtil
{
using System;
using System.IO;
using System.Security.Permissions;
public class Log
{
public Log()
{
}
[FileIOPermission(SecurityAction.Assert, All = @"C:\Log.txt")]
public void MakeLog()
{
StreamWriter TextStream = new StreamWriter(@"C:\Log.txt");
TextStream.WriteLine("This Log was created on {0}", DateTime.Now);
TextStream.Close();
}
}
}
Assert メソッドを使用してセキュリティ チェックをオーバーライドするための強制構文の例を次のコード片に示します。 この例では、FileIOPermission オブジェクトのインスタンスが宣言されています。 そのコンストラクターには、許可されるアクセスの種類を定義する FileIOPermissionAccess.AllAccess と、ファイルの場所を示す文字列が渡されます。 FileIOPermission オブジェクトが定義されたら、そのオブジェクトに対して Assert メソッドを呼び出すだけで、セキュリティ チェックをオーバーライドできます。
[Visual Basic]
Option Explicit
Option Strict
Imports System
Imports System.IO
Imports System.Security.Permissions
Namespace LogUtil
Public Class Log
Public Sub New()
End Sub 'New
Public Sub MakeLog()
Dim FilePermission As New FileIOPermission(FileIOPermissionAccess.AllAccess, "C:\Log.txt")
FilePermission.Assert()
Dim TextStream As New StreamWriter("C:\Log.txt")
TextStream.WriteLine("This Log was created on {0}", DateTime.Now)
TextStream.Close()
End Sub
End Class
End Namespace
namespace LogUtil
{
using System;
using System.IO;
using System.Security.Permissions;
public class Log
{
public Log()
{
}
public void MakeLog()
{
FileIOPermission FilePermission = new FileIOPermission(FileIOPermissionAccess.AllAccess,@"C:\Log.txt");
FilePermission.Assert();
StreamWriter TextStream = new StreamWriter(@"C:\Log.txt");
TextStream.WriteLine("This Log was created on {0}", DateTime.Now);
TextStream.Close();
}
}
}