Tutorial: Emitir código en escenarios que no son de plena confianza

Actualización: noviembre 2007

La emisión de reflexión usa el mismo conjunto de API con confianza completa o parcial, pero algunas características requieren permisos especiales en entornos de confianza parcial. Además, la emisión de reflexión tiene una característica, los métodos dinámicos hospedados de forma anónima, diseñada para su uso en entornos de confianza parcial y por ensamblados transparentes en seguridad.

Nota:

Antes de .NET Framework versión 3.5, la emisión de código requería ReflectionPermission con el marcador ReflectionPermissionFlag.ReflectionEmit. Este permiso está incluido de forma predeterminada en los conjuntos de permisos con nombre FullTrust e Intranet, pero no en el conjunto de permisos Internet. Por consiguiente, sólo se podía usar una biblioteca de código de confianza parcial si tenía el atributo SecurityCriticalAttribute y ejecutaba además un método Assert para ReflectionEmit. Estas bibliotecas requieren una revisión cuidadosa de la seguridad porque los errores de codificación pueden provocar carencias de seguridad. .NET Framework 3.5 permite emitir el código en escenarios de confianza parcial sin emitir ninguna petición de seguridad, porque la generación de código no es en sí una operación de privilegio. Es decir, el código generado no tiene más permisos que el ensamblado que lo emite. Esto permite que las bibliotecas que emiten código sean transparentes en seguridad y elimina la necesidad de validar ReflectionEmit, de manera que escribir una biblioteca segura no requiere este tipo de revisión exhaustiva de la seguridad.

En este tutorial se muestran las tareas siguientes:

  • Preparar entornos de confianza parcial para probar el código.

  • Ejecutar código en dominios de aplicación de confianza parcial.

  • Usar métodos dinámicos hospedados de forma anónima para emitir y ejecutar el código en entornos de confianza parcial.

Para obtener más información acerca de cómo emitir código en escenarios de confianza parcial, vea Problemas de seguridad en la emisión de la reflexión.

Para obtener una lista completa del código mostrado en estos procedimientos, vea la sección Ejemplo al final de este tutorial.

Preparar las ubicaciones de confianza parcial

En los procedimientos siguientes se muestra cómo preparar las ubicaciones desde las que se ejecutará el código con confianza parcial.

  • En el primer procedimiento se muestra cómo crear un dominio de aplicación en un recinto de seguridad en el que el código se ejecuta con confianza en Internet. También se explica un problema habitual.

  • En el segundo procedimiento se muestra cómo agregar ReflectionPermission con el marcador ReflectionPermissionFlag.RestrictedMemberAccess al dominio de aplicación de confianza parcial, y de este modo habilitar a los ensamblados con una confianza igual o menor para el acceso a los datos privados.

  • En el tercer procedimiento se muestra cómo crear un grupo de código que asocia un nivel de confianza a una carpeta, para que cualquier ensamblado ubicado en esa carpeta se ejecute con confianza parcial.

Además de estos procedimientos, en escenarios simples puede usar un atributo de ensamblado como el siguiente para ejecutar un ensamblado sin el permiso SkipVerification. De igual manera, puede usar un atributo para rechazar el permiso MemberAccess.

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

Crear dominios de aplicación en recintos de seguridad

Para crear un dominio de aplicación en el que los ensamblados se ejecuten con confianza parcial, debe especificar el conjunto de permisos que se va a conceder a los ensamblados mediante la sobrecarga del método AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) para crear el dominio de aplicación. La manera más fácil de especificar el conjunto de permisos concedidos es recuperar un conjunto de permisos con nombre de la directiva de seguridad.

Precaución:

No puede crear un dominio de aplicación en un recinto de seguridad especificando únicamente la evidencia. Debe especificar un conjunto de permisos concedidos o un nivel directiva del dominio de aplicación. (En este tema no se explica cómo establecer un nivel de directiva del dominio de aplicación.) Por ejemplo, si usa la sobrecarga del método CreateDomain(String, Evidence) con la evidencia de Internet, sólo se exigen los permisos en el límite del dominio de aplicación. En el dominio de aplicación, los permisos se conceden a los ensamblados en función de la directiva de seguridad estándar. Para una aplicación de consola en su equipo, los permisos serían de plena confianza.

En el procedimiento siguiente se crea un dominio de aplicación en un recinto de seguridad que ejecuta el código de confianza parcial, para probar los escenarios en los que el código emitido sólo puede tener acceso a los miembros públicos de tipos públicos. En un procedimiento posterior se muestra cómo agregar RestrictedMemberAccess para probar los escenarios en los que el código emitido puede tener acceso a los tipos y miembros no públicos de los ensamblados con permisos iguales o menores.

Para crear un dominio de aplicación con confianza parcial

  1. Use la siguiente función auxiliar para obtener los conjuntos de permisos con nombre de la directiva de seguridad.

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

    Un conjunto de permisos concedidos es la intersección de los conjuntos de permisos concedidos en todos los niveles de directiva. Es decir, un permiso determinado no se concede a menos que se conceda en todos los niveles de directiva. Por consiguiente, la función auxiliar se inicia con un conjunto de permisos de plena confianza concedidos y enumera los niveles de la jerarquía de directivas, tomando la intersección de ese conjunto de permisos concedidos con el conjunto de permisos definido para cada nivel.

    Nota:

    Puede crear conjuntos de permisos concedidos que contengan cualquier combinación de permisos; para ello, use la clase PermissionSet.

    El uso de la función auxiliar se muestra más adelante en este procedimiento.

  2. Cree la evidencia de la ubicación de confianza parcial usando zonas de seguridad. En este caso, se usa la zona 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. Cree un objeto AppDomainSetup para inicializar el dominio de aplicación con una ruta de aplicación. En el ejemplo de código se usa la carpeta actual.

    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
  4. Use la función auxiliar para recuperar el conjunto de permisos con nombre de la directiva del sistema.

    Dim internetSet As PermissionSet = GetNamedPermissionSet("Internet")
    
    PermissionSet internetSet = GetNamedPermissionSet("Internet");
    
  5. Cree el dominio de aplicación y especifique la evidencia, la información de configuración del dominio de aplicación y el conjunto de permisos concedidos.

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

    El último parámetro de la sobrecarga del método AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) permite especificar un conjunto de ensamblados a los que se concederá plena confianza, en lugar del conjunto de permisos concedidos del dominio de aplicación. No es necesario especificar los ensamblados de .NET Framework que la aplicación usa, porque esos ensamblados están en la caché de ensamblados global. Los ensamblados que están en la caché de ensamblados global siempre son de plena confianza. Puede usar este parámetro para especificar ensamblados con nombre seguro que no están en la caché de ensamblados global.

Agregar RestrictedMemberAccess a dominios en recintos de seguridad

Las aplicaciones host pueden permitir que los métodos dinámicos hospedados de forma anónima tengan acceso a los datos privados de ensamblados con niveles de confianza igual o menor que el nivel de confianza del ensamblado que emite el código. Para habilitar esta capacidad restringida para omitir las comprobaciones de visibilidad Just-In-Time (JIT), la aplicación host agrega un objeto ReflectionPermission con el marcador ReflectionPermissionFlag.RestrictedMemberAccess (RMA) al conjunto de permisos concedidos.

Por ejemplo, un host podría conceder a aplicaciones de Internet permisos de Internet más RMA, de manera que una aplicación de Internet pueda emitir código que tiene acceso a los datos privados de sus propios ensamblados. Debido a que el acceso se limita a los ensamblados con una confianza igual o menor, una aplicación de Internet no puede tener acceso a los miembros de ensamblados de plena confianza, como los ensamblados de .NET Framework.

Nota:

Para evitar la elevación de privilegios, la información de pila del ensamblado emisor se incluye al construir los métodos dinámicos hospedados de forma anónima. Cuando se invoca el método, se comprueba la información de la pila. Así, un método dinámico hospedado de forma anónima que se invoca desde código de plena confianza seguirá limitado al nivel de confianza del ensamblado emisor.

Para crear un dominio de aplicación con confianza parcial más RMA

  1. Use la función auxiliar para recuperar el conjunto de permisos con nombre Internet de la directiva de seguridad.

    internetSet = GetNamedPermissionSet("Internet")
    
    internetSet = GetNamedPermissionSet("Internet");
    
  2. Cree un nuevo objeto ReflectionPermission con el marcador RestrictedMemberAccess y use el método PermissionSet.SetPermission para agregar el permiso al conjunto de permisos concedidos.

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

    El método AddPermission agrega el permiso al conjunto de permisos concedidos si aún no está incluido. Si el permiso ya está incluido en el conjunto de permisos concedidos, los marcadores especificados se agregan al permiso existente.

    Nota:

    RMA es una característica de los métodos dinámicos hospedados de forma anónima. Cuando los métodos dinámicos normales omiten las comprobaciones de visibilidad JIT, se debe conceder ReflectionPermission al código emitido con el marcador ReflectionPermissionFlag.MemberAccess además del marcador RestrictedMemberAccess.

  3. Cree el dominio de aplicación y especifique la evidencia, la información de configuración del dominio de aplicación y el conjunto de permisos concedidos.

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

Crear una carpeta con permisos restringidos

En el procedimiento siguiente se muestra cómo crear un grupo de código que asocia los permisos de Internet a una carpeta y cómo agregar el marcador RestrictedMemberAccess al conjunto de permisos concedidos del código que se ejecuta desde la carpeta.

Para crear una carpeta que tiene permisos de Internet

  1. Haga clic en Inicio, elija Panel de control, Herramientas administrativas y, a continuación, haga clic en Configuración de Microsoft .NET Framework 3.5. Para usar la herramienta de configuración, debe tener privilegios de administrador del sistema.

  2. En el panel izquierdo, en Configuración de .NET Framework 2.0, expanda Mi PC, Directiva de seguridad en tiempo de ejecución, Equipo, Grupos de código y, finalmente, All_Code.

  3. En el panel derecho, haga clic en Agregar un grupo de código secundario para ejecutar el asistente Crear grupo de código.

  4. Asigne al grupo de código un nombre, como "Recinto de Internet" y, opcionalmente, una descripción. Haga clic en Siguiente.

  5. En la lista Tipo de condición para este grupo de código, seleccione Dirección URL. En el cuadro Dirección URL, escriba la ruta de acceso completa de la carpeta que desea usar y, a continuación, haga clic en Siguiente. Por ejemplo, podría escribir:

    file://c:/InternetSandbox/*
    
  6. Seleccione Internet en la lista Usar conjunto de permisos existente y, a continuación, haga clic en Siguiente.

    Nota:

    Para especificar un conjunto de permisos con nombre y un objeto ReflectionPermission con el marcador RestrictedMemberAccess, haga clic en Crear un nuevo conjunto de permisos y especifique el conjunto de permisos personalizado mediante un archivo XML.

  7. Haga clic en Finalizar para crear el grupo de código.

  8. Coloque los ensamblados que desea ejecutar con confianza limitada en la carpeta especificada en el paso 5.

Ejecutar código en dominios de aplicación en un recinto de seguridad

En el procedimiento siguiente se explica cómo definir una clase mediante métodos que se pueden ejecutar en un dominio de aplicación, cómo crear una instancia de la clase en el dominio y cómo ejecutar sus métodos.

Para definir y ejecutar un método en un dominio de aplicación

  1. Defina una clase que se derive de MarshalByRefObject. Esto permite crear instancias de la clase en otros dominios de aplicación y realizar llamadas a métodos en los límites del dominio de aplicación. En este ejemplo, la clase se denomina Worker.

    Public Class Worker
        Inherits MarshalByRefObject
    
    public class Worker : MarshalByRefObject
    {
    
  2. Defina un método público que contenga el código que desea ejecutar. En este ejemplo, el código emite un método dinámico simple, crea un delegado para ejecutar el método e invoca al delegado.

    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. En el programa principal, obtenga el nombre para mostrar del ensamblado. Este nombre se usa al crear instancias de la clase Worker en el dominio de aplicación en un recinto de seguridad.

    Dim asmName As String = [Assembly].GetExecutingAssembly().FullName
    
    String asmName = Assembly.GetExecutingAssembly().FullName;
    
  4. En el programa principal, cree un dominio de aplicación en un recinto de seguridad, como se describe en el primer procedimiento de este tutorial. No es necesario agregar ningún permiso al conjunto de permisos Internet, porque el método SimpleEmitDemo sólo usa métodos públicos.

  5. En el programa principal, cree una instancia de la clase Worker en el dominio de aplicación en un recinto de seguridad.

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

    El método CreateInstanceAndUnwrap crea el objeto en el dominio de aplicación de destino y devuelve un proxy que se puede usar para llamar a las propiedades y métodos del objeto.

    Nota:

    Si usa este código en Visual Studio, debe cambiar el nombre de la clase para que incluya el espacio de nombres. De forma predeterminada, el espacio de nombres es el nombre del proyecto. Por ejemplo, si el proyecto es "PartialTrust", el nombre de clase debe ser "PartialTrust.Worker".

  6. Agregue código para llamar al método SimpleEmitDemo. Se calculan las referencias de la llamada en el límite del dominio de aplicación y el código se ejecuta en el dominio de aplicación en un recinto de seguridad.

    w.SimpleEmitDemo()
    
    w.SimpleEmitDemo();
    

Usar métodos dinámicos hospedados de forma anónima

Los métodos dinámicos hospedados de forma anónima están asociados a un ensamblado proporcionado por el sistema. Por consiguiente, están aislados del resto del código. Los métodos dinámicos normales, por otro lado, deben estar asociados a un módulo o tipo existente.

Nota:

La única manera de asociar un método dinámico al ensamblado que proporciona hospedaje anónimo es usar los constructores que se describen en el procedimiento siguiente. No puede especificar explícitamente un módulo en el ensamblado hospedado de forma anónima.

Los métodos dinámicos normales tienen acceso a los miembros internos del módulo al que están asociados o a los miembros privados del tipo al que están asociados. Debido a que los métodos dinámicos de hospedaje anónimo están aislados del resto del código, no tienen acceso a los datos privados. Sin embargo, tienen una capacidad restringida para omitir las comprobaciones de visibilidad JIT para obtener acceso a los datos privados. Esta capacidad se limita a los ensamblados que tienen un nivel de confianza igual o menor que el nivel de confianza del ensamblado que emite el código.

Para evitar la elevación de privilegios, la información de pila del ensamblado emisor se incluye al construir los métodos dinámicos hospedados de forma anónima. Cuando se invoca el método, se comprueba la información de la pila. Un método dinámico hospedado de forma anónima que se invoca desde código de plena confianza seguirá limitado al nivel de confianza del ensamblado que lo emitió.

Para usar métodos dinámicos hospedados de forma anónima

  • Cree un método dinámico hospedado de forma anónima mediante un constructor que no especifique ningún módulo o tipo asociado.

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

    Si un método dinámico hospedado de forma anónima sólo usa tipos y métodos públicos, no necesita acceso restringido a los miembros y no tiene que omitir las comprobaciones de visibilidad JIT.

    No son necesarios permisos especiales para emitir un método dinámico, pero el código emitido requiere los permisos exigidos por los tipos y métodos que use. Por ejemplo, si el código emitido llama a un método que tiene acceso a un archivo, requiere FileIOPermission. Si el nivel de confianza no incluye ese permiso, se produce una excepción de seguridad al ejecutar el código emitido. El código mostrado aquí emite un método dinámico que sólo usa el método Console.WriteLine. Por consiguiente, el código se puede ejecutar desde ubicaciones de confianza parcial.

  • Opcionalmente, para crear un método dinámico de host anónimo con capacidad restringida para omitir las comprobaciones de visibilidad JIT, puede usar el constructor DynamicMethod(String, Type, array<Type[], Boolean) y especificar true para el parámetro 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 restricción es que el método dinámico hospedado de forma anónima sólo puede tener acceso a los datos privados de ensamblados con niveles de confianza iguales o menores que el nivel de confianza del ensamblado emisor. Por ejemplo, si el método dinámico se ejecuta con confianza en Internet, puede tener acceso a los datos privados de otros ensamblados que también se ejecutan con confianza en Internet, pero no puede tener acceso a los datos privados de ensamblados de .NET Framework. Los ensamblados de .NET Framework se instalan en la caché de ensamblados global y son siempre de plena confianza.

    Los métodos dinámicos hospedados de forma anónima pueden usar esta capacidad restringida para omitir las comprobaciones de visibilidad JIT sólo si la aplicación host concede ReflectionPermission con el marcador ReflectionPermissionFlag.RestrictedMemberAccess. La petición de este permiso se realiza al invocar el método.

    Nota:

    La información de pila de llamadas del ensamblado emisor se incluye al construir el método dinámico. Por consiguiente, se realiza una petición de los permisos del ensamblado emisor, no del ensamblado que invoca el método. Así se evita que el código emitido se ejecute con permisos elevados.

    El ejemplo de código completo al final de este tutorial muestra el uso y las limitaciones de acceso a miembros restringidos. Su clase Worker incluye un método que puede crear métodos dinámicos hospedados de forma anónima con o sin la capacidad restringida de omitir las comprobaciones de visibilidad y el ejemplo muestra el resultado de ejecutar este método en dominios de aplicación que tienen niveles de confianza diferentes.

    Nota:

    La capacidad restringida de omitir las comprobaciones de visibilidad es una característica de los métodos dinámicos hospedados de forma anónima. Cuando los métodos dinámicos normales omiten las comprobaciones de visibilidad JIT, se les debe conceder ReflectionPermission con el marcador ReflectionPermissionFlag.MemberAccess. Además, los métodos dinámicos normales que tienen acceso a los datos privados de ensamblados distintos del ensamblado emisor, deben tener ReflectionPermission con el marcador RestrictedMemberAccess o SecurityPermission con el marcador SecurityPermissionFlag.ControlEvidence.

Ejemplo

Descripción

En el ejemplo de código siguiente se muestra el uso del marcador RestrictedMemberAccess para permitir que los métodos dinámicos hospedados de forma anónima omitan las comprobaciones de visibilidad JIT, pero sólo cuando el miembro de destino tiene un nivel de confianza igual o menor que el ensamblado que emite el código.

El ejemplo define una clase Worker cuyas referencias se pueden calcular en los límites del dominio de aplicación. La clase tiene dos sobrecargas de método AccessPrivateMethod que emiten y ejecutan métodos dinámicos. La primera sobrecarga emite un método dinámico que llama al método privado PrivateMethod de la clase Worker y puede emitir el método dinámico con o sin comprobaciones de visibilidad JIT. La segunda sobrecarga emite un método dinámico que tiene acceso a una propiedad (propiedad Friend en Visual Basic) internal de la clase String.

El ejemplo usa un método de aplicación auxiliar para obtener el conjunto de permisos Internet de la directiva de seguridad y, a continuación, crea un dominio de aplicación mediante la sobrecarga del método AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) para especificar que todo el código que se ejecute en el dominio usa este conjunto de permisos concedidos. El ejemplo crea una instancia de la clase Worker en el dominio de aplicación y ejecuta el método AccessPrivateMethod dos veces.

  • La primera vez que se ejecuta el método AccessPrivateMethod, se exigen las comprobaciones de visibilidad JIT. Se producirá un error en el método dinámico cuando se invoca, porque las comprobaciones de visibilidad JIT le impiden el acceso al método privado.

  • La segunda vez que se ejecuta el método AccessPrivateMethod, se omiten las comprobaciones de visibilidad JIT. Se producirá un error en el método dinámico al compilarse, porque el conjunto de permisos concedidos Internet no concede permisos suficientes para omitir las comprobaciones de visibilidad.

El ejemplo usa un método de aplicación auxiliar para obtener el conjunto de permisos concedidos Internet y agrega ReflectionPermission con ReflectionPermissionFlag.RestrictedMemberAccess al conjunto de permisos concedidos. A continuación, el ejemplo crea un segundo dominio y especifica que todo el código que se ejecute en el dominio tenga los permisos del nuevo conjunto de permisos concedidos. El ejemplo crea una instancia de la clase Worker en el nuevo dominio de aplicación y ejecuta ambas sobrecargas del método AccessPrivateMethod.

  • Se ejecuta la primera sobrecarga del método AccessPrivateMethod y se omiten las comprobaciones de visibilidad JIT. El método dinámico se compila y se ejecuta correctamente, porque el ensamblado que emite el código es igual que el ensamblado que contiene el método privado. Por consiguiente, los niveles de confianza son iguales. Si la aplicación que contiene la clase Worker tuviera varios ensamblados, el mismo proceso sería correcto para cualquiera de esos ensamblados, porque todos tendrían el mismo nivel de confianza.

  • Se ejecuta la segunda sobrecarga del método AccessPrivateMethod y, de nuevo, se omiten las comprobaciones de visibilidad JIT. Esta vez, se produce un error en el método dinámico al compilarse porque intenta tener acceso a la propiedad internalFirstChar de la clase String. El ensamblado que contiene la clase String es de plena confianza. Por consiguiente, está en un nivel de confianza superior al del ensamblado que emite el código.

Esta comparación muestra cómo ReflectionPermissionFlag.RestrictedMemberAccess habilita al código de confianza parcial para omitir las comprobaciones de visibilidad de otro código de confianza parcial sin poner en peligro la seguridad del código de confianza.

Código

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

Compilar el código

  • Si genera este código de ejemplo en Visual Studio, debe cambiar el nombre de la clase para que incluya el espacio de nombres al pasarla al método CreateInstanceAndUnwrap. De forma predeterminada, el espacio de nombres es el nombre del proyecto. Por ejemplo, si el proyecto es "PartialTrust", el nombre de clase debe ser "PartialTrust.Worker".

Vea también

Tareas

Cómo: Ejecutar código de confianza parcial en un recinto

Conceptos

Problemas de seguridad en la emisión de la reflexión