Procedura dettagliata: emissione di codice in scenari di attendibilità parziale

Aggiornamento: novembre 2007

Reflection emit utilizza le stesse API impostate in attendibilità totale o parziale, ma alcune funzionalità richiedono autorizzazioni speciali per il codice parzialmente attendibile. Inoltre, reflection emit presenta una funzionalità, metodi dinamici ospitati anonimamente, progettati per essere utilizzati in scenari di attendibilità parziale e dagli assembly SecurityTransparent.

Nota:

Prima di .NET Framework versione 3.5, la creazione di codice richiedeva ReflectionPermission con il flag ReflectionPermissionFlag.ReflectionEmit. Questa autorizzazione è inclusa per impostazione predefinita nei set di autorizzazioni denominati FullTrust e Intranet, ma non nel set di autorizzazioni Internet. Pertanto, una libreria potrebbe essere utilizzata da applicazioni di attendibilità parziale solo se possiedono l'attributo SecurityCriticalAttribute e hanno anche eseguito un metodo Assert per ReflectionEmit. Tali librerie richiedono un'accurata revisione della sicurezza perché eventuali errori nel codice potrebbero comportare problemi di sicurezza. .NET Framework 3.5 consente che il codice venga generato in scenari di attendibilità parziale senza emettere alcuna richiesta di protezione, perché la generazione di codice non è, per sua natura, un'operazione privilegiata. Ovvero, il codice generato non ha più autorizzazioni dell'assembly che lo genera. Questo consente alle librerie che generano il codice di essere security transparent ed elimina la necessità di asserire ReflectionEmit, in modo tale che la scrittura di una libreria protetta non richieda una revisione della sicurezza così approfondita.

In questa procedura dettagliata vengono illustrate le attività seguenti:

  • Impostazione di ambienti parzialmente attendibili per testare codice.

  • Esecuzione di codice nei domini applicazione parzialmente attendibili.

  • Utilizzo di metodi dinamici ospitati anonimamente per generare ed eseguire codice in attendibilità parziale.

Per ulteriori informazioni sulla generazione di codice in scenari di attendibilità parziale, vedere Problemi di sicurezza con la reflection emit.

Per un elenco completo del codice illustrato in queste procedure, vedere la sezione Esempio alla fine di questa procedura dettagliata.

Impostazione di percorsi parzialmente attendibili

Nelle procedure descritte di seguito viene illustrato come configurare percorsi dai quali il codice può essere eseguito con attendibilità parziale.

  • Nella prima procedura viene descritto come creare un dominio applicazione mediante sandbox in cui il codice possa essere eseguito con attendibilità Internet. Vengono illustrate anche problematiche comuni.

  • Nella seconda procedura viene spiegato come aggiungere ReflectionPermission con il flag ReflectionPermissionFlag.RestrictedMemberAccess al dominio applicazione parzialmente attendibile, per consentire l'accesso ai dati privati negli assembly di attendibilità uguale o inferiore.

  • Nella terza procedura viene illustrato come creare un gruppo di codice che associa un livello di attendibilità a una cartella, in modo che qualsiasi assembly all'interno della cartella venga eseguito con attendibilità parziale.

Oltre a queste procedure, negli scenari semplici è possibile utilizzare un attributo dell'assembly come il seguente per eseguire un assembly senza autorizzazione SkipVerification. Analogamente, è possibile utilizzare un attributo per rifiutare l'autorizzazione MemberAccess.

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

Creazione di domini dell'applicazione mediante sandbox

Per creare un dominio applicazione nel quale gli assembly siano in esecuzione con attendibilità parziale è necessario specificare l'insieme di autorizzazioni da concedere agli assembly utilizzando l'overload di metodo AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]). Il modo più semplice per specificare la concessione è recuperare una concessione denominata dai criteri di sicurezza.

Attenzione:

Non è possibile creare un dominio applicazione mediante sandbox specificando solo l'evidenza. È necessario specificare una concessione o un livello dei criteri del dominio applicazione. L'impostazione di un livello dei criteri del dominio applicazione non è oggetto di questo argomento. Ad esempio, se si utilizza l'overload del metodo CreateDomain(String, Evidence) con evidenza Internet, le autorizzazioni vengono applicate solo al limite del dominio applicazione. Nel dominio applicazione le autorizzazioni vengono concesse in base ai criteri di sicurezza standard. Per un'applicazione console nel computer i criteri standard corrispondono all'attendibilità totale.

Nella procedura descritta di seguito viene creato un dominio applicazione mediante sandbox che esegue il codice con attendibilità parziale, per testare scenari in cui il codice generato può accedere solo a membri pubblici di tipi pubblici. In una procedura successiva viene mostrato come aggiungere RestrictedMemberAccess, per testare scenari in cui il codice generato può accedere a tipi e membri non pubblici in assembly che dispongono di autorizzazioni uguali o inferiori.

Per creare un dominio applicazione con attendibilità parziale

  1. Utilizzare la funzione di supporto seguente per ottenere set di autorizzazioni denominati dai criteri di sicurezza.

    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;
    }
    

    Una concessione è l'intersezione dei set di autorizzazioni concessi a tutti i livelli di criteri. Ovvero, una particolare autorizzazione non viene concessa a meno che non lo sia a tutti i livelli di criteri. Pertanto, la funzione di supporto viene avviata con una concessione per l'attendibilità totale ed enumera i livelli della gerarchia dei criteri, accettando l'intersezione di tale concessione con l'insieme di autorizzazioni definito per ogni livello.

    Nota:

    È possibile creare concessioni che contengano qualsiasi combinazione di autorizzazioni utilizzando la classe PermissionSet.

    L'utilizzo della funzione di supporto verrà illustrato più avanti in questa procedura.

  2. Creare l'evidenza per il percorso parzialmente attendibile utilizzando aree di sicurezza. In questo caso, viene utilizzata l'area Internet.

    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. Creare un oggetto AppDomainSetup per inizializzare il dominio applicazione con un percorso dell'applicazione. Nel presente esempio di codice viene utilizzata la cartella corrente.

    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
  4. Utilizzare la funzione di supporto per recuperare il set di autorizzazioni denominato dai criteri di sistema.

    Dim internetSet As PermissionSet = GetNamedPermissionSet("Internet")
    
    PermissionSet internetSet = GetNamedPermissionSet("Internet");
    
  5. Creare un nuovo dominio applicazione specificando l'evidenza, le informazioni di installazione del dominio applicazione e la concessione.

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

    L'ultimo parametro dell'overload del metodo AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) consente di specificare un insieme di assembly che devono essere concessi in attendibilità totale, invece della concessione del dominio applicazione. Non è necessario specificare gli assembly .NET Framework utilizzati dall'applicazione, perché si trovano nella Global Assembly Cache. Gli assembly della Global Assembly Cache sono sempre completamente attendibili. È possibile utilizzare questo parametro per specificare assembly con nome non sicuro che non si trovano nella Global Assembly Cache.

Aggiunta di RestrictedMemberAccess ai domini creati mediante sandbox

Le applicazioni host possono consentire a metodi dinamici ospitati anonimamente di accedere ai dati privati in assembly con livelli di attendibilità uguali o inferiori al livello di attendibilità dell'assembly che genera il codice. Per attivare la possibilità limitata di ignorare i controlli di visibilità Just-In-Time (JIT) l'applicazione host aggiunge un oggetto ReflectionPermission con il flag ReflectionPermissionFlag.RestrictedMemberAccess (RMA) alla concessione.

Ad esempio, un host potrebbe concedere alle applicazioni Internet autorizzazioni Internet più RMA, in modo che un'applicazione Internet possa generare codice che accede a dati privati nei propri assembly. Poiché l'accesso è limitato agli assembly di attendibilità uguale o inferiore, un'applicazione Internet non può accedere a membri di assembly di attendibilità totale ad esempio gli assembly di .NET Framework.

Nota:

Per impedire l'aumento dei privilegi, quando vengono costruiti i metodi dinamici ospitati anonimamente, vengono incluse le informazioni sullo stack per l'assembly che genera. Quando viene chiamato il metodo, vengono verificate le informazioni sullo stack. Pertanto, un metodo dinamico ospitato anonimamente richiamato da codice di attendibilità totale è ancora limitato al livello di attendibilità dell'assembly che genera.

Per creare un dominio applicazione con attendibilità parziale più RMA

  1. Utilizzare la funzione di supporto per recuperare il set di autorizzazioni denominato Internet dai criteri di sicurezza.

    internetSet = GetNamedPermissionSet("Internet")
    
    internetSet = GetNamedPermissionSet("Internet");
    
  2. Creare un nuovo oggetto ReflectionPermission con il flag RestrictedMemberAccess e utilizzare il metodo PermissionSet.SetPermission per aggiungere l'autorizzazione alla concessione.

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

    Il metodo AddPermission consente di aggiungere l'autorizzazione alla concessione, se non è già inclusa. Se l'autorizzazione è già inclusa nella concessione, i flag specificati vengono aggiunti all'autorizzazione esistente.

    Nota:

    RMA è una funzionalità di metodi dinamici ospitati anonimamente. Quando metodi dinamici comuni ignorano i controlli di visibilità JIT, al codice generato deve essere concesso ReflectionPermission con il flag ReflectionPermissionFlag.MemberAccess oltre al flag RestrictedMemberAccess.

  3. Creare un nuovo dominio applicazione specificando l'evidenza, le informazioni di installazione del dominio applicazione e la concessione.

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

Creazione di una cartella con autorizzazioni limitate

Nella procedura descritta di seguito viene illustrato come creare un gruppo di codice che associa autorizzazioni Internet a una cartella e come aggiungere il flag RestrictedMemberAccess alla concessione per il codice eseguito dalla cartella.

Per creare una cartella con autorizzazioni Internet

  1. Fare clic su Start, scegliere Pannello di controllo, Strumenti di amministrazione, quindi fare clic su Microsoft .NET Framework 3.5 Configuration. Per utilizzare lo strumento di configurazione è necessario disporre dei privilegi di amministratore di sistema.

  2. Nel riquadro sinistro, in .NET Framework 2.0 Configuration, espandere Risorse del computer, Criteri di sicurezza runtime, Computer, Gruppi di codice, quindi All_Code.

  3. Nel riquadro di destra, fare clic su Aggiungi gruppo di codice figlio per eseguire la creazione guidata gruppo di codice.

  4. Assegnare un nome al gruppo di codice, ad esempio "Sandbox Internet", e una descrizione, se si desidera. Fare clic su Avanti.

  5. Selezionare URL nell'elenco Specificare il tipo di condizione per il gruppo di codice. Nella casella URL, digitare il percorso completo della cartella che si desidera utilizzare e quindi fare clic su Avanti. È ad esempio possibile specificare quanto segue:

    file://c:/InternetSandbox/*
    
  6. Selezionare Internet dall'elenco Usa set di autorizzazione esistente e quindi scegliere Avanti.

    Nota:

    Per specificare un set di autorizzazioni denominato e un oggetto ReflectionPermission con il flag RestrictedMemberAccess, fare clic su Crea un nuovo set di autorizzazioni e specificare il set di autorizzazioni personalizzato utilizzando un file XML.

  7. Scegliere Fine per creare il gruppo di codice.

  8. Posizionare gli assembly che si desidera eseguire con attendibilità limitata nella cartella specificata al passaggio 5.

Esecuzione di codice nei domini applicazione creati mediante sandbox

Nella procedura descritta di seguito viene illustrato come definire una classe utilizzando metodi eseguibili in un dominio applicazione, come creare un'istanza della classe nel dominio e come eseguire i relativi metodi.

Per definire ed eseguire un metodo in un dominio applicazione

  1. Definire una classe che deriva da MarshalByRefObject. In tal modo sarà possibile creare istanze della classe negli altri domini applicazione e fare chiamate al metodo attraverso limiti del dominio applicazione. In questo esempio la classe è denominata Worker.

    Public Class Worker
        Inherits MarshalByRefObject
    
    public class Worker : MarshalByRefObject
    {
    
  2. Definire un metodo pubblico che contiene il codice da eseguire. In questo esempio, il codice genera un semplice metodo dinamico, crea un delegato per eseguire il metodo e richiama il delegato.

    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. Nel programma principale, ottenere il nome visualizzato dell'assembly. Questo nome viene utilizzato quando si creano istanze della classe Worker nel dominio applicazione creato mediante sandbox.

    Dim asmName As String = [Assembly].GetExecutingAssembly().FullName
    
    String asmName = Assembly.GetExecutingAssembly().FullName;
    
  4. Nel programma principale, creare un dominio applicazione mediante sandbox, come descritto nella prima procedura di questa procedura dettagliata. Non è necessario aggiungere autorizzazioni al set di autorizzazioni Internet impostato, perché il metodo SimpleEmitDemo utilizza solo metodi pubblici.

  5. Nel programma principale, creare un'istanza della classe Worker nel dominio applicazione creato mediante sandbox.

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

    Il metodo CreateInstanceAndUnwrap crea l'oggetto nel dominio applicazione di destinazione e restituisce un proxy che può essere utilizzato per chiamare le proprietà e i metodi dell'oggetto.

    Nota:

    Se si utilizza il codice in Visual Studio, è necessario modificare il nome della classe in modo che includa lo spazio dei nomi. Per impostazione predefinita, lo spazio dei nomi rappresenta il nome del progetto. Ad esempio, se il progetto è "PartialTrust", il nome della classe deve essere "PartialTrust.Worker".

  6. Aggiungere codice per chiamare il metodo SimpleEmitDemo. La chiamata viene sottoposta a marshalling attraverso il limite del dominio applicazione e il codice viene eseguito nel dominio applicazione creato mediante sandbox.

    w.SimpleEmitDemo()
    
    w.SimpleEmitDemo();
    

Utilizzo di metodi dinamici ospitati anonimamente

I metodi dinamici ospitati anonimamente sono associati a un assembly fornito dal sistema. Pertanto, sono isolati da codice diverso. I metodi dinamici comuni, d'altra parte, devono essere associati a un modulo o un tipo esistente.

Nota:

L'unico modo per associare un metodo dinamico all'assembly che fornisce hosting anonimo è utilizzare i costruttori descritti nella procedura riportata di seguito. Non è possibile specificare in modo esplicito un modulo nell'assembly di hosting anonimo.

I metodi dinamici comuni hanno accesso ai membri interni del modulo ai quali sono associati, o ai membri privati del tipo a cui sono associati. I metodi dinamici ospitati anonimamente sono isolati da codice diverso, pertanto non hanno accesso ai dati privati. Tuttavia, hanno una possibilità limitata di ignorare i controlli di visibilità JIT per accedere ai dati privati. Questa possibilità è limitata agli assembly con livelli di attendibilità uguali o inferiori al livello di attendibilità dell'assembly che genera il codice.

Per impedire l'aumento dei privilegi, quando vengono costruiti i metodi dinamici ospitati anonimamente, vengono incluse le informazioni sullo stack per l'assembly che genera. Quando viene chiamato il metodo, vengono verificate le informazioni sullo stack. Un metodo dinamico ospitato anonimamente richiamato da codice di attendibilità totale è ancora limitato al livello di attendibilità dell'assembly che lo ha generato.

Per utilizzare metodi dinamici ospitati anonimamente

  • Creare un metodo dinamico ospitato anonimamente utilizzando un costruttore che non specifica un modulo o un tipo associato.

    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);
    

    Se un metodo dinamico ospitato anonimamente utilizza solo tipi e metodi pubblici, non richiede accesso limitato al membro e non deve ignorare i controlli di visibilità di JIT.

    Per generare un metodo dinamico non sono necessarie autorizzazioni speciali, ma il codice generato richiede le autorizzazioni obbligatorie per i tipi e i metodi che utilizza. Ad esempio, se il codice generato chiama un metodo che accede a un file, richiede FileIOPermission. Se il livello di attendibilità non include tale autorizzazione, viene generata un'eccezione di sicurezza quando il codice generato viene eseguito. Il codice illustrato genera un metodo dinamico che utilizza solo il metodo Console.WriteLine. Pertanto, il codice può essere eseguito da percorsi parzialmente attendibili.

  • In alternativa, creare un metodo dinamico ospitato anonimamente con possibilità limitata di ignorare i controlli di visibilità JIT, utilizzando il costruttore DynamicMethod(String, Type, array<Type[], Boolean) e specificando true per il parametro restrictedSkipVisibility.

    Dim meth As New DynamicMethod("", _
                                  GetType(Char), _
                                  New Type() {GetType(String)}, _
                                  True)
    
    DynamicMethod meth = new DynamicMethod("",
                                           typeof(char), 
                                           new Type[] { typeof(String) }, 
                                           true);
    

    La limitazione consiste nel fatto che il metodo dinamico ospitato anonimamente può accedere ai dati privati solo in assembly con livelli di attendibilità uguali o inferiori al livello di attendibilità dell'assembly che genera il codice. Ad esempio, se il metodo dinamico è in esecuzione con attendibilità Internet, può accedere a dati privati negli altri assembly in esecuzione con attendibilità Internet, ma non può accedere a dati privati degli assembly .NET Framework. Gli assembly .NET Framework sono installati nella Global Assembly Cache e sono sempre totalmente attendibili.

    I metodi dinamici ospitati anonimamente possono utilizzare questa possibilità limitata per ignorare i controlli di visibilità JIT solo se l'applicazione host concede autorizzazioni ReflectionPermission con il flag ReflectionPermissionFlag.RestrictedMemberAccess. La richiesta di questa autorizzazione viene fatta quando viene richiamato il metodo.

    Nota:

    Quando viene costruito un metodo dinamico, vengono incluse le informazioni dello stack di chiamate dell'assembly che lo genera. Pertanto, la richiesta viene fatta per le autorizzazioni dell'assembly che genera anziché per l'assembly che richiama il metodo. In tal modo si evita che il codice generato venga eseguito con autorizzazioni elevate.

    Nell'esempio di codice completo alla fine di questa procedura dettagliata vengono illustrati l'utilizzo e le limitazioni di accesso al membro. La classe Worker include un metodo che può creare metodi dinamici ospitati anonimamente con o senza la possibilità limitata di ignorare controlli di visibilità. Nell'esempio viene illustrato il risultato dell'esecuzione di tale metodo in domini applicazione con livelli di attendibilità diversi.

    Nota:

    La possibilità limitata di ignorare i controlli di visibilità è una funzionalità di metodi dinamici ospitati anonimamente. Quando i metodi dinamici comuni ignorano i controlli di visibilità JIT, richiedono la concessione di ReflectionPermission con il flag ReflectionPermissionFlag.MemberAccess. Inoltre, i metodi dinamici comuni che accedono a dati privati in assembly diversi dall'assembly che genera devono avere ReflectionPermission con il flag RestrictedMemberAccess o SecurityPermission con il flag SecurityPermissionFlag.ControlEvidence.

Esempio

Descrizione

Nell'esempio di codice riportato di seguito viene illustrato l'utilizzo del flag RestrictedMemberAccess per consentire ai metodi dinamici ospitati anonimamente di ignorare i controlli di visibilità JIT, ma solo quando il membro di destinazione è a un livello di attendibilità uguale o inferiore a quello dell'assembly che genera il codice.

Nell'esempio viene definita una classe Worker di cui può essere effettuato il marshalling attraverso limiti del dominio applicazione. La classe ha due overload di metodi AccessPrivateMethod che generano ed eseguono metodi dinamici. Il primo overload genera un metodo dinamico che chiama il metodo privato PrivateMethod della classe Worker e può generare il metodo dinamico con o senza controlli di visibilità JIT. Il secondo overload genera un metodo dinamico che accede alla proprietà internal (proprietàFriend in Visual Basic) della classe String.

Nell'esempio viene utilizzato un metodo di supporto per ottenere l'autorizzazione Internet dai criteri di sicurezza e quindi creare un dominio applicazione, utilizzando l'overload del metodo AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) per specificare che ogni codice eseguito nel dominio utilizza questa concessione. Nell'esempio viene creata un'istanza della classe Worker nel dominio applicazione e viene eseguito il metodo AccessPrivateMethod due volte.

  • La prima volta che viene eseguito il metodo AccessPrivateMethod vengono applicati i controlli di visibilità JIT. Il metodo dinamico ha esito negativo quando viene richiamato, perché i controlli di visibilità JIT gli impediscono di accedere al metodo privato.

  • La seconda volta che viene eseguito il metodo AccessPrivateMethod i controlli di visibilità JIT vengono ignorati. Il metodo dinamico ha esito negativo quando viene compilato, perché la concessione Internet non è sufficiente per ignorare i controlli di visibilità.

Nell'esempio viene utilizzato un metodo di supporto per ottenere la concessione Internet e aggiungere un oggetto ReflectionPermission con ReflectionPermissionFlag.RestrictedMemberAccess alla concessione. Nell'esempio viene creato quindi un secondo dominio, specificando che tutto il codice eseguito nel dominio possiede le autorizzazioni del nuovo set. Nell'esempio viene creata un'istanza della classe Worker nel nuovo dominio applicazione e vengono eseguiti entrambi gli overload del metodo AccessPrivateMethod.

  • Viene eseguito il primo overload del metodo AccessPrivateMethod e i controlli di visibilità JIT vengono ignorati. Il metodo dinamico compila ed esegue correttamente, perché l'assembly che genera il codice corrisponde all'assembly che contiene il metodo privato. Pertanto, i livelli di attendibilità sono uguali. Se l'applicazione che contiene la classe Worker ha molti assembly, lo stesso processo riuscirebbe per uno qualsiasi di tali assembly, perché sarebbero tutti allo stesso livello di attendibilità.

  • Viene eseguito il secondo overload del metodo AccessPrivateMethod e i controlli di visibilità JIT vengono di nuovo ignorati. Questa volta il metodo dinamico ha esito negativo quando viene compilato, perché tenta di accedere alla proprietà internalFirstChar della classe String. L'assembly che contiene la classe String è totalmente attendibile. Pertanto dispone di un livello di attendibilità superiore a quello dell'assembly che genera il codice.

Nel confronto viene illustrato come ReflectionPermissionFlag.RestrictedMemberAccess consente a codice parzialmente attendibile di ignorare i controlli di visibilità per altro codice parzialmente attendibile senza compromettere la sicurezza del codice attendibile.

Codice

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.
 */

Compilazione del codice

  • Se si compila questo esempio di codice in Visual Studio, è necessario modificare il nome della classe per includere lo spazio dei nomi quando viene passato al metodo CreateInstanceAndUnwrap. Per impostazione predefinita, lo spazio dei nomi rappresenta il nome del progetto. Ad esempio, se il progetto è "PartialTrust", il nome della classe deve essere "PartialTrust.Worker".

Vedere anche

Attività

Procedura: eseguire codice parzialmente attendibile in un oggetto sandbox

Concetti

Problemi di sicurezza con la reflection emit