チュートリアル: 部分信頼シナリオにおけるコード出力
リフレクション出力は、完全信頼または部分信頼において同じ API セットを使用しますが、部分的に信頼されるコードでは実行する機能によって特定のアクセス許可が必要になります。 リフレクション出力には、匿名でホストされる動的メソッドという機能があります。この機能は、透過的セキュリティ アセンブリによって部分信頼で使用されます。
メモ |
---|
.NET Framework Version 3.5 以前では、コード出力を行うには ReflectionPermission に ReflectionPermissionFlag.ReflectionEmit フラグを指定する必要がありました。このアクセス許可は、既定で FullTrust および Intranet の名前付きアクセス許可セットには含まれますが、Internet アクセス許可セットには含まれません。したがって、ライブラリを部分信頼で使用するには、SecurityCriticalAttribute 属性を設定し、ReflectionEmit に対して Assert メソッドを実行する必要がありました。このようなライブラリでは、コーディング エラーがあるとセキュリティ ホールが発生するおそれがあるため、セキュリティを慎重に確認する必要があります。コードの生成は本質的に特権を必要とする操作ではないため、.NET Framework 3.5 はセキュリティ確認要求を発行せずに部分信頼シナリオでコードを出力できます。これは、生成されたコードには、コードを出力したアセンブリと同等以下のアクセス許可しかないことを意味します。これにより、コードを出力するライブラリは透過的セキュリティになるため、ReflectionEmit を要求する必要がなくなります。つまり、安全なライブラリを記述するためにセキュリティを入念に確認する必要がなくなります。 |
このチュートリアルでは、次の作業について説明します。
部分的に信頼されたコードのテスト用に単純なサンドボックスを設定する
重要 これは、部分信頼でコードを試す簡単な方法です。実際に信頼できない場所から取得されたコードを実行する方法については、「方法 : サンドボックスで部分信頼コードを実行する」を参照してください。
部分的に信頼されたアプリケーション ドメインでコードを実行する
匿名でホストされる動的メソッドを使用して部分信頼でコードを出力し実行する
部分信頼シナリオでのコード出力の詳細については、「リフレクション出力のセキュリティ関連事項」を参照してください。
ここで説明する手順に示すコードの完全な一覧については、このチュートリアルの最後の「例」を参照してください。
部分的に信頼された場所を設定する
次の 2 つの手順では、部分信頼でテストできるコードの場所を設定する方法を説明します。
最初の手順では、サンドボックス化されたアプリケーション ドメインを作成する方法について説明します。このドメインでは、インターネット アクセス許可がコードに付与されます。
2 番目の手順では、部分的に信頼されたアプリケーション ドメインに対し、ReflectionPermissionFlag.RestrictedMemberAccess フラグを設定して ReflectionPermission を追加する方法を説明します。これにより、信頼レベルが同等以下のアセンブリでプライベート データにアクセスできるようになります。
サンドボックス化されたアプリケーション ドメインを作成する
部分信頼でアセンブリを実行するアプリケーション ドメインを作成するには、AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) メソッド オーバーロードを使用して、アセンブリに付与するアクセス許可セットを指定する必要があります。 許可セットを指定するための最も簡単な方法として、セキュリティ ポリシーから名前付きアクセス許可セットを取得する方法があります。
次の手順では、部分信頼でコードを実行するサンドボックス化されたアプリケーション ドメインを作成し、出力されるコードが、パブリック型のパブリック メンバーにのみアクセスできるというシナリオをテストします。 後続の手順では、RestrictedMemberAccess を追加する方法を説明し、出力されるコードが、同等以下のアクセス許可を付与されたアセンブリ内にある非パブリックな型とメンバーにアクセスできるというシナリオをテストします。
部分信頼でアプリケーション ドメインを作成するには
サンドボックス化されたアプリケーション ドメイン内のアセンブリに付与するアクセス許可セットを作成します。 ここでは、インターネット ゾーンのアクセス許可セットを使用します。
Dim ev As New Evidence() ev.AddHostEvidence(new Zone(SecurityZone.Internet)) Dim pset As New NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev))
Evidence ev = new Evidence(); ev.AddHostEvidence(new Zone(SecurityZone.Internet)); PermissionSet pset = new NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev));
アプリケーション ドメインをアプリケーション パスで初期化するため、AppDomainSetup オブジェクトを作成します。
重要 単純にするため、このコード例では現在のフォルダーを使用します。実際にインターネットから取得されたコードを実行する場合は、信頼できないコード用の別のフォルダーを使用してください。詳細については、「方法 : サンドボックスで部分信頼コードを実行する」を参照してください。
Dim adSetup As New AppDomainSetup() adSetup.ApplicationBase = "."
AppDomainSetup adSetup = new AppDomainSetup(); adSetup.ApplicationBase = ".";
アプリケーション ドメインで実行されるすべてのアセンブリについて、アプリケーション ドメイン設定情報、および許可セットを指定して、アプリケーション ドメインを作成します。
Dim ad As AppDomain = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, Nothing)
AppDomain ad = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, null);
AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) メソッド オーバーロードの最後のパラメーターを使用すると、アプリケーション ドメインの許可セットではなく、完全な信頼が付与されたアセンブリのセットを指定できます。 これらのアセンブリはグローバル アセンブリ キャッシュに存在するため、アプリケーションが使用する .NET Framework アセンブリを指定する必要はありません。 グローバル アセンブリ キャッシュにあるアセンブリは、常に完全に信頼されます。 このパラメーターを使用して、グローバル アセンブリ キャッシュには存在しない厳密名のアセンブリを指定できます。
サンドボックス化されたドメインに RestrictedMemberAccess を追加する
ホスト アプリケーションは、匿名でホストされる動的メソッドが、コードを出力するアセンブリの信頼レベルと同等以下の信頼レベルが設定されたアセンブリ内のプライベート データにアクセスすることを許可できます。 この制限付き機能を有効にして Just-In-Time (JIT) 参照範囲チェックをスキップするため、ホスト アプリケーションは、許可セットに対し、ReflectionPermissionFlag.RestrictedMemberAccess (RMA) フラグを設定した ReflectionPermission オブジェクトを追加します。
たとえば、ホストはインターネット アプリケーションに対し、RMA を設定した Internet アクセス許可を付与し、インターネット アプリケーションが独自のアセンブリ内にあるプライベート データにアクセスするコードを出力できるようにすることが可能です。 このアクセスは、信頼レベルが同等以下であるアセンブリに限定されるため、インターネット アプリケーションは、.NET Framework アセンブリのような完全に信頼されたアセンブリのメンバーにはアクセスできません。
メモ |
---|
特権の昇格を回避するため、匿名でホストされる動的メソッドの作成時にはアセンブリ出力の履歴情報が含まれます。メソッドの呼び出し時に履歴情報がチェックされるため、完全に信頼されたコードから呼び出された、匿名でホストされる動的メソッドは、依然として出力アセンブリの信頼レベルに制限されます。 |
RMA を設定した部分信頼でアプリケーション ドメインを作成するには
RestrictedMemberAccess (RMA) フラグを指定した新しい ReflectionPermission オブジェクトを作成し、PermissionSet.SetPermission メソッドを使用して、許可セットにアクセス許可を追加します。
pset.SetPermission( _ New ReflectionPermission( _ ReflectionPermissionFlag.RestrictedMemberAccess))
pset.SetPermission( new ReflectionPermission( ReflectionPermissionFlag.RestrictedMemberAccess));
許可セットにアクセス許可が含まれていない場合、AddPermission メソッドによって、許可セットにアクセス許可が追加されます。 許可セットにアクセス許可が含まれている場合は、既存のアクセス許可に指定フラグが追加されます。
メモ RMA は、匿名でホストされる動的メソッドの 1 つの機能です。通常の動的メソッドが JIT 参照範囲チェックをスキップした場合、出力コードには完全信頼が必要です。
アプリケーション ドメイン設定情報、および許可セットを指定して、アプリケーション ドメインを作成します。
ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, Nothing)
ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, null);
サンドボックス化されたアプリケーション ドメインでコードを実行する
次の手順では、アプリケーション ドメインで実行可能なメソッドを使用してクラスを定義する方法、そのドメインでクラスのインスタンスを作成する方法、そのメソッドを実行する方法について説明します。
アプリケーション ドメインでメソッドを定義して実行するには
MarshalByRefObject から派生するクラスを定義します。 これにより、他のアプリケーション ドメインにクラスのインスタンスを作成し、アプリケーション ドメインの境界を越えてメソッドを呼び出すことができます。 この例のクラスの名前は Worker です。
Public Class Worker Inherits MarshalByRefObject
public class Worker : MarshalByRefObject {
実行するコードを含むパブリック メソッドを定義します。 この例では、コードは単純な動的メソッドを出力し、メソッドを実行するためのデリゲートを作成して、デリゲートを呼び出します。
Public Sub SimpleEmitDemo() Dim meth As DynamicMethod = new DynamicMethod("", Nothing, Nothing) Dim il As ILGenerator = meth.GetILGenerator() il.EmitWriteLine("Hello, World!") il.Emit(OpCodes.Ret) Dim t1 As Test1 = CType(meth.CreateDelegate(GetType(Test1)), Test1) t1() End Sub
public void SimpleEmitDemo() { DynamicMethod meth = new DynamicMethod("", null, null); ILGenerator il = meth.GetILGenerator(); il.EmitWriteLine("Hello, World!"); il.Emit(OpCodes.Ret); Test1 t1 = (Test1) meth.CreateDelegate(typeof(Test1)); t1(); }
メイン プログラムで、アセンブリの表示名を取得します。 この名前は、サンドボックス化されたアプリケーション ドメインで Worker クラスのインスタンスを作成するときに使用します。
Dim asmName As String = [Assembly].GetExecutingAssembly().FullName
String asmName = Assembly.GetExecutingAssembly().FullName;
メイン プログラムでは、このチュートリアルの最初の手順で説明したとおり、サンドボックス化されたアプリケーション ドメインを作成します。 SimpleEmitDemo メソッドはパブリック メソッドのみを使用するため、Internet アクセス許可セットに任意のアクセス許可を追加しないでください。
メイン プログラムで、サンドボックス化されたアプリケーション ドメインに Worker クラスのインスタンスを作成します。
Dim w As Worker = _ CType(ad.CreateInstanceAndUnwrap(asmName, "Worker"), Worker)
Worker w = (Worker) ad.CreateInstanceAndUnwrap(asmName, "Worker");
CreateInstanceAndUnwrap メソッドにより、対象のアプリケーション ドメイン内にオブジェクトが作成され、オブジェクトのプロパティおよびメソッドの呼び出しに使用できるプロキシが返されます。
メモ このコードを Visual Studio で使用する場合は、名前空間を含むようにクラスの名前を変更する必要があります。既定では、名前空間がプロジェクト名になります。たとえば、プロジェクト名が "PartialTrust" である場合、クラス名は "PartialTrust.Worker" にする必要があります。
SimpleEmitDemo メソッドを呼び出すコードを追加します。 呼び出しはアプリケーションのドメイン境界を越えてマーシャリングされ、コードはサンドボックス化されたアプリケーション ドメインで実行されます。
w.SimpleEmitDemo()
w.SimpleEmitDemo();
匿名でホストされる動的メソッドを使用する
匿名でホストされる動的メソッドは、システムが提供する透過的なアセンブリに関連付けられます。 そのため、これらのメッソドに含まれるコードは透過的になります。 それに対し、通常の動的メソッドは、直接指定されるか関連付けられた型から推論されるかに関係なく、既存のモジュールに関連付ける必要があり、そのモジュールからセキュリティ レベルを引き継ぎます。
メモ |
---|
次の手順では、匿名のホストを提供するアセンブリに動的メソッドを関連付ける唯一の方法であるコンストラクターの使用法について説明します。匿名のホスト アセンブリでモジュールを明示的に指定することはできません。 |
通常の動的メソッドは、関連付けられているモジュールの内部メンバーまたは関連付けられている型のプライベート メンバーにアクセスできます。 匿名でホストされる動的メソッドは他のコードから分離されているため、プライベート データにアクセスすることはできません。 ただし、JIT 参照範囲チェックをスキップしてプライベート データにアクセスするための、制限付き機能が設定されています。 この機能が設定されるのは、コードを出力するアセンブリの信頼レベルと同等以下の信頼レベルが設定されたアセンブリに限定されます。
特権の昇格を回避するため、匿名でホストされる動的メソッドの作成時にはアセンブリ出力の履歴情報が含まれます。 メソッドの呼び出し時に履歴情報がチェックされるため、 完全に信頼されたコードから呼び出された、匿名でホストされる動的メソッドは、依然として出力アセンブリの信頼レベルに制限されます。
匿名でホストされる動的メソッドを使用するには
関連付けられたモジュールや型を指定しないコンストラクターを使用して、匿名でホストされる動的メソッドを作成します。
Dim meth As DynamicMethod = new DynamicMethod("", Nothing, Nothing) Dim il As ILGenerator = meth.GetILGenerator() il.EmitWriteLine("Hello, World!") il.Emit(OpCodes.Ret)
DynamicMethod meth = new DynamicMethod("", null, null); ILGenerator il = meth.GetILGenerator(); il.EmitWriteLine("Hello, World!"); il.Emit(OpCodes.Ret);
匿名でホストされる動的メソッドが使用するのがパブリック型とパブリック メソッドのみである場合、メンバー アクセスを制限する必要はなく、JIT 参照範囲チェックをスキップする必要もありません。
動的メソッドの出力に必要なアクセス許可はありませんが、出力コードには、そのコードが使用する型やメソッドに応じたアクセス許可が必要です。 たとえば、ファイルにアクセスするメソッドを呼び出す出力コードには、FileIOPermission が必要です。 信頼レベルに該当のアクセス許可が含まれていないと、出力コードの実行時にセキュリティ例外がスローされます。 ここでは、Console.WriteLine メソッドのみを使用する動的メソッドを出力するコードを示します。 このコードは、部分的に信頼された場所から実行できます。
または、JIT 参照範囲チェックをスキップするために機能を制限した、匿名でホストされる動的メソッドを作成します。これを行うには、DynamicMethod(String, Type, Type[], Boolean) コンストラクターを使用して、restrictedSkipVisibility パラメーターに true を指定します。
Dim meth As New DynamicMethod("", _ GetType(Char), _ New Type() {GetType(String)}, _ True)
DynamicMethod meth = new DynamicMethod("", typeof(char), new Type[] { typeof(String) }, true);
ここでの制限は、匿名でホストされる動的メソッドがアクセスできるプライベート データは、出力アセンブリの信頼レベルと同等以下の信頼レベルが設定されたアセンブリ内にあるプライベート データに限定されることを意味します。 たとえば、動的メソッドをインターネット信頼で実行している場合、同様にインターネット信頼で実行している他のアセンブリ内にあるプライベート データにはアクセスできますが、.NET Framework アセンブリのプライベート データにはアクセスできません。 .NET Framework アセンブリは、グローバル アセンブリ キャッシュにインストールされているもので、常に完全に信頼されます。
匿名でホストされる動的メソッドは、ホスト アプリケーションが ReflectionPermissionFlag.RestrictedMemberAccess フラグが設定された ReflectionPermission を付与している場合のみ、JIT 参照範囲チェックをスキップして制限付き機能を使用できます。 このアクセス許可は、メソッドの呼び出し時に要求されます。
メモ 出力アセンブリの呼び出し履歴情報は、動的メソッドの作成時に含まれます。したがって、この要求は、メソッドを呼び出すアセンブリではなく、出力アセンブリのアクセス許可に対して発行されます。これにより、昇格されたアクセス権で出力コードが実行されることを回避します。
制限付きメンバー アクセスの使用と制約については、このチュートリアルの最後の「完全なコード例」に示します。 Worker クラスには、参照範囲チェックをスキップするための制限付き機能の有無に関係なく匿名でホストされる動的メソッドを作成できるメソッドが含まれます。例では、異なる信頼レベルを設定したアプリケーション ドメインでこのメソッドを実行した結果も示します。
メモ 参照範囲チェックをスキップするための制限付き機能は、匿名でホストされる動的メソッドの 1 つの機能です。通常の動的メソッドが JIT 参照範囲チェックをスキップした場合、完全信頼が与えられている必要があります。
例
説明
RestrictedMemberAccess フラグを使用して、匿名でホストされる動的メソッドが JIT の参照範囲チェックをスキップできるようにするコード例を次に示します。ただし、対象のメンバーの信頼レベルは、コードを出力するアセンブリ以下であることを前提とします。
この例では、アプリケーション ドメインの境界を越えてマーシャリングできる Worker クラスを定義します。 クラスには、動的メソッドを出力して実行する、AccessPrivateMethod メソッド オーバーロードが 2 つあります。 最初のオーバーロードは、Worker クラスのプライベート PrivateMethod メソッドを呼び出す動的メソッドを出力します。これは、JIT の参照範囲チェックを実行するかどうかにかかわらず、動的メソッドを出力できます。 2 番目のオーバーロードは、String クラスの internal プロパティ (Visual Basic では Friend プロパティ) にアクセスする動的メソッドを出力します。
この例では、ヘルパー メソッドを使用して、Internet アクセス許可に制限される許可セットを作成します。次に、AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) メソッド オーバーロードを使用してアプリケーション ドメインを作成し、この許可セットを使用するドメインで実行するすべてのコードを指定します。 アプリケーション ドメインに Worker クラスのインスタンスを作成し、AccessPrivateMethod メソッドを 2 回実行します。
AccessPrivateMethod メソッドを初めて実行すると、JIT の参照範囲チェックが強制的に実行されます。 動的メソッドは、JIT の参照範囲チェックによってプライベート メソッドにアクセスできなくなるため、呼び出し時に失敗します。
AccessPrivateMethod メソッドの 2 回目の実行では、JIT 参照範囲チェックをスキップします。 Internet 許可セットでは、参照範囲チェックをスキップするのに十分なアクセス許可が付与されていないため、動的メソッドはコンパイル時に失敗します。
この例では、ReflectionPermissionFlag.RestrictedMemberAccess を指定した ReflectionPermission を許可セットに追加します。 次に、2 番目のドメインを作成し、新しい許可セットにアクセス許可が付与されるドメインで実行するすべてのコードを指定します。 新しいアプリケーション ドメインに Worker クラスのインスタンスを作成し、AccessPrivateMethod メソッドの両方のオーバーロードを実行します。
AccessPrivateMethod メソッドの最初のオーバーロードが実行され、JIT の参照範囲チェックがスキップされます。 コードを出力するアセンブリがプライベート メソッドを含むアセンブリと同じであるため、動的メソッドのコンパイルと実行は成功します。 したがって、信頼レベルは同等です。 Worker クラスを含むアプリケーションに複数のアセンブリが存在する場合、これらは同じ信頼レベルにあるため、どのアセンブリでも同じプロセスが成功します。
AccessPrivateMethod メソッドの 2 番目のオーバーロードが実行され、JIT の参照範囲チェックは再度スキップされます。 String クラスの internal FirstChar プロパティへのアクセスを試行するため、ここでは、動的メソッドはコンパイル時に失敗します。 String クラスを含むアセンブリは完全に信頼されます。 したがって、コードを出力するアセンブリよりも信頼レベルが高くなります。
この比較により、ReflectionPermissionFlag.RestrictedMemberAccess によって、信頼されたコードのセキュリティを損なうことなく、部分的に信頼されたコードが、部分的に信頼された他のコードの参照範囲チェックをスキップできるようにする方法を示します。
コード
Imports System
Imports System.Reflection.Emit
Imports System.Reflection
Imports System.Security
Imports System.Security.Permissions
Imports System.Security.Policy
Imports System.Collections
Imports System.Diagnostics
' This code example works properly only if it is run from a fully
' trusted location, such as your local computer.
' Delegates used to execute the dynamic methods.
'
Public Delegate Sub Test(ByVal w As Worker)
Public Delegate Sub Test1()
Public Delegate Function Test2(ByVal instance As String) As Char
' The Worker class must inherit MarshalByRefObject so that its public
' methods can be invoked across application domain boundaries.
'
Public Class Worker
Inherits MarshalByRefObject
Private Sub PrivateMethod()
Console.WriteLine("Worker.PrivateMethod()")
End Sub
Public Sub SimpleEmitDemo()
Dim meth As DynamicMethod = new DynamicMethod("", Nothing, Nothing)
Dim il As ILGenerator = meth.GetILGenerator()
il.EmitWriteLine("Hello, World!")
il.Emit(OpCodes.Ret)
Dim t1 As Test1 = CType(meth.CreateDelegate(GetType(Test1)), Test1)
t1()
End Sub
' This overload of AccessPrivateMethod emits a dynamic method and
' specifies whether to skip JIT visiblity checks. It creates a
' delegate for the method and invokes the delegate. The dynamic
' method calls a private method of the Worker class.
Overloads Public Sub AccessPrivateMethod( _
ByVal restrictedSkipVisibility As Boolean)
' Create an unnamed dynamic method that has no return type,
' takes one parameter of type Worker, and optionally skips JIT
' visiblity checks.
Dim meth As New DynamicMethod("", _
Nothing, _
New Type() { GetType(Worker) }, _
restrictedSkipVisibility)
' Get a MethodInfo for the private method.
Dim pvtMeth As MethodInfo = GetType(Worker).GetMethod( _
"PrivateMethod", _
BindingFlags.NonPublic Or BindingFlags.Instance)
' Get an ILGenerator and emit a body for the dynamic method.
Dim il As ILGenerator = meth.GetILGenerator()
' Load the first argument, which is the target instance, onto the
' execution stack, call the private method, and return.
il.Emit(OpCodes.Ldarg_0)
il.EmitCall(OpCodes.Call, pvtMeth, Nothing)
il.Emit(OpCodes.Ret)
' Create a delegate that represents the dynamic method, and
' invoke it.
Try
Dim t As Test = CType(meth.CreateDelegate(GetType(Test)), Test)
Try
t(Me)
Catch ex As Exception
Console.WriteLine("{0} was thrown when the delegate was invoked.", _
ex.GetType().Name)
End Try
Catch ex As Exception
Console.WriteLine("{0} was thrown when the delegate was compiled.", _
ex.GetType().Name)
End Try
End Sub
' This overload of AccessPrivateMethod emits a dynamic method that takes
' a string and returns the first character, using a private field of the
' String class. The dynamic method skips JIT visiblity checks.
Overloads Public Sub AccessPrivateMethod()
Dim meth As New DynamicMethod("", _
GetType(Char), _
New Type() {GetType(String)}, _
True)
' Get a MethodInfo for the 'get' accessor of the private property.
Dim pi As PropertyInfo = GetType(String).GetProperty( _
"FirstChar", _
BindingFlags.NonPublic Or BindingFlags.Instance)
Dim pvtMeth As MethodInfo = pi.GetGetMethod(True)
' Get an ILGenerator and emit a body for the dynamic method.
Dim il As ILGenerator = meth.GetILGenerator()
' Load the first argument, which is the target string, onto the
' execution stack, call the 'get' accessor to put the result onto
' the execution stack, and return.
il.Emit(OpCodes.Ldarg_0)
il.EmitCall(OpCodes.Call, pvtMeth, Nothing)
il.Emit(OpCodes.Ret)
' Create a delegate that represents the dynamic method, and
' invoke it.
Try
Dim t As Test2 = CType(meth.CreateDelegate(GetType(Test2)), Test2)
Dim first As Char = t("Hello, World!")
Console.WriteLine("{0} is the first character.", first)
Catch ex As Exception
Console.WriteLine("{0} was thrown when the delegate was compiled.", _
ex.GetType().Name)
End Try
End Sub
End Class
Friend Class Example
' The entry point for the code example.
Shared Sub Main()
' Get the display name of the executing assembly, to use when
' creating objects to run code in application domains.
Dim asmName As String = [Assembly].GetExecutingAssembly().FullName
' Create the permission set to grant to other assemblies. In this
' case they are the permissions found in the Internet zone.
Dim ev As New Evidence()
ev.AddHostEvidence(new Zone(SecurityZone.Internet))
Dim pset As New NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev))
' For simplicity, set up the application domain to use the
' current path as the application folder, so the same executable
' can be used in both trusted and untrusted scenarios. Normally
' you would not do this with real untrusted code.
Dim adSetup As New AppDomainSetup()
adSetup.ApplicationBase = "."
' Create an application domain in which all code that executes is
' granted the permissions of an application run from the Internet.
Dim ad As AppDomain = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, Nothing)
' Create an instance of the Worker class in the partially trusted
' domain. Note: If you build this code example in Visual Studio,
' you must change the name of the class to include the default
' namespace, which is the project name. For example, if the project
' is "AnonymouslyHosted", the class is "AnonymouslyHosted.Worker".
Dim w As Worker = _
CType(ad.CreateInstanceAndUnwrap(asmName, "Worker"), Worker)
' Emit a simple dynamic method that prints "Hello, World!"
w.SimpleEmitDemo()
' Emit and invoke a dynamic method that calls a private method
' of Worker, with JIT visibility checks enforced. The call fails
' when the delegate is invoked.
w.AccessPrivateMethod(False)
' Emit and invoke a dynamic method that calls a private method
' of Worker, skipping JIT visibility checks. The call fails when
' the method is compiled.
w.AccessPrivateMethod(True)
' Unload the application domain. Add RestrictedMemberAccess to the
' grant set, and use it to create an application domain in which
' partially trusted code can call private members, as long as the
' trust level of those members is equal to or lower than the trust
' level of the partially trusted code.
AppDomain.Unload(ad)
pset.SetPermission( _
New ReflectionPermission( _
ReflectionPermissionFlag.RestrictedMemberAccess))
ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, Nothing)
' Create an instance of the Worker class in the partially trusted
' domain.
w = CType(ad.CreateInstanceAndUnwrap(asmName, "Worker"), Worker)
' Again, emit and invoke a dynamic method that calls a private method
' of Worker, skipping JIT visibility checks. This time compilation
' succeeds because of the grant for RestrictedMemberAccess.
w.AccessPrivateMethod(True)
' Finally, emit and invoke a dynamic method that calls an internal
' method of the String class. The call fails, because the trust level
' of the assembly that contains String is higher than the trust level
' of the assembly that emits the dynamic method.
w.AccessPrivateMethod()
End Sub
End Class
' This code example produces the following output:
'
'Hello, World!
'MethodAccessException was thrown when the delegate was invoked.
'MethodAccessException was thrown when the delegate was invoked.
'Worker.PrivateMethod()
'MethodAccessException was thrown when the delegate was compiled.
'
using System;
using System.Reflection.Emit;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;
using System.Collections;
using System.Diagnostics;
// This code example works properly only if it is run from a fully
// trusted location, such as your local computer.
// Delegates used to execute the dynamic methods.
//
public delegate void Test(Worker w);
public delegate void Test1();
public delegate char Test2(String instance);
// The Worker class must inherit MarshalByRefObject so that its public
// methods can be invoked across application domain boundaries.
//
public class Worker : MarshalByRefObject
{
private void PrivateMethod()
{
Console.WriteLine("Worker.PrivateMethod()");
}
public void SimpleEmitDemo()
{
DynamicMethod meth = new DynamicMethod("", null, null);
ILGenerator il = meth.GetILGenerator();
il.EmitWriteLine("Hello, World!");
il.Emit(OpCodes.Ret);
Test1 t1 = (Test1) meth.CreateDelegate(typeof(Test1));
t1();
}
// This overload of AccessPrivateMethod emits a dynamic method and
// specifies whether to skip JIT visiblity checks. It creates a
// delegate for the method and invokes the delegate. The dynamic
// method calls a private method of the Worker class.
public void AccessPrivateMethod(bool restrictedSkipVisibility)
{
// Create an unnamed dynamic method that has no return type,
// takes one parameter of type Worker, and optionally skips JIT
// visiblity checks.
DynamicMethod meth = new DynamicMethod(
"",
null,
new Type[] { typeof(Worker) },
restrictedSkipVisibility);
// Get a MethodInfo for the private method.
MethodInfo pvtMeth = typeof(Worker).GetMethod("PrivateMethod",
BindingFlags.NonPublic | BindingFlags.Instance);
// Get an ILGenerator and emit a body for the dynamic method.
ILGenerator il = meth.GetILGenerator();
// Load the first argument, which is the target instance, onto the
// execution stack, call the private method, and return.
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, pvtMeth, null);
il.Emit(OpCodes.Ret);
// Create a delegate that represents the dynamic method, and
// invoke it.
try
{
Test t = (Test) meth.CreateDelegate(typeof(Test));
try
{
t(this);
}
catch (Exception ex)
{
Console.WriteLine("{0} was thrown when the delegate was invoked.",
ex.GetType().Name);
}
}
catch (Exception ex)
{
Console.WriteLine("{0} was thrown when the delegate was compiled.",
ex.GetType().Name);
}
}
// This overload of AccessPrivateMethod emits a dynamic method that takes
// a string and returns the first character, using a private field of the
// String class. The dynamic method skips JIT visiblity checks.
public void AccessPrivateMethod()
{
DynamicMethod meth = new DynamicMethod("",
typeof(char),
new Type[] { typeof(String) },
true);
// Get a MethodInfo for the 'get' accessor of the private property.
PropertyInfo pi = typeof(System.String).GetProperty(
"FirstChar",
BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo pvtMeth = pi.GetGetMethod(true);
// Get an ILGenerator and emit a body for the dynamic method.
ILGenerator il = meth.GetILGenerator();
// Load the first argument, which is the target string, onto the
// execution stack, call the 'get' accessor to put the result onto
// the execution stack, and return.
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, pvtMeth, null);
il.Emit(OpCodes.Ret);
// Create a delegate that represents the dynamic method, and
// invoke it.
try
{
Test2 t = (Test2) meth.CreateDelegate(typeof(Test2));
char first = t("Hello, World!");
Console.WriteLine("{0} is the first character.", first);
}
catch (Exception ex)
{
Console.WriteLine("{0} was thrown when the delegate was compiled.",
ex.GetType().Name);
}
}
// The entry point for the code example.
static void Main()
{
// Get the display name of the executing assembly, to use when
// creating objects to run code in application domains.
String asmName = Assembly.GetExecutingAssembly().FullName;
// Create the permission set to grant to other assemblies. In this
// case they are the permissions found in the Internet zone.
Evidence ev = new Evidence();
ev.AddHostEvidence(new Zone(SecurityZone.Internet));
PermissionSet pset = new NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev));
// For simplicity, set up the application domain to use the
// current path as the application folder, so the same executable
// can be used in both trusted and untrusted scenarios. Normally
// you would not do this with real untrusted code.
AppDomainSetup adSetup = new AppDomainSetup();
adSetup.ApplicationBase = ".";
// Create an application domain in which all code that executes is
// granted the permissions of an application run from the Internet.
AppDomain ad = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, null);
// Create an instance of the Worker class in the partially trusted
// domain. Note: If you build this code example in Visual Studio,
// you must change the name of the class to include the default
// namespace, which is the project name. For example, if the project
// is "AnonymouslyHosted", the class is "AnonymouslyHosted.Worker".
Worker w = (Worker) ad.CreateInstanceAndUnwrap(asmName, "Worker");
// Emit a simple dynamic method that prints "Hello, World!"
w.SimpleEmitDemo();
// Emit and invoke a dynamic method that calls a private method
// of Worker, with JIT visibility checks enforced. The call fails
// when the delegate is invoked.
w.AccessPrivateMethod(false);
// Emit and invoke a dynamic method that calls a private method
// of Worker, skipping JIT visibility checks. The call fails when
// the method is invoked.
w.AccessPrivateMethod(true);
// Unload the application domain. Add RestrictedMemberAccess to the
// grant set, and use it to create an application domain in which
// partially trusted code can call private members, as long as the
// trust level of those members is equal to or lower than the trust
// level of the partially trusted code.
AppDomain.Unload(ad);
pset.SetPermission(
new ReflectionPermission(
ReflectionPermissionFlag.RestrictedMemberAccess));
ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, null);
// Create an instance of the Worker class in the partially trusted
// domain.
w = (Worker) ad.CreateInstanceAndUnwrap(asmName, "Worker");
// Again, emit and invoke a dynamic method that calls a private method
// of Worker, skipping JIT visibility checks. This time compilation
// succeeds because of the grant for RestrictedMemberAccess.
w.AccessPrivateMethod(true);
// Finally, emit and invoke a dynamic method that calls an internal
// method of the String class. The call fails, because the trust level
// of the assembly that contains String is higher than the trust level
// of the assembly that emits the dynamic method.
w.AccessPrivateMethod();
}
}
/* This code example produces the following output:
Hello, World!
MethodAccessException was thrown when the delegate was invoked.
MethodAccessException was thrown when the delegate was invoked.
Worker.PrivateMethod()
MethodAccessException was thrown when the delegate was compiled.
*/
コードのコンパイル
- このコード例を Visual Studio でビルドする場合は、クラスを CreateInstanceAndUnwrap メソッドに渡すときに、名前空間を含むようにクラスの名前を変更する必要があります。 既定では、名前空間がプロジェクト名になります。 たとえば、プロジェクト名が "PartialTrust" である場合、クラス名は "PartialTrust.Worker" にする必要があります。