c# - 在 .NET 4.0 中,如何 'sandbox' 一个内存程序集并执行一个方法?

标签 c# security .net-4.0 appdomain

以下是提出此问题的原因: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:我有一个程序集引用,因为它是由 CSharpCodeProvider 类生成的,所以如果您知道将 that 转换为字节数组的方法,那么将是完美的!

显示安全漏洞的示例代码

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. 此代码可能需要运行数千次,我不想要 1,000 个 dll,即使是暂时的。

最佳答案

好的,首先要做的是:没有实际的方法可以使用 CSharpCodeProvider 完全在内存中动态编译 C# 源代码。有些方法似乎支持该功能,但由于 C# 编译器是无法在进程内运行的 native 可执行文件,源字符串被保存到临时文件,编译器在该文件上调用,然后生成的程序集是保存到磁盘,然后使用 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 提供安全证据,则需要对您的程序集进行签名以赋予它强名称。

请注意,这在进程中运行时效果很好,但如果您真的想要一个防弹脚本环境,您需要更进一步,将脚本隔离在一个单独的进程中,以确保恶意脚本(或只是愚蠢的)诸如堆栈溢出、fork 炸弹和内存不足的情况不会导致整个应用程序进程崩溃。如果您需要,我可以为您提供更多相关信息。

关于c# - 在 .NET 4.0 中,如何 'sandbox' 一个内存程序集并执行一个方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5997995/

相关文章:

c# - 这段 RxUI 代码有什么问题?

c# - 系统.Delegate System.Reflection.MethodInfo.CreateDelegate(System.Type)

javascript - 限制正则​​表达式中的字符长度

PHP:检查 $_SERVER ['HTTP_REFERER' ] 相等性的最佳方法是什么?

javascript - 将 ID 传递给 View ,如何避免可能的操作?

.net - 在 VB.NET 或 C# 中编写 VBA Excel 宏,首先进行早期绑定(bind),然后进行后期绑定(bind)

dynamic - .NET 4.0 - "dynamic"关键字

c# - "ÅÄÖ"个字母的 QueryString 问题

php - MD5 + Salt 给我一些奇怪的字符

c# - 静态元素的执行顺序初始化