47 votes

Dans .NET 4.0, comment dois-je 'bac à sable' une mémoire de l'assemblée et exécuter une méthode?

Voici la raison pour laquelle cette question a été posée: www.devplusplus.com/Tests/CSharp/Hello_World.

Si des questions similaires ont été posées, le nombre de réponses en ligne ont plusieurs questions:

  1. Cela doit être fait ".Net 4.0" style", pas en mode legacy.
  2. L'assemblée est en mémoire et ne sera en mémoire, il ne peut pas être écrit dans le fichier système.
  3. Je voudrais limiter l'accès au système de fichiers, réseau, etc.

Quelque chose comme ceci:

    var evidence = new Evidence();
    evidence.AddHostEvidence(new Zone(SecurityZone.Internet));
    var permissionSet = SecurityManager.GetStandardSandbox(evidence);

Jusqu'à présent, je ne peux pas trouver un moyen de créer un domaine d'application et de charger un assembly QUI n'EST PAS SUR LE SYSTÈME de FICHIERS, mais plutôt dans la RAM.

Encore une fois, les raisons pour lesquelles les autres solutions ne fonctionnent pas sont identifiées ci-dessus: 1. Beaucoup ont été pré-4.0, et 2. De nombreux invoqué le ".Charge" méthode de pointage pour le système de fichiers.

Réponse 2: j'ai une référence d'assembly parce qu'elle est générée par l' CSharpCodeProvider classe, donc si vous connaissez un moyen de tourner que dans un tableau d'octets, ce serait parfait!

Exemple de Code pour Afficher La Faille de Sécurité

var provider = new CSharpCodeProvider(new Dictionary<String, String>
    { { "CompilerVersion", "v4.0" } });

var compilerparams = new CompilerParameters
    { GenerateExecutable = false, GenerateInMemory = true, };

var compilerResults = provider.CompileAssemblyFromSource(compilerparams,
    string_Of_Code_From_A_User);

var instanceOfSomeClass = compilerResults.CompiledAssembly
    .CreateInstance(className);

// The 'DoSomething' method can write to the file system and I don't like that!
instanceOfSomeClass.GetType().GetMethod("DoSomething")
    .Invoke(instanceOfSomeClass, null);

Alors, pourquoi ne puis-je pas simplement enregistrer l'assemblage d'abord dans un fichier?

Pour deux raisons:

  1. Ce code est sur un serveur web partagé avec des autorisations limitées pour le système de fichiers lui-même.
  2. Ce code peut-être être exécuté potentiellement des milliers de fois, et je ne veux pas de 1 000 dll, même temporairement.

42voto

MikeP Points 4823

OK, tout d'abord: il n'y a pas de véritable moyen d'utiliser le CSharpCodeProvider de faire de la compilation dynamique de source C# entièrement en mémoire. Il existe des méthodes qui semblent à l'appui de cette fonctionnalité, mais depuis que le compilateur C# est un fichier exécutable natif qui ne peuvent pas s'exécuter dans le processus, la chaîne source est enregistré dans un fichier temporaire, le compilateur est invoqué sur ce fichier, puis l'assemblage qui en résulte est enregistrée sur le disque, puis chargé à l'aide de l'Assemblée.De la charge.

Deuxièmement, comme vous l'avez découvert, vous devriez être en mesure d'utiliser la méthode de Compilation à partir de l'intérieur du domaine d'application de la charge de l'assemblage et de lui donner les autorisations souhaitées. Je suis tombé sur cette même comportement inhabituel, et après beaucoup de creuser trouvé que c'était un bug dans le cadre. J'ai déposé un rapport sur MS Connecter.

Car le cadre est déjà en train d'écrire sur le système de fichiers de toute façon, la solution de contournement est d'avoir de l'assemblée écrites dans un fichier temporaire, puis chargé en tant que de besoin. Lorsque vous chargez toutefois, vous aurez besoin temporairement de faire valoir les autorisations dans le domaine d'application, puisque vous avez refusé l'accès au système de fichiers. Voici un exemple extrait de:

new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
var assembly = Assembly.LoadFile(assemblyPath);
CodeAccessPermission.RevertAssert();

De là, vous pouvez utiliser de l'assemblée et de réflexion pour appeler votre méthode. Notez que cette méthode vous permet de hisser le processus de compilation en dehors de la sandbox domaine d'application, ce qui est un plus à mon avis.

Pour référence, voici mon bac à sable de la classe créée pour faciliter le lancement de script assemblées dans une belle, claire et distincte domaine d'application qui a des autorisations limitées et peuvent être facilement démontée lorsque nécessaire:

class Sandbox : MarshalByRefObject
{
    const string BaseDirectory = "Untrusted";
    const string DomainName = "Sandbox";

    public Sandbox()
    {
    }

    public static Sandbox Create()
    {
        var setup = new AppDomainSetup()
        {
            ApplicationBase = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, BaseDirectory),
            ApplicationName = DomainName,
            DisallowBindingRedirects = true,
            DisallowCodeDownload = true,
            DisallowPublisherPolicy = true
        };

        var permissions = new PermissionSet(PermissionState.None);
        permissions.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess));
        permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

        var domain = AppDomain.CreateDomain(DomainName, null, setup, permissions,
            typeof(Sandbox).Assembly.Evidence.GetHostEvidence<StrongName>());

        return (Sandbox)Activator.CreateInstanceFrom(domain, typeof(Sandbox).Assembly.ManifestModule.FullyQualifiedName, typeof(Sandbox).FullName).Unwrap();
    }

    public string Execute(string assemblyPath, string scriptType, string method, params object[] parameters)
    {
        new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
        var assembly = Assembly.LoadFile(assemblyPath);
        CodeAccessPermission.RevertAssert();

        Type type = assembly.GetType(scriptType);
        if (type == null)
            return null;

        var instance = Activator.CreateInstance(type);
        return string.Format("{0}", type.GetMethod(method).Invoke(instance, parameters));
    }
}

Note rapide: si vous utilisez cette méthode pour la sécurité de l'approvisionnement des éléments de preuve pour le nouveau domaine d'application, vous devez vous connecter à votre assemblée de lui donner un nom fort.

Notez que cela fonctionne bien lorsque vous exécutez dans le processus, mais si vous voulez vraiment un pare-balles environnement de script, vous avez besoin d'aller plus loin et d'isoler le script dans un processus distinct pour s'assurer que les scripts qui ne malveillant (ou tout simplement stupide) des choses comme les débordements de pile, la fourche des bombes, et de mémoire, ne pas faire tomber l'ensemble du processus de demande. Je peux vous donner plus d'informations sur le faire que si vous en avez besoin.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X