チュートリアル : 部分信頼シナリオにおけるコード出力

更新 : 2007 年 11 月

リフレクション出力は、完全信頼または部分信頼において同じ API セットを使用しますが、部分的に信頼されるコードでは実行する機能によって特定のアクセス許可が必要になります。リフレクション出力には、匿名でホストされる動的メソッドという機能があります。この機能は、透過的セキュリティ アセンブリによって部分信頼で使用されます。

メモ :

.NET Framework version 3.5 以前では、コード出力を行うには ReflectionPermissionReflectionPermissionFlag.ReflectionEmit フラグを指定する必要がありました。このアクセス許可は、既定で FullTrust および Intranet の名前付きアクセス許可セットには含まれますが、Internet アクセス許可セットには含まれません。したがって、ライブラリを部分信頼で使用するには、SecurityCriticalAttribute 属性を設定し、ReflectionEmit に対して Assert メソッドを実行する必要がありました。このようなライブラリでは、コーディング エラーがあるとセキュリティ ホールが発生するおそれがあるため、セキュリティを慎重に確認する必要があります。コードの生成は本質的に特権を必要とする操作ではないため、.NET Framework 3.5 はセキュリティ確認要求を発行せずに部分信頼シナリオでコードを出力できます。これは、生成されたコードには、コードを出力したアセンブリと同等以下のアクセス許可しかないことを意味します。これにより、コードを出力するライブラリは透過的セキュリティになるため、ReflectionEmit を要求する必要がなくなります。つまり、安全なライブラリを記述するためにセキュリティを入念に確認する必要がなくなります。

このチュートリアルでは、次のタスクについて説明します。

  • コード テスト用に部分的に信頼された環境を設定する

  • 部分的に信頼されたアプリケーション ドメインでコードを実行する

  • 匿名でホストされる動的メソッドを使用して部分信頼でコードを出力し実行する

部分信頼シナリオでのコード出力の詳細については、「リフレクション出力のセキュリティ関連事項」を参照してください。

ここで説明する手順に示すコードの完全な一覧については、このチュートリアルの最後の「例」を参照してください。

部分的に信頼された場所を設定する

次の手順では、部分信頼で実行できるコードの場所を設定する方法を説明します。

  • 最初の手順では、サンドボックス化されたアプリケーション ドメインを作成する方法について説明します。このドメインから、インターネット信頼でコードを実行します。一般的な落とし穴についても説明します。

  • 2 番目の手順では、部分的に信頼されたアプリケーション ドメインに対し、ReflectionPermissionFlag.RestrictedMemberAccess フラグを設定して ReflectionPermission を追加する方法を説明します。これにより、信頼レベルが同等以下のアセンブリでプライベート データにアクセスできるようになります。

  • 3 番目の手順では、信頼レベルをフォルダに関連付けるコード グループを作成する方法について説明します。このフォルダに配置されるアセンブリは、部分信頼で実行されるようになります。

これらの手順に加えて、次に示すようなアセンブリ属性を使用する簡単なシナリオにおいて、SkipVerification アクセス許可なしでアセンブリを実行します。同様の方法で、MemberAccess アクセス許可を拒否する属性を使用できます。

<Assembly:SecurityPermissionAttribute(SecurityAction.RequestRefuse, Flags:=SecurityPermissionFlag.SkipVerification)>
[assembly:SecurityPermissionAttribute(SecurityAction.RequestRefuse, Flags=SecurityPermissionFlag.SkipVerification)]

サンドボックス化されたアプリケーション ドメインを作成する

部分信頼でアセンブリを実行するアプリケーション ドメインを作成するには、AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) メソッド オーバーロードを使用して、アセンブリに付与するアクセス許可セットを指定する必要があります。許可セットを指定するための最も簡単な方法として、セキュリティ ポリシーから名前付きアクセス許可セットを取得する方法があります。

注意 :

証拠を指定するだけでは、サンドボックス化されたアプリケーション ドメインを作成することはできません。許可セットまたはアプリケーション ドメイン ポリシー レベルを指定する必要があります (アプリケーション ドメイン ポリシー レベルの設定については、このトピックでは説明しません)。たとえば、インターネットの証拠で CreateDomain(String, Evidence) メソッド オーバーロードを使用すると、そのアクセス許可はアプリケーション ドメインの境界に対してのみ強化されます。アプリケーション ドメイン内では、標準のセキュリティ ポリシーに基づいてアセンブリにアクセス許可が付与されます。コンピュータ上のコンソール アプリケーションの場合は、完全信頼になります。

