在 .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 并加载不在文件系统上的程序集的方法,而是在 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);
那么为什么我不能先将程序集保存到文件中呢?
有两个原因:
- 该代码位于共享 Web 服务器上,对文件系统本身的权限有限。
- 这段代码可能需要运行数千次,而且我不想要 1,000 个 dll,即使是暂时的。
Here is the reason why this question was being asked: www.devplusplus.com/Tests/CSharp/Hello_World.
While similar questions were asked before, the many answers online have several issues:
- This must be done ".Net 4.0" style, not legacy mode.
- The assembly is in-memory and will only be in memory, it cannot be written to the file system.
- I would like to limit all access to the file-system, network, etc.
Something like this:
var evidence = new Evidence();
evidence.AddHostEvidence(new Zone(SecurityZone.Internet));
var permissionSet = SecurityManager.GetStandardSandbox(evidence);
So far, I cannot find a way to create an AppDomain and load an assembly THAT IS NOT ON THE FILE SYSTEM, but rather in RAM.
Again, the reasons why the other solutions didn't work are identified above: 1. Many were for pre-4.0, and 2. Many relied on the ".Load" method pointing to the file system.
Answer 2: I have an assembly reference due to it being generated by the CSharpCodeProvider
class, so if you know a way to turn that into a byte array, that would be perfect!
Sample Code to Show The Security Flaw
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);
So why can't I just save the assembly to a file first?
For two reasons:
- This code is on a shared web server with limited permissions to the file-system itself.
- This code may need to be run potentially thousands of times, and I don't want 1,000 dlls, even temporarily.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好的,首先要做的事情是:没有实际的方法可以使用 CSharpCodeProvider 完全在内存中动态编译 C# 源代码。有些方法似乎支持该功能,但由于 C# 编译器是无法在进程中运行的本机可执行文件,因此源字符串将保存到临时文件中,在该文件上调用编译器,然后生成的程序集是保存到磁盘,然后使用 Assembly.Load 为您加载。
其次,正如您所发现的,您应该能够使用 AppDomain 中的 Compile 方法来加载程序集并为其提供所需的权限。我遇到了同样的异常行为,经过大量挖掘后发现这是框架中的一个错误。我在 MS Connect。
由于框架已经在写入文件系统,因此解决方法是将程序集写入临时文件,然后根据需要加载。但是,当您加载它时,您需要临时断言 AppDomain 中的权限,因为您已禁止访问文件系统。下面是一个示例片段:
从那里您可以使用程序集和反射来调用您的方法。请注意,此方法允许您将编译过程提升到沙盒 AppDomain 之外,我认为这是一个优点。
作为参考,这里是我创建的 Sandbox 类,以便于在一个干净的独立 AppDomain 中启动脚本程序集,该 AppDomain 具有有限的权限,并且可以在必要时轻松卸载:
快速说明:如果您使用此方法为新 AppDomain 提供安全证据,您需要签署您的程序集以为其指定一个强名称。
请注意,这在进程中运行时效果很好,但如果您确实想要一个防弹脚本环境,则需要更进一步,将脚本隔离在单独的进程中,以确保脚本执行恶意(或只是愚蠢)的操作像堆栈溢出、fork 炸弹和内存不足等情况不会导致整个应用程序进程崩溃。如果您需要的话,我可以为您提供更多有关这样做的信息。
OK, first things first: there's no actual way to use the CSharpCodeProvider to do dynamic compilation of C# source entirely in memory. There are methods that seem to support that functionality, but since the C# compiler is a native executable that cannot run in-process, the source string is saved to a temporary file, the compiler is invoked on that file, and then the resulting assembly is saved to disk and then loaded for you using Assembly.Load.
Secondly, as you've discovered, you should be able to use the Compile method from within the AppDomain to load the assembly and give it the desired permissions. I ran into this same unusual behavior, and after a lot of digging found that it was a bug in the framework. I filed an issue report for it on MS Connect.
Since the framework is already writing to the filesystem anyway, the workaround is to have the assembly written to a temporary file and then loaded as needed. When you load it however, you'll need to temporarily assert permissions in the AppDomain, since you've disallowed access to the file system. Here's an example snippet of that:
From there you can use the assembly and reflection to invoke your method. Note that this method lets you hoist the compilation process outside of the sandboxed AppDomain, which is a plus in my opinion.
For reference, here is my Sandbox class created to facilitate the launching of script assemblies in a nice clean separate AppDomain that has limited permissions and can be easily unloaded when necessary:
Quick note: if you use this method to supply security evidence for the new AppDomain, you need to sign your assembly to give it a strong name.
Note that this works fine when run in process, but if you really want a bullet-proof script environment, you need to go one step further and isolate the script in a separate process to ensure that scripts that do malicious (or just stupid) things like stack overflows, fork bombs, and out of memory situations don't bring down the whole application process. I can give you more information on doing that if you need it.