Sandboxing code dynamically
I have been getting questions from people about how to make a piece of
managed code execute in a security restricted environment, or how to
"sandbox" it. Some people are trying to use stack walk modifiers (such as
Deny()) to do it, which does not work for this purpose. Other people are
trying to modify the machine security policy (which when done correctly
works) or put an application in question on a network share and run it from
there (which also works and gives the application LocalIntranet permissions,
if your policy is default). There is however a way to sandbox a piece of
code dynamically, by creating an appdomain, setting its security policy and
loading an assembly there.
I have written the following code to demonstrate this technique to you (also check out my question at the end). // // This program illustrates how you can execute a managed application // in a security sandbox without modifying the machine security policy. // You can save the source to a .cs file and compile it “csc.exe sandbox.cs” //
using System; using System.Security; using System.Security.Policy; using System.Security.Permissions;
class Sandbox { static void Main (string[] args) { // check the arguments if (args.Length < 1) { Console.WriteLine("Usage: Sandbox <assembly>"); return; }
// get the assembly file name string fileName = args[0];
// Create an appdomain where our assembly is going to be sandboxed. // AppDomains can have their own security policy that allows to // further restrict assemblies' permissions. AppDomain ad = AppDomain.CreateDomain("MySandboxAppdomain");
// Now we need to set the appdomain policy, // and to do that we will need to create a Policy Level. // A Policy Level is a tree-like structure that has Code Groups as its nodes. // Each code group consists of a Membership Condition (something that // defines if an assembly in question belongs to the code group) and // a Permission Set that is granted to the assembly if it does. PolicyLevel domainPolicy = PolicyLevel.CreateAppDomainLevel();
// Let's create a code group that gives Internet permission set // to all code.
// First, let's create a membership condition that accepts all code. AllMembershipCondition allCodeMC = new AllMembershipCondition(); // If you were to build a more complex policy (giving different permissions // to different assemblies) you could use other membership conditions, // such as ZoneMembershipCondition, StrongNameMembershipCondition, etc.
// Now let's create a policy statement that represents Internet permissions. // Here we just grab named permission set called "Internet" from the default policy, // but you could also create your own permission set with whatever permissions // you want in there. PermissionSet internetPermissionSet = domainPolicy.GetNamedPermissionSet("Internet"); PolicyStatement internetPolicyStatement = new PolicyStatement(internetPermissionSet);
// We are ready to create a code group that maps all code to Internet permissions CodeGroup allCodeInternetCG = new UnionCodeGroup(allCodeMC, internetPolicyStatement); // We have used a UnionCodeGroup here. It does not make much difference for // a simple policy like ours here, but if you were to set up a more complex one // you would probably add some child code groups and then the type of the parent // code group would matter. UnionCodeGroup unions all permissions granted by its // child code groups (as opposed to FirstMatchCodeGroup that only takes one child // code group into effect).
// Once we have the CodeGroup set up we can add it to our Policy Level. domainPolicy.RootCodeGroup = allCodeInternetCG; // If our root code group had any children the whole tree would be added // to the appdomain security policy now. // Imagine you wanted to modify our policy so that your strongname signed // assemblies would get FullTrust and all other assemblies would get Internet // permissions. Do accomplish that you would create a new UnionCodeGroup, // whose membership condition would be a StrongNameMembershipCondition // specifying your public key, and its permission set would be a "FullTrust" // or just a "new PermissionSet(PermissionState.Unrestricted)". // Then you would add that code group as a child to our allCodeInternetCG by // calling its AddChild method. Whenever you then loaded a correct strong // name signed assembly into your appdomain it would get Internet from the // root code group and FullTrust from the child code group, and the effective // permissions would be a union of the two, which is FullTrust.
// and our final policy related step is setting the AppDomain policy ad.SetAppDomainPolicy(domainPolicy);
// now whatever code we load into the AppDomain is going to get Internet permissions string[] newArgs = new string[args.Length-1]; Array.Copy(args, 1, newArgs, 0, args.Length-1); ad.ExecuteAssembly(fileName, null, newArgs); // We have called ExecuteAssembly to run an application in a sandbox. // If you are dealing with a library where you want to call some methods // you would use ad.Load() method to load the assembly and then use // reflection to instantiate assembly's classes and invoke methods. } }
Ok, now my question for you: if you compile this into sandbox.exe and run "sandbox.exe sandbox.exe" you will get a security exception. Why does this happen? Whoever gives a clear and comprehensive explanation first will earn a honourable mention!
Sandbox, security
level software, obfuscators
at our software archive (c) Ivan Medvedev 2003
|