次の手順では、部分信頼でコードを実行するサンドボックス化されたアプリケーション ドメインを作成し、出力されるコードが、パブリック型のパブリック メンバにのみアクセスできるというシナリオをテストします。後続の手順では、RestrictedMemberAccess を追加する方法を説明し、出力されるコードが、同等以下のアクセス許可を付与されたアセンブリ内にある非パブリックな型とメンバにアクセスできるというシナリオをテストします。

部分信頼でアプリケーション ドメインを作成するには

  1. 次のヘルパー関数を使用して、セキュリティ ポリシーから名前付きアクセス許可セットを取得します。

    Private Shared Function GetNamedPermissionSet(ByVal name As String) As PermissionSet 
    
        If (String.IsNullOrEmpty(name)) Then 
            Throw New ArgumentException("name", "Cannot search for a permission set without a name.")
        End If
    
        Dim foundName As Boolean = False
        Dim setIntersection As New PermissionSet(PermissionState.Unrestricted)
    
        ' Search all policy levels.
        Dim levelEnumerator As IEnumerator = SecurityManager.PolicyHierarchy()
        While (levelEnumerator.MoveNext())
    
            Dim level As PolicyLevel = levelEnumerator.Current 
            Debug.Assert(level IsNot Nothing)
    
            ' If this policy level has a named permission set with the 
            ' specified name, intersect it with previous levels.
            Dim levelSet As PermissionSet = level.GetNamedPermissionSet(name)
            If (levelSet IsNot Nothing) Then
    
                foundName = True
                setIntersection = setIntersection.Intersect(levelSet)
    
                ' Intersect() returns null for an empty set. If this occurs
                ' at any point, the resulting permission set is empty.
                If (setIntersection Is Nothing) Then
                    Return New PermissionSet(PermissionState.None)
                End If
            End If
        End While
    
        If Not foundName Then
            setIntersection = New PermissionSet(PermissionState.None)
        End If
        Return setIntersection
    
    End Function 
    
    private static PermissionSet GetNamedPermissionSet(string name)
    {
        if (String.IsNullOrEmpty(name))
            throw new ArgumentException("name", "Cannot search for a permission set without a name.");
    
        bool foundName = false;
        PermissionSet setIntersection = new PermissionSet(PermissionState.Unrestricted);
    
        // Search all policy levels.
        IEnumerator levelEnumerator = SecurityManager.PolicyHierarchy();
        while (levelEnumerator.MoveNext())
        {
            PolicyLevel level = levelEnumerator.Current as PolicyLevel;
            Debug.Assert(level != null);
    
            // If this policy level has a named permission set with the 
            // specified name, intersect it with previous levels.
            PermissionSet levelSet = level.GetNamedPermissionSet(name);
            if (levelSet != null)
            {
                foundName = true;
                setIntersection = setIntersection.Intersect(levelSet);
    
                // Intersect() returns null for an empty set. If this occurs
                // at any point, the resulting permission set is empty.
                if (setIntersection == null)
                    return new PermissionSet(PermissionState.None);
            }
        }
    
        if (!foundName)
            setIntersection = new PermissionSet(PermissionState.None);
    
        return setIntersection;
    }
    

    許可セットは、すべてのポリシー レベルで付与されたアクセス許可セットの積集合です。言い換えると、すべてのポリシー レベルで付与されていないアクセス許可は付与されないことになります。したがって、ヘルパー関数は、完全信頼の許可セットから開始して、ポリシー階層のレベルを列挙し、各レベルに定義されたアクセス許可セットを使用して許可セットの積集合を作成します。

    メモ :

    PermissionSet クラスを使用することで、アクセス許可を任意に組み合わせた許可セットを作成できます。

    ヘルパー関数の使用法については、この手順の後半で説明します。

  2. セキュリティ ゾーンを使用して、部分的に信頼された場所の証拠を作成します。ここでは、インターネット ゾーンを使用します。

    Dim zoneEvidence() As Object = { New Zone(SecurityZone.Internet) }
    Dim internetZone As New Evidence(zoneEvidence, zoneEvidence)
    
    Object[] zoneEvidence = { new Zone(SecurityZone.Internet) };
    Evidence internetZone = new Evidence(zoneEvidence, zoneEvidence);
    
  3. アプリケーション ドメインをアプリケーション パスで初期化するため、AppDomainSetup オブジェクトを作成します。このコード例では、現在のフォルダを使用します。

    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
  4. ヘルパー関数を使用して、システム ポリシーから名前付きアクセス許可セットを取得します。

    Dim internetSet As PermissionSet = GetNamedPermissionSet("Internet")
    
    PermissionSet internetSet = GetNamedPermissionSet("Internet");
    
  5. 証拠、アプリケーション ドメイン設定情報、および許可セットを指定して、アプリケーション ドメインを作成します。

    Dim ad As AppDomain = AppDomain.CreateDomain("ChildDomain1", _
                                                 internetZone, _
                                                 adSetup, _
                                                 internetSet, _
                                                 Nothing)
    
    AppDomain ad = AppDomain.CreateDomain("ChildDomain1", 
                                          internetZone, 
                                          adSetup, 
                                          internetSet, 
                                          null);
    

    AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) メソッド オーバーロードの最後のパラメータを使用すると、アプリケーション ドメインの許可セットではなく、完全な信頼が付与されたアセンブリのセットを指定できます。これらのアセンブリはグローバル アセンブリ キャッシュに存在するため、アプリケーションが使用する .NET Framework アセンブリを指定する必要はありません。グローバル アセンブリ キャッシュにあるアセンブリは、常に完全に信頼されます。このパラメータを使用して、グローバル アセンブリ キャッシュには存在しない厳密名のアセンブリを指定できます。

