在.NET 4.0中,如何"沙箱"内存中的程序集并执行方法?

Tim*_*uri 47 c# security appdomain .net-4.0

以下是问题的原因: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);
Run Code Online (Sandbox Code Playgroud)

到目前为止,我找不到创建AppDomain并加载程序集的方法,该程序集不在文件系统中,而是在RAM中.

同样,上面列出了其他解决方案不起作用的原因: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);
Run Code Online (Sandbox Code Playgroud)

那么为什么我不能先将程序集保存到文件中呢?

有两个原因:

  1. 此代码位于共享Web服务器上,对文件系统本身具有有限的权限.
  2. 这段代码可能需要运行数千次,我不想要1000个dll,甚至是暂时的.

Mik*_*keP 42

好的,首先要做的事情是:没有实际的方法可以使用CSharpCodeProvider完全在内存中动态编译C#源代码.有些方法似乎支持该功能,但由于C#编译器是无法在进程中运行的本机可执行文件,因此源字符串将保存到临时文件中,在该文件上调用编译器,然后生成的程序集为保存到磁盘,然后使用Assembly.Load为您加载.

其次,正如您所发现的,您应该能够在AppDomain中使用Compile方法来加载程序集并为其提供所需的权限.我遇到了同样不寻常的行为,经过大量挖掘后发现它是框架中的一个错误.我在MS Connect上提交了一份问题报告.

由于框架已经写入文件系统,因此解决方法是将程序集写入临时文件,然后根据需要加载.但是,当您加载它时,您需要在AppDomain中临时断言权限,因为您不允许访问文件系统.这是一个示例片段:

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

从那里,您可以使用程序集和反射来调用您的方法.请注意,此方法允许您在沙盒AppDomain之外提升编译过程,这在我看来是一个加号.

作为参考,这里是我的Sandbox类,它是为了便于在一个干净的单独AppDomain中启动脚本程序集,该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));
    }
}
Run Code Online (Sandbox Code Playgroud)

快速注意:如果您使用此方法为新AppDomain提供安全证据,则需要对程序集进行签名以赋予其强名称.

请注意,这在运行过程中工作正常,但是如果您真的想要一个防弹脚本环境,则需要更进一步,在单独的进程中隔离脚本,以确保执行恶意(或只是愚蠢)事情的脚本像堆栈溢出,fork炸弹和内存不足情况不会导致整个应用程序进程失效.如果您需要,我可以为您提供更多相关信息.