В.NET 4.0, как я могу "песочницей" сборки в памяти и выполнить метод?

Вот причина, по которой этот вопрос задавался: http://www.devplusplus.com/Tests/CSharp/Hello_World.

Хотя подобные вопросы задавались ранее, многие ответы в Интернете имеют несколько проблем:

  1. Это должно быть сделано в стиле.Net 4.0, а не в устаревшем режиме.
  2. Сборка находится в памяти и будет только в памяти, она не может быть записана в файловую систему.
  3. Я хотел бы ограничить весь доступ к файловой системе, сети и т. Д.

Что-то вроде этого:

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

Пока что я не могу найти способ создать домен приложения и загрузить сборку, которая находится не в файловой системе, а в оперативной памяти.

Опять же, причины, по которым другие решения не работали, указаны выше: 1. Многие были до версии 4.0, и 2. Многие полагались на метод.Load, указывающий на файловую систему.

Ответ 2: у меня есть ссылка на сборку, потому что она генерируется CSharpCodeProvider класс, так что если вы знаете способ превратить это в байтовый массив, это было бы идеально!

Пример кода для демонстрации недостатка безопасности

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

Так почему я не могу сначала сохранить сборку в файл?

По двум причинам:

  1. Этот код находится на общем веб-сервере с ограниченными разрешениями на саму файловую систему.
  2. Этот код, возможно, придется запускать потенциально тысячи раз, и я не хочу 1000 dll, даже временно.

1 ответ

Решение

Хорошо, обо всем по порядку: нет никакого реального способа использовать CSharpCodeProvider для динамической компиляции исходного кода C# полностью в памяти. Существуют методы, которые поддерживают эту функциональность, но поскольку компилятор C# является собственным исполняемым файлом, который не может быть запущен внутри процесса, исходная строка сохраняется во временном файле, компилятор вызывается для этого файла, а затем результирующая сборка сохраняется на диск и затем загружается для вас с помощью Assembly.Load.

Во-вторых, как вы обнаружили, вы должны иметь возможность использовать метод Compile из AppDomain, чтобы загрузить сборку и предоставить ей необходимые разрешения. Я столкнулся с тем же самым необычным поведением, и после многих копаний обнаружил, что это ошибка в фреймворке. Я подал отчет о проблеме для MS Connect.

Так как фреймворк уже записывает в файловую систему, обходной путь - записать сборку во временный файл и затем загрузить при необходимости. Однако, когда вы загрузите его, вам нужно будет временно утвердить разрешения в AppDomain, поскольку вы запретили доступ к файловой системе. Вот пример фрагмента этого:

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

Оттуда вы можете использовать сборку и отражение для вызова вашего метода. Обратите внимание, что этот метод позволяет поднять процесс компиляции за пределы изолированного AppDomain, что, на мой взгляд, является плюсом.

Для справки, вот мой класс Sandbox, созданный для облегчения запуска сборок скриптов в хорошем чистом отдельном AppDomain, который имеет ограниченные разрешения и может быть легко выгружен при необходимости:

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

Краткое примечание: если вы используете этот метод для предоставления доказательств безопасности для нового домена приложений, вам необходимо подписать сборку, чтобы дать ей строгое имя.

Обратите внимание, что это работает нормально при запуске в процессе, но если вы действительно хотите использовать среду сценариев с пуленепробиваемой защитой, вам нужно сделать еще один шаг и изолировать сценарий в отдельном процессе, чтобы обеспечить выполнение сценариев, которые совершают вредоносные (или просто глупые) действия. как переполнение стека, разветвление бомб и ситуации с нехваткой памяти, не разрушают весь процесс приложения. Я могу дать вам больше информации, если вам это нужно.

Другие вопросы по тегам