サンドボックス化されたドメインに RestrictedMemberAccess を追加する

ホスト アプリケーションは、匿名でホストされる動的メソッドが、コードを出力するアセンブリの信頼レベルと同等以下の信頼レベルが設定されたアセンブリ内のプライベート データにアクセスすることを許可できます。この制限付き機能を有効にして Just-In-Time (JIT) 参照範囲チェックをスキップするため、ホスト アプリケーションは、許可セットに対し、ReflectionPermissionFlag.RestrictedMemberAccess (RMA) フラグを設定した ReflectionPermission オブジェクトを追加します。

たとえば、ホストはインターネット アプリケーションに対し、RMA を設定した Internet アクセス許可を付与し、インターネット アプリケーションが独自のアセンブリ内にあるプライベート データにアクセスするコードを出力できるようにすることが可能です。このアクセスは、信頼レベルが同等以下であるアセンブリに限定されるため、インターネット アプリケーションは、.NET Framework アセンブリのような完全に信頼されたアセンブリのメンバにはアクセスできません。

メモ :

特権の昇格を回避するため、匿名でホストされる動的メソッドの作成時にはアセンブリ出力の履歴情報が含まれます。メソッドの呼び出し時に履歴情報がチェックされるため、完全に信頼されたコードから呼び出された、匿名でホストされる動的メソッドは、依然として出力アセンブリの信頼レベルに制限されます。

RMA を設定した部分信頼でアプリケーション ドメインを作成するには

  1. ヘルパー関数を使用して、セキュリティ ポリシーから Internet 名前付きアクセス許可セットを取得します。

    internetSet = GetNamedPermissionSet("Internet")
    
    internetSet = GetNamedPermissionSet("Internet");
    
  2. RestrictedMemberAccess フラグを指定した新しい ReflectionPermission オブジェクトを作成し、PermissionSet.SetPermission メソッドを使用して、許可セットにアクセス許可を追加します。

    internetSet.SetPermission( _
        New ReflectionPermission( _
            ReflectionPermissionFlag.RestrictedMemberAccess))
    
    internetSet.SetPermission(
        new ReflectionPermission(
            ReflectionPermissionFlag.RestrictedMemberAccess));
    

    許可セットにアクセス許可が含まれていない場合、AddPermission メソッドによって、許可セットにアクセス許可が追加されます。許可セットにアクセス許可が含まれている場合は、既存のアクセス許可に指定フラグが追加されます。

    メモ :

    RMA は、匿名でホストされる動的メソッドの 1 つの機能です。通常の動的メソッドが JIT 参照範囲チェックをスキップした場合、出力コードには RestrictedMemberAccess フラグに加えて ReflectionPermissionFlag.MemberAccess フラグを設定した ReflectionPermission が付与されている必要があります。

  3. 証拠、アプリケーション ドメイン設定情報、および許可セットを指定して、アプリケーション ドメインを作成します。

    ad = AppDomain.CreateDomain("ChildDomain2", _
                                internetZone, _
                                adSetup, _
                                internetSet, _
                                Nothing)
    
    ad = AppDomain.CreateDomain("ChildDomain2", 
                                internetZone, 
                                adSetup, 
                                internetSet, 
                                null);
    

アクセス許可が制限されたフォルダを作成する

次の手順では、Internet アクセス許可をフォルダに関連付けるコード グループを作成する方法と、そのフォルダから実行されるコードの許可セットに RestrictedMemberAccess フラグを追加する方法について説明します。

Internet アクセス許可が設定されたフォルダを作成するには

  1. [スタート] ボタンをクリックし、[コントロール パネル] をポイントして、[管理ツール] をポイントします。次に、[Microsoft .NET Framework 3.5 構成ツール] をクリックします。構成ツールを使用するには、システム管理者の特権が必要です。

  2. 左ペインの [.NET Framework 2.0 構成ツール] で、[マイ コンピュータ]、[ランタイム セキュリティ ポリシー]、[コンピュータ]、[コード グループ]、[All_Code] を順に展開します。

  3. 右ペインで [子コード グループを追加] をクリックし、コード グループの作成ウィザードを実行します。

  4. コード グループに名前を割り当て ("Internet Sandbox" など)、必要に応じて説明を入力します。[次へ] をクリックします。

  5. [このコード グループの条件の種類を選択します] ボックスで [URL] をクリックします。[URL] ボックスに、使用するフォルダの完全パスを入力し、[次へ] をクリックします。たとえば、次のように入力できます。

    file://c:/InternetSandbox/*
    
  6. [既存のアクセス許可セットを使用] ボックスの一覧の [Internet] をクリックし、[次へ] をクリックします。

    メモ :

    名前付きアクセス許可セットと、RestrictedMemberAccess フラグを設定した ReflectionPermission オブジェクトを指定するには、[新しいアクセス許可セットの作成] をクリックし、XML ファイルを使用してカスタム アクセス許可を指定します。

  7. [完了] をクリックし、コード グループを作成します。

  8. 手順 5. で指定したフォルダに、部分信頼で実行するアセンブリを配置します。

サンドボックス化されたアプリケーション ドメインでコードを実行する

次の手順では、アプリケーション ドメインで実行可能なメソッドを使用してクラスを定義する方法、そのドメインでクラスのインスタンスを作成する方法、そのメソッドを実行する方法について説明します。

アプリケーション ドメインでメソッドを定義して実行するには

  1. MarshalByRefObject から派生するクラスを定義します。これにより、他のアプリケーション ドメインにクラスのインスタンスを作成し、アプリケーション ドメインの境界を越えてメソッドを呼び出すことができます。この例のクラスの名前は Worker です。

    Public Class Worker
        Inherits MarshalByRefObject
    
    public class Worker : MarshalByRefObject
    {
    
  2. 実行するコードを含むパブリック メソッドを定義します。この例では、コードは単純な動的メソッドを出力し、メソッドを実行するためのデリゲートを作成して、デリゲートを呼び出します。

    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();
    }
    
  3. メイン プログラムで、アセンブリの表示名を取得します。この名前は、サンドボックス化されたアプリケーション ドメインで Worker クラスのインスタンスを作成するときに使用します。

    Dim asmName As String = [Assembly].GetExecutingAssembly().FullName
    
    String asmName = Assembly.GetExecutingAssembly().FullName;
    
  4. メイン プログラムでは、このチュートリアルの最初の手順で説明したとおり、サンドボックス化されたアプリケーション ドメインを作成します。SimpleEmitDemo メソッドはパブリック メソッドのみを使用するため、Internet アクセス許可セットに任意のアクセス許可を追加しないでください。

  5. メイン プログラムで、サンドボックス化されたアプリケーション ドメインに Worker クラスのインスタンスを作成します。

    Dim w As Worker = _
        CType(ad.CreateInstanceAndUnwrap(asmName, "Worker"), Worker)
    
    Worker w = (Worker) ad.CreateInstanceAndUnwrap(asmName, "Worker");
    

    CreateInstanceAndUnwrap メソッドにより、対象のアプリケーション ドメイン内にオブジェクトが作成され、オブジェクトのプロパティおよびメソッドの呼び出しに使用できるプロキシが返されます。

    メモ :

    このコードを Visual Studio で使用する場合は、名前空間を含むようにクラスの名前を変更する必要があります。既定では、名前空間がプロジェクト名になります。たとえば、プロジェクト名が "PartialTrust" である場合、クラス名は "PartialTrust.Worker" にする必要があります。

  6. 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, array<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 参照範囲チェックをスキップした場合、ReflectionPermissionFlag.MemberAccess フラグを設定した ReflectionPermission が付与されている必要があります。さらに、出力アセンブリ以外のアセンブリにあるプライベート データにアクセスできる通常の動的メソッドには、RestrictedMemberAccess フラグを設定した ReflectionPermission または SecurityPermissionFlag.ControlEvidence フラグを設定した SecurityPermission が必要です。

説明

次のコード例に、RestrictedMemberAccess フラグを使用して、匿名でホストされる動的メソッドが JIT 参照範囲チェックをスキップできるようにする方法を示します。ただし、この例では、対象のメンバには、コードを出力するアセンブリと同等以下の信頼レベルが設定されていることを前提としています。

例では、アプリケーションのドメイン境界を越えてマーシャリング可能な Worker クラスを定義します。クラスには、動的メソッドを出力して実行する、AccessPrivateMethod メソッド オーバーロードが 2 つあります。最初のオーバーロードは、Worker クラスのプライベート PrivateMethod メソッドを呼び出す動的メソッドを出力し、JIT 参照範囲チェックの有無に関係なくこの動的メソッドを出力できます。2 番目のオーバーロードは、String クラスの internal プロパティ (Visual Basic では Friend プロパティ) にアクセスする動的メソッドを出力します。

この例では、ヘルパー メソッドを使用して、セキュリティ ポリシーから Internet アクセス許可セットを取得します。そして、AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) メソッド オーバーロードを使用してアプリケーション ドメインを作成し、この許可セットを使用するドメインで実行するすべてのコードを指定します。アプリケーション ドメインに Worker クラスのインスタンスを作成し、AccessPrivateMethod メソッドを 2 回実行します。

  • AccessPrivateMethod メソッドの初回の実行では、JIT 参照範囲チェックが強制されます。参照範囲チェックではプライベート メソッドにアクセスできないため、動的メソッドは呼び出し時に失敗します。

  • AccessPrivateMethod メソッドの 2 回目の実行では、JIT 参照範囲チェックをスキップします。Internet 許可セットでは、参照範囲チェックをスキップするのに十分なアクセス許可が付与されていないため、動的メソッドはコンパイル時に失敗します。

この例では、ヘルパー メソッドを使用して、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 evidence for a partially trusted location and a setup object
        ' that specifies the current directory for the application directory.
        Dim zoneEvidence() As Object = { New Zone(SecurityZone.Internet) }
        Dim internetZone As New Evidence(zoneEvidence, zoneEvidence)
        Dim adSetup As New AppDomainSetup()
        adSetup.ApplicationBase = "."

        ' Retrieve the Internet grant set from system policy, and create 
        ' an application domain in which all code that executes is granted
        ' the permissions of an application run from the Internet.
        Dim internetSet As PermissionSet = GetNamedPermissionSet("Internet")
        Dim ad As AppDomain = AppDomain.CreateDomain("ChildDomain1", _
                                                     internetZone, _
                                                     adSetup, _
                                                     internetSet, _
                                                     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. Now create a grant set composed 
        ' of the permissions granted to an Internet application plus
        ' RestrictedMemberAccess, 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)
        internetSet = GetNamedPermissionSet("Internet")
        internetSet.SetPermission( _
            New ReflectionPermission( _
                ReflectionPermissionFlag.RestrictedMemberAccess))
        ad = AppDomain.CreateDomain("ChildDomain2", _
                                    internetZone, _
                                    adSetup, _
                                    internetSet, _
                                    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 


    ' This method retrieves a named permission set from security policy.
    ' The return value is the intersection of all permission sets with the
    ' given name, from all policy levels, or an empty permission set if the
    ' name is not found.
    Private Shared Function GetNamedPermissionSet(ByVal name As String) As PermissionSet 

        If (String.IsNullOrEmpty(name)) Then 
            Throw New ArgumentException("name", "Cannot search for a permission set without a name.")
        End If

        Dim foundName As Boolean = False
        Dim setIntersection As New PermissionSet(PermissionState.Unrestricted)

        ' Search all policy levels.
        Dim levelEnumerator As IEnumerator = SecurityManager.PolicyHierarchy()
        While (levelEnumerator.MoveNext())

            Dim level As PolicyLevel = levelEnumerator.Current 
            Debug.Assert(level IsNot Nothing)

            ' If this policy level has a named permission set with the 
            ' specified name, intersect it with previous levels.
            Dim levelSet As PermissionSet = level.GetNamedPermissionSet(name)
            If (levelSet IsNot Nothing) Then

                foundName = True
                setIntersection = setIntersection.Intersect(levelSet)

                ' Intersect() returns null for an empty set. If this occurs
                ' at any point, the resulting permission set is empty.
                If (setIntersection Is Nothing) Then
                    Return New PermissionSet(PermissionState.None)
                End If
            End If
        End While

        If Not foundName Then
            setIntersection = New PermissionSet(PermissionState.None)
        End If
        Return setIntersection

    End Function 

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 compiled.
'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 evidence for a partially trusted location and a setup object
        // that specifies the current directory for the application directory.
        Object[] zoneEvidence = { new Zone(SecurityZone.Internet) };
        Evidence internetZone = new Evidence(zoneEvidence, zoneEvidence);
        AppDomainSetup adSetup = new AppDomainSetup();
        adSetup.ApplicationBase = ".";

        // Retrieve the Internet grant set from system policy, and create 
        // an application domain in which all code that executes is granted
        // the permissions of an application run from the Internet.
        PermissionSet internetSet = GetNamedPermissionSet("Internet");
        AppDomain ad = AppDomain.CreateDomain("ChildDomain1", 
                                              internetZone, 
                                              adSetup, 
                                              internetSet, 
                                              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 compiled.
        w.AccessPrivateMethod(true);


        // Unload the application domain. Now create a grant set composed 
        // of the permissions granted to an Internet application plus
        // RestrictedMemberAccess, 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);
        internetSet = GetNamedPermissionSet("Internet");
        internetSet.SetPermission(
            new ReflectionPermission(
                ReflectionPermissionFlag.RestrictedMemberAccess));
        ad = AppDomain.CreateDomain("ChildDomain2", 
                                    internetZone, 
                                    adSetup, 
                                    internetSet, 
                                    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 method retrieves a named permission set from security policy.
    // The return value is the intersection of all permission sets with the
    // given name, from all policy levels, or an empty permission set if the
    // name is not found.
    private static PermissionSet GetNamedPermissionSet(string name)
    {
        if (String.IsNullOrEmpty(name))
            throw new ArgumentException("name", "Cannot search for a permission set without a name.");

        bool foundName = false;
        PermissionSet setIntersection = new PermissionSet(PermissionState.Unrestricted);

        // Search all policy levels.
        IEnumerator levelEnumerator = SecurityManager.PolicyHierarchy();
        while (levelEnumerator.MoveNext())
        {
            PolicyLevel level = levelEnumerator.Current as PolicyLevel;
            Debug.Assert(level != null);

            // If this policy level has a named permission set with the 
            // specified name, intersect it with previous levels.
            PermissionSet levelSet = level.GetNamedPermissionSet(name);
            if (levelSet != null)
            {
                foundName = true;
                setIntersection = setIntersection.Intersect(levelSet);

                // Intersect() returns null for an empty set. If this occurs
                // at any point, the resulting permission set is empty.
                if (setIntersection == null)
                    return new PermissionSet(PermissionState.None);
            }
        }

        if (!foundName)
            setIntersection = new PermissionSet(PermissionState.None);

        return setIntersection;
    }
}

/* This code example produces the following output:

Hello, World!
MethodAccessException was thrown when the delegate was invoked.
MethodAccessException was thrown when the delegate was compiled.
Worker.PrivateMethod()
MethodAccessException was thrown when the delegate was compiled.
 */

コードのコンパイル

  • このコード例を Visual Studio でビルドする場合は、クラスを CreateInstanceAndUnwrap メソッドに渡すときに、名前空間を含むようにクラスの名前を変更する必要があります。既定では、名前空間がプロジェクト名になります。たとえば、プロジェクト名が "PartialTrust" である場合、クラス名は "PartialTrust.Worker" にする必要があります。

参照

処理手順

方法 : サンドボックスで部分信頼コードを実行する

概念

リフレクション出力のセキュリティ関連事項