Подтвердить что ты не робот

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

Вот почему задавался этот вопрос: www.devplusplus.com/Tests/CSharp/Hello_World.

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

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

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

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

Пока я не могу найти способ создания AppDomain и загружать сборку , которая НЕ НА СИСТЕМУ ФАЙЛОВ, а скорее в ОЗУ.

Опять же, причины, по которым другие решения не работали, указаны выше: 1. Многие из них были для pre-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);

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

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

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

Ответ 1

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

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

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

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

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

Для справки, вот мой класс Sandbox, созданный для облегчения запуска сборок script в чистом отдельном 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));
    }
}

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

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