gpt4 book ai didi

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

转载 作者:IT王子 更新时间:2023-10-29 03:52:00 29 4
gpt4 key购买 nike

以下是提出此问题的原因: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/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com