In .NET 4.0, how do I "isolate" an assembly in memory and execute a method?

This is why this question was asked: www.devplusplus.com/Tests/CSharp/Hello_World .

While similar questions were asked earlier, there are several problems in the numerous online answers:

  • This should be done with ".Net 4.0" and not obsolete.
  • The assembly is in memory and will only be in memory; it cannot be written to the file system.
  • I would like to restrict access to the file system, network, etc.

Something like that:

var evidence = new Evidence(); evidence.AddHostEvidence(new Zone(SecurityZone.Internet)); var permissionSet = SecurityManager.GetStandardSandbox(evidence); 

So far, I can’t find a way to create AppDomain and load the assembly , which is NOT ON the FILE SYSTEM , but rather on RAM.

Again, the reasons why other solutions did not work are indicated above: 1. Many of them were for pre-4.0 and 2. Many relied on the ".Load" method, pointing to the file system.

Answer 2: I have a reference to the assembly, because it is generated by the CSharpCodeProvider class, so if you know a way to turn this into an array of bytes, this will be perfect!

Sample Security Identification Code

 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 save the assembly to a file first?

For two reasons:

  • This code is located on a shared web server with limited file system rights.
  • This code can run potentially thousands of times, and I don't want 1000 DLLs, even temporarily.
+45
security c # appdomain
May 13 '11 at 21:31
source share
1 answer

OK, first first: there is no real way to use CSharpCodeProvider to dynamically compile a C # source completely in memory. There are methods that seem to support this functionality, but since the C # compiler is a natural executable file that cannot be started in the process, the original string is stored in a temporary file, the compiler is called in this file, and then the resulting assembly is saved to disk and then loaded for you using Assembly.Load.

Secondly, as you have discovered, you should be able to use the Compile method from AppDomain to load the assembly and grant it the necessary permissions. I came across the same unusual behavior, and after a lot of digging I discovered that it was a mistake in the framework. I sent a problem report to MS Connect .

Since the structure is already written to the file system anyway, the workaround is for the assembly to be written to a temporary file and then loaded as needed. However, at boot you need to temporarily approve permissions in AppDomain, since you have denied access to the file system. Here is an example of this snippet:

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

From there, you can use assembly and reflection to call your method. Please note that this method allows you to raise the compilation process outside the isolated AppDomain, which is a plus in my opinion.

For reference, here is my Sandbox class, designed to make it easy to run script builds in a clean, separate AppDomain that has limited permissions and can be easily unloaded if necessary:

 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)); } } 

Quick note: if you use this method to provide security evidence for the new AppDomain, you need to sign your assembly to give it a strong name.

Note that this works great at startup, but if you really need a bulletproof script environment, you need to take one more step and isolate the script in a separate process to make sure that scripts that do malicious (or just plain dumb) things like stack overflows, fork bombs and memory situations do not ruin the entire application process. I can give you more information about this if you need it.

+42
May 13 '11 at 23:55
source share



All Articles