c#-在.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);

到目前为止,我还没有找到一种创建AppDomain并加载程序集的方法,该方法不在文件系统上,而是在RAM中。

再次指出,其他解决方案不起作用的原因已在上面指出:1.许多解决方案适用于4.0之前的版本;以及2.许多依靠指向文件系统的“ .Load”方法。

答案2:我有一个程序集引用,因为它是由2936965929412068368352类生成的,因此,如果您知道将其转换为字节数组的方法,那就太完美了!

显示安全缺陷的示例代码

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. 此代码位于对文件系统本身具有有限权限的共享Web服务器上。
  2. 这段代码可能需要运行数千次,而且我暂时不需要1,000个dll。
Timothy Khouri asked 2020-06-24T10:29:34Z
1个解决方案
42 votes

好的,首先要做的是:没有实际方法可以使用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();

从那里,您可以使用汇编和反射来调用您的方法。 请注意,此方法使您可以在沙盒化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));
    }
}

快速说明:如果使用此方法为新的AppDomain提供安全性证据,则需要在程序集上签名以使用强名称。

请注意,这在进程中运行时可以很好地工作,但是如果您确实想要防弹脚本环境,则需要进一步走一步,并在一个单独的进程中隔离该脚本,以确保执行恶意(或愚蠢)操作的脚本 像堆栈溢出,分支炸弹和内存不足的情况都不会降低整个应用程序过程。 如果需要,我可以为您提供更多信息。

MikeP answered 2020-06-24T10:30:17Z
translate from https://stackoverflow.com:/questions/5997995/in-net-4-0-how-do-i-sandbox-an-in-memory-assembly-and-execute-a-method