Procédure pas à pas : émission de code dans des scénarios de confiance partielle

Mise à jour : novembre 2007

L'émission de réflexion utilise le même jeu d'API dans les cas de confiance totale ou partielle, mais certaines fonctionnalités requièrent des autorisations spéciales dans le code d'un niveau de confiance partiel. De plus, l'émission de réflexion a une fonctionnalité, méthodes dynamiques hébergées de manière anonyme, qui est destinée à être utilisée avec la confiance partielle et par les assemblys Security Transparent.

Remarque :

Avant .NET Framework version 3.5, l'émission de code est requise ReflectionPermission avec l'indicateur ReflectionPermissionFlag.ReflectionEmit. Cette autorisation est incluse par défaut dans les jeux d'autorisations nommés FullTrust et Intranet, mais pas dans le jeu d'autorisations Internet. Par conséquent, une bibliothèque pourrait être utilisée avec une confiance partielle uniquement si elle dispose de l'attribut SecurityCriticalAttribute et si elle exécute une méthode Assert pour ReflectionEmit. Ces bibliothèques nécessitent une révision de sécurité approfondie car les erreurs de codage pourraient provoquer des failles de sécurité. .NET Framework 3.5 autorise l'émission de code dans des scénarios de confiance partielle sans émission de demandes de sécurité, parce que la génération du code n'est pas fondamentalement une opération privilégiée. Autrement dit, le code généré n'a pas plus d'autorisations que l'assembly qui l'émet. Cela permet aux bibliothèques qui émettent du code d'être Security Transparent et évite d'avoir à déclarer ReflectionEmit, de sorte que l'écriture d'une bibliothèque sécurisée ne nécessite pas de révision de sécurité approfondie.

Cette procédure pas à pas illustre les tâches suivantes :

  • Installation d'environnements de confiance partielle pour tester le code.

  • Exécution du code dans les domaines d'application de confiance partielle.

  • Utilisation de méthodes dynamiques hébergées de manière anonyme pour émettre et exécuter du code de confiance partielle.

Pour plus d'informations sur l'émission du code dans les scénarios de confiance partielle, consultez Problèmes de sécurité dans l'émission de réflexion.

Pour une liste complète du code utilisé dans ces procédures, consultez la section Exemple à la fin de cette procédure pas à pas.

Installation d'emplacements de confiance partielle

Les procédures suivantes montrent comment installer des emplacements à partir desquels le code peut être exécuté avec une confiance partielle.

  • La première procédure indique comment créer un domaine d'application placé dans le bac à sable (sandbox) dans lequel le code s'exécute avec une confiance de niveau Internet. Elle explique également un piège courant.

  • La deuxième procédure montre comment ajouter ReflectionPermission avec l'indicateur ReflectionPermissionFlag.RestrictedMemberAccess au domaine d'application de confiance partielle, en vue de permettre l'accès aux données privées dans les assemblys de niveau de confiance inférieur ou égal.

  • La troisième procédure montre comment créer un groupe de codes qui associe un niveau de confiance à un dossier, afin que les assemblys qui figurent dans ce dossier s'exécutent avec une confiance partielle.

En plus de ces procédures, vous pouvez utiliser un attribut d'assembly, comme l'élément suivant, pour exécuter un assembly sans autorisation SkipVerification dans les scénarios simples. De même, vous pouvez utiliser un attribut pour refuser l'autorisation MemberAccess.

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

Création de domaines d'application en bac à sable (Sandboxed)

Pour créer un domaine d'application dans lequel vos assemblys s'exécutent avec la confiance partielle, vous devez spécifier le jeu d'autorisations à accorder aux assemblys en utilisant la surcharge de méthode AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) pour créer le domaine d'application. Pour spécifier le jeu d'autorisations, le plus simple est de récupérer un jeu d'autorisations nommé à partir de la stratégie de sécurité.

Attention :

Vous ne pouvez pas créer un domaine d'application en bac à sable (sandbox) en spécifiant les éléments de preuve uniquement. Vous devez spécifier un jeu d'autorisations ou un niveau de stratégie de domaine d'application. (La définition d'un niveau de stratégie de domaine d'application n'est pas couverte dans cette rubrique.) Par exemple, si vous utilisez la surcharge de méthode CreateDomain(String, Evidence) avec les éléments de preuve Internet, les autorisations sont appliquées uniquement à la limite de domaine d'application. Dans le domaine d'application, les autorisations sont accordées aux assemblys sur la base de la stratégie de sécurité standard. Pour une application console sur votre ordinateur, cela équivaut à une confiance totale.

La procédure suivante crée un domaine d'application en bac à sable (sandbox) qui exécute votre code avec une confiance partielle, afin de tester des scénarios dans lesquels le code émis peut accéder uniquement aux membres publics des types publics. Une procédure suivante montre comment ajouter RestrictedMemberAccess, afin de tester des scénarios dans lesquels le code émis peut accéder aux types non public et aux membres dans les assemblys bénéficiant d'autorisations égales ou moindres.

Pour créer un domaine d'application avec une confiance partielle

  1. Utilisez la fonction d'assistance suivante pour obtenir des jeux d'autorisations nommés à partir de la stratégie de sécurité.

    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 jeu d'autorisations se situe à l'intersection des jeux d'autorisations accordées à tous les niveaux de stratégie. Autrement dit, une autorisation particulière n'est accordée que si elle est accordé à tous les niveaux de stratégie. Par conséquent, la fonction d'assistance démarre avec un jeu d'autorisations de confiance totale et énumère les niveaux de la hiérarchie de stratégie, en choisissant l'intersection de ce jeu d'autorisations avec le jeu d'autorisations défini pour chaque égal.

    Remarque :

    Vous pouvez créer des jeux d'autorisations qui contiennent toute combinaison d'autorisations en utilisant la classe PermissionSet.

    L'utilisation de la fonction d'assistance est illustrée ultérieurement dans cette procédure.

  2. Créez la preuve pour l'emplacement de confiance partielle en utilisant des zones de sécurité. Dans le cas présent, la zone Internet est utilisée.

    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. Créez un objet AppDomainSetup pour initialiser le domaine d'application avec un chemin d'accès d'application. Cet exemple de code utilise le dossier actif.

    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
  4. Utilisez la fonction d'assistance pour récupérer le jeu d'autorisations nommé à partir de la stratégie système.

    Dim internetSet As PermissionSet = GetNamedPermissionSet("Internet")
    
    PermissionSet internetSet = GetNamedPermissionSet("Internet");
    
  5. Créez le domaine d'application, en spécifiant la preuve, les informations de configuration du domaine d'application et le jeu d'autorisations.

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

    Le dernier paramètre de la surcharge de méthode AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) vous permet de spécifier un jeu des assemblys qui bénéficient de la confiance totale, à la place du jeu d'autorisations du domaine d'application. Vous n'avez pas à spécifier les assemblys .NET Framework que votre application utilise, parce que ces assemblys sont dans le Global Assembly Cache. Les assemblys dans le Global Assembly Cache ont toujours un niveau de confiance total. Vous pouvez utiliser ce paramètre pour spécifier des assemblys avec nom fort qui ne sont pas dans le Global Assembly Cache.

Ajout de RestrictedMemberAccess aux domaines en bac à sable (sandbox)

Les applications hôtes peuvent permettre aux méthodes dynamiques hébergées de manière anonyme d'avoir accès aux données privées dans les assemblys bénéficiant de niveaux de confiance inférieurs ou égaux au niveau de confiance de l'assembly qui émet le code. Pour permettre à cette capacité limitée d'ignorer les vérifications de visibilité JIT (juste-à-temps), l'application hôte ajoute un objet ReflectionPermission avec l'indicateur ReflectionPermissionFlag.RestrictedMemberAccess (RMA) au jeu d'autorisations.

Par exemple, un hôte peut accorder des autorisations Internet aux applications Internet plus RMA, afin qu'une application Internet puisse émettre du code qui accède aux données privées dans ses propres assemblys. Dans la mesure où l'accès est limité aux assemblys de confiance inférieure ou égale une application Internet ne peut pas accéder aux membres d'assemblys d'un niveau de confiance total tels que les assemblys .NET Framework.

Remarque :

Pour empêcher l'élévation de privilège, des informations de la pile pour l'assembly émetteur sont incluses lorsque des méthodes dynamiques hébergées de manière anonyme sont construites. Lorsque la méthode est appelée, les informations de la pile sont vérifiées. Ainsi, une méthode dynamique hébergée de manière anonyme qui est appelée à partir d'un code de niveau de confiance total est encore limitée au niveau de confiance de l'assembly émetteur.

Pour créer un domaine d'application avec une confiance partielle plus RMA

  1. Utilisez la fonction d'assistance pour récupérer le jeu d'autorisations nommé Internet à partir de la stratégie de sécurité.

    internetSet = GetNamedPermissionSet("Internet")
    
    internetSet = GetNamedPermissionSet("Internet");
    
  2. Créez un objet ReflectionPermission avec l'indicateur RestrictedMemberAccess et utilisez la méthode PermissionSet.SetPermission pour ajouter l'autorisation au jeu d'autorisations.

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

    La méthode AddPermission ajoute l'autorisation au jeu d'autorisations, si ce n'est déjà fait. Si l'autorisation est déjà incluse dans le jeu d'autorisations, les indicateurs spécifiés sont ajoutés à l'autorisation existante.

    Remarque :

    RMA est une fonctionnalité de méthodes dynamiques hébergées de manière anonyme. Lorsque les méthodes dynamiques ordinaires ignorent les contrôles de visibilité JIT, le code émis doit recevoir ReflectionPermission avec l'indicateur ReflectionPermissionFlag.MemberAccess en plus de l'indicateur RestrictedMemberAccess.

  3. Créez le domaine d'application, en spécifiant la preuve, les informations de configuration du domaine d'application et le jeu d'autorisations.

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

Création d'un dossier avec des autorisations restreintes

La procédure suivante montre comment créer un groupe de codes qui associe des autorisations Internet à un dossier, et comment ajouter l'indicateur RestrictedMemberAccess au jeu d'autorisations pour le code exécuté à partir du dossier.

Pour créer un dossier qui a des autorisations Internet

  1. Cliquez sur Démarrer, pointez sur Panneau de configuration, pointez sur Outils d'administration, puis cliquez sur Configuration Microsoft .NET Framework 3.5. Vous devez avoir des privilèges d'administrateur système pour utiliser l'outil de configuration.

  2. Dans le volet de gauche, sous Configuration du .NET Framework 2.0, développez Poste de travail, Stratégie de sécurité du runtime, Ordinateur, Groupes de codes , All_Code.

  3. Dans le volet de droite, cliquez sur Ajouter un groupe de codes enfant pour exécuter l'Assistant Créer un groupe de codes.

  4. Attribuez au groupe de codes un nom, tel que « Bac à sable (sandbox) Internet » et, éventuellement, une description. Cliquez sur Suivant.

  5. Dans la liste Type de condition pour ce groupe de codes, sélectionnez URL. Dans la zone URL, tapez le chemin d'accès complet du dossier que vous souhaitez utiliser, puis cliquez sur Suivant. Par exemple, vous pouvez taper ceci :

    file://c:/InternetSandbox/*
    
  6. Sélectionnez Internet dans la liste Utiliser un jeu d'autorisations existant, puis cliquez sur Suivant.

    Remarque :

    Pour spécifier un jeu d'autorisations nommé et un objet ReflectionPermission avec l'indicateur RestrictedMemberAccess, cliquez sur Créer un nouveau jeu d'autorisations et spécifiez le jeu d'autorisations personnalisé en utilisant un fichier XML.

  7. Cliquez sur Terminer pour créer le groupe de codes.

  8. Placez les assemblys que vous souhaitez exécuter avec une confiance limitée dans le dossier que vous avez spécifié à l'étape 5.

Exécution du code dans des domaines d'application en bac de sable (sandbox)

La procédure suivante explique comment définir une classe en utilisant des méthodes qui peuvent être exécutées dans un domaine d'application, comment créer une instance de la classe dans le domaine, et comment exécuter ses méthodes.

Pour définir et exécuter une méthode dans un domaine d'application

  1. Définissez une classe qui dérive de MarshalByRefObject. Cela vous permet de créer des instances de la classe dans d'autres domaines d'application et faire des appels de méthode au-delà des limites de domaine d'application. Dans cet exemple, la classe s'appelle Worker.

    Public Class Worker
        Inherits MarshalByRefObject
    
    public class Worker : MarshalByRefObject
    {
    
  2. Définissez une méthode publique qui contient le code à exécuter. Dans cet exemple, le code émet une méthode dynamique simple, crée un délégué pour exécuter la méthode et appelle le délégué.

    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. Dans votre programme principal, obtenez le nom complet de votre assembly. Ce nom est utilisé lorsque vous créez des instances de la classe Worker dans le domaine d'application en bac à sable (sandbox).

    Dim asmName As String = [Assembly].GetExecutingAssembly().FullName
    
    String asmName = Assembly.GetExecutingAssembly().FullName;
    
  4. Dans votre programme principal, créez un domaine d'application en bac à sable (sandbox), comme décrit dans la première procédure de cette procédure pas à pas. Vous n'avez à ajouter aucune autorisation au jeu d'autorisations Internet, parce que la méthode SimpleEmitDemo utilise des méthodes publiques uniquement.

  5. Dans votre programme principal, créez une instance de la classe Worker dans le domaine d'application en bac à sable (sandbox).

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

    La méthode CreateInstanceAndUnwrap crée l'objet dans le domaine d'application cible et retourne un proxy qui peut être utilisé pour appeler les propriétés et méthodes de l'objet.

    Remarque :

    Si vous utilisez ce code dans Visual Studio, vous devez modifier le nom de la classe pour inclure l'espace de noms. Par défaut, l'espace de noms est le nom du projet. Par exemple, si le projet est « PartialTrust », le nom de classe doit être « PartialTrust.Worker ».

  6. Ajoutez du code pour appeler la méthode SimpleEmitDemo. L'appel est marshalé à travers la limite du domaine d'application, et le code est exécuté dans le domaine d'application placée en bac à sable (sandbox).

    w.SimpleEmitDemo()
    
    w.SimpleEmitDemo();
    

Utilisation des méthodes dynamiques hébergées de manière anonyme

Les méthodes dynamiques hébergées de manière anonyme sont associées à un assembly fourni par le système. Par conséquent, elles sont isolées du reste de code. Les méthodes dynamiques ordinaires, en revanche, doivent être associées à un module existant ou type.

Remarque :

La seule méthode pour associer une méthode dynamique à l'assembly qui fournit l'hébergement anonyme est d'utiliser les constructeurs décrits dans la procédure suivante. Vous ne pouvez pas spécifier explicitement de module dans l'assembly d'hébergement anonyme.

Les méthodes dynamiques ordinaires ont accès aux membres internes du module auquel elles sont associées, ou aux membres privés du type auquel elles sont associées. Dans la mesure où les méthodes dynamiques hébergées de manière anonyme sont isolées du reste du code, elles n'ont pas accès aux données privées. Toutefois, elles ont une capacité restreinte d'ignorer les vérifications de visibilité JIT pour accéder aux données privées. Cette capacité est limitée aux assemblys qui ont des niveaux de confiance inférieure ou égaux au niveau de confiance de l'assembly qui émet le code.

Pour empêcher l'élévation de privilège, des informations de la pile pour l'assembly émetteur sont incluses lorsque des méthodes dynamiques hébergées de manière anonyme sont construites. Lorsque la méthode est appelée, les informations de la pile sont vérifiées. Une méthode dynamique hébergée de manière anonyme qui est appelée à partir d'un code de niveau de confiance total est encore limitée au niveau de confiance de l'assembly à l'origine de son émission.

Pour utiliser des méthodes dynamiques hébergées de manière anonyme

  • Créez une méthode dynamique hébergée de manière anonyme en utilisant un constructeur qui ne spécifie pas un module associé ou type.

    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 une méthode dynamique hébergée de manière anonyme utilise uniquement des types publics et des méthodes, elle ne requiert pas d'accès de membre restreint et n'a pas à ignorer les contrôles de visibilité JIT.

    Aucune autorisation spéciale n'est requise pour émettre une méthode dynamique, mais le code émis requiert les autorisations demandées par les types et méthodes qu'il utilise. Par exemple, si le code émis appelle une méthode qui accède à un fichier, il requiert FileIOPermission. Si le niveau de confiance n'inclut pas cette autorisation, une exception de sécurité est levée lorsque le code émis est exécuté. Le code montré ici émet une méthode dynamique qui utilise uniquement la méthode Console.WriteLine. Par conséquent, le code peut être exécuté à partir d'emplacements de confiance partielle.

  • Ou bien, créez une méthode dynamique hébergée de manière anonyme avec capacité restreinte pour ignorer les contrôles de visibilité JIT, en utilisant le constructeur DynamicMethod(String, Type, array<Type[], Boolean) et en spécifiant true pour le paramètre 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 restriction est que la méthode dynamique hébergée de manière anonyme peut accéder uniquement aux données privées dans les assemblys avec des niveaux de confiance inférieurs ou égaux au niveau de confiance de l'assembly d'émission. Par exemple, si la méthode dynamique s'exécute avec la confiance Internet, elle peut accéder aux données privées dans les autres assemblys qui s'exécutent également avec la confiance Internet, mais elle ne peut pas accéder aux données privées des assemblys .NET Framework. Les assemblys .NET Framework sont installés dans le Global Assembly Cache et sont toujours d'un niveau de confiance total.

    Les méthodes dynamiques hébergées de manière anonyme peuvent utiliser cette capacité restreinte pour ignorer les vérifications de visibilité JIT uniquement si l'application hôte accorde ReflectionPermission avec l'indicateur ReflectionPermissionFlag.RestrictedMemberAccess. La demande pour cette autorisation est faite lorsque la méthode est appelée.

    Remarque :

    Les informations de la pile d'appel pour l'assembly d'émission sont incluses lors de la construction de la méthode dynamique. Par conséquent, la demande est faite par rapport aux autorisations de l'assembly d'émission et non de l'assembly qui appelle la méthode. Cela empêche l'exécution du code émis avec des autorisations élevées.

    L'exemple de code complet à la fin de cette procédure pas à pas illustre l'utilisation et les limitations d'accès de membre restreint. Sa classe Worker inclut une méthode qui peut créer des méthodes dynamiques hébergées de manière anonyme avec ou sans la capacité restreinte d'ignorer les contrôles de visibilité, et l'exemple montre le résultat de l'exécution de cette méthode dans les domaines d'application qui ont des niveaux de confiance différents.

    Remarque :

    La capacité restreinte d'ignorer les contrôles de visibilité est une caractéristique des méthodes dynamiques hébergées de manière anonyme. Lorsque les méthodes dynamiques ordinaires ignorent les contrôles de visibilité JIT, il faut leur accorder ReflectionPermission avec l'indicateur ReflectionPermissionFlag.MemberAccess. De plus, les méthodes dynamiques ordinaires qui accèdent aux données privées dans les assemblys autres que l'assembly émetteur doivent avoir ReflectionPermission avec l'indicateur RestrictedMemberAccess ou SecurityPermission avec l'indicateur SecurityPermissionFlag.ControlEvidence.

Exemple

Description

L'exemple de code suivant montre l'utilisation de l'indicateur RestrictedMemberAccess pour permettre aux méthodes dynamiques hébergées de manière anonyme d'ignorer les contrôles de visibilité JIT, mais uniquement lorsque le membre cible a un niveau de confiance inférieur ou égal à celui de l'assembly qui émet le code.

L'exemple définit une classe Worker qui peut être marshalée au-delà des limites du domaine d'application. La classe a deux surcharges de méthode AccessPrivateMethod qui émettent et exécutent des méthodes dynamiques. La première surcharge émet une méthode dynamique qui appelle la méthode PrivateMethod privée de la classe Worker, et elle peut émettre la méthode dynamique avec ou sans contrôles de visibilité JIT. La seconde surcharge émet une méthode dynamique qui accède à une propriété internal (propriétéFriend dans Visual Basic) de la classe String.

L'exemple utilise une méthode d'assistance pour obtenir le jeu d'autorisations Internet à partir de la stratégie de sécurité, puis crée un domaine d'application, en utilisant la surcharge de méthode AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) pour spécifier que tout le code qui s'exécute dans ce domaine utilise ce jeu d'autorisations. L'exemple crée une instance de la classe Worker dans le domaine d'application et exécute la méthode AccessPrivateMethod deux fois.

  • La première fois que la méthode AccessPrivateMethod est exécutée, les contrôles de visibilité JIT sont appliqués. La méthode dynamique échoue lorsqu'elle est appelée, parce que les contrôles de visibilité JIT l'empêchent d'accéder à la méthode privée.

  • La seconde fois que la méthode AccessPrivateMethod est exécutée, les contrôles de visibilité JIT sont ignorés. La méthode dynamique échoue lors de la compilation, parce que le jeu d'autorisations Internet n'accorde pas d'autorisations suffisantes pour ignorer les contrôles de visibilité.

L'exemple utilise une méthode d'assistance pour obtenir le jeu d'autorisations Internet et ajoute ReflectionPermission avec ReflectionPermissionFlag.RestrictedMemberAccess au jeu d'autorisations. L'exemple crée ensuite un deuxième domaine, en spécifiant que les autorisations du nouveau jeu d'autorisations sont accordées à l'ensemble du code qui s'exécute dans le domaine. L'exemple crée une instance de la classe Worker dans le nouveau domaine d'application et exécute les deux surcharges de la méthode AccessPrivateMethod.

  • La première surcharge de la méthode AccessPrivateMethod est exécutée et les contrôles de visibilité JIT sont ignorés. La méthode dynamique est compilée et exécutée avec succès, parce que l'assembly qui émet le code est le même que l'assembly qui contient la méthode privée. Par conséquent, les niveaux de confiance sont égaux. Si l'application qui contient la classe Worker avait plusieurs assemblys, le même processus réussirait pour chacun de ces assemblys, parce qu'ils seraient tous au même niveau de confiance.

  • La seconde surcharge de la méthode AccessPrivateMethod est exécutée et les contrôles de visibilité JIT sont à nouveau ignorés. Cette fois-ci, la méthode dynamique échoue lors de la compilation, parce qu'elle essaie d'accéder à la propriété internal FirstChar de la classe String. L'assembly qui contient la classe String est d'un niveau de confiance suffisant. Par conséquent, il s'agit d'un niveau de confiance supérieur à celui de l'assembly qui émet le code.

Cette comparaison montre comment ReflectionPermissionFlag.RestrictedMemberAccess permet à un code de niveau de confiance partiel d'ignorer les contrôles de visibilité pour un autre code de niveau de confiance partiel sans compromettre la sécurité du code de confiance.

Code

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

Compilation du code

  • Si vous générez cet exemple de code dans Visual Studio, vous devez modifier le nom de la classe pour inclure l'espace de noms lorsque vous le passez à la méthode CreateInstanceAndUnwrap. Par défaut, l'espace de noms est le nom du projet. Par exemple, si le projet est « PartialTrust », le nom de classe doit être « PartialTrust.Worker ».

Voir aussi

Tâches

Comment : exécuter du code d'un niveau de confiance partiel dans un bac à sable (sandbox)

Concepts

Problèmes de sécurité dans l'émission de réflexion