Geeks With Blogs
Simon Cooper Peering into the depths of .NET

You write a security-related application that allows addins to be used. These addins (as dlls) can be downloaded from anywhere, and, if allowed to run full-trust, could open a security hole in your application. So you want to restrict what the addin dlls can do, using a sandboxed appdomain, as explained in my previous posts.

But there needs to be an interaction between the code running in the sandbox and the code that created the sandbox, so the sandboxed code can control or react to things that happen in the controlling application. Sandboxed code needs to be able to call code outside the sandbox.

Now, there are various methods of allowing cross-appdomain calls, the two main ones being .NET Remoting with MarshalByRefObject, and WCF named pipes. I'm not going to cover the details of setting up such mechanisms here, or which you should choose for your specific situation; there are plenty of blogs and tutorials covering such issues elsewhere. What I'm going to concentrate on here is the more general problem of running fully-trusted code within a sandbox, which is required in most methods of app-domain communication and control.

Defining assemblies as fully-trusted

In my last post, I mentioned that when you create a sandboxed appdomain, you can pass in a list of assembly strongnames that run as full-trust within the appdomain:

// get the Assembly object for the assembly
Assembly assemblyWithApi = ...    

// get the StrongName from the assembly's collection of evidence
StrongName apiStrongName = assemblyWithApi.Evidence.GetHostEvidence<StrongName>();

// create the sandbox
AppDomain sandbox = AppDomain.CreateDomain(
    "Sandbox", null, appDomainSetup, restrictedPerms, apiStrongName);

Any assembly that is loaded into the sandbox with a strong name the same as one in the list of full-trust strong names is unconditionally given full-trust permissions within the sandbox, irregardless of permissions and sandbox setup. This is very powerful! You should only use this for assemblies that you trust as much as the code creating the sandbox.

So now you have a class that you want the sandboxed code to call:

// within assemblyWithApi
public class MyApi
    public static void MethodToDoThings() { ... }

// within the sandboxed dll
public class UntrustedSandboxedClass
    public void DodgyMethod()

However, if you try to do this, you get quite an ugly exception:

MethodAccessException: Attempt by security transparent method 'UntrustedSandboxedClass.DodgyMethod()' to access security critical method 'MyApi.MethodToDoThings()' failed.
Security transparency, which I covered in my first post in the series, has entered the picture. Partially-trusted code runs at the Transparent security level, fully-trusted code runs at the Critical security level, and Transparent code cannot under any circumstances call Critical code.

Security transparency and AllowPartiallyTrustedCallersAttribute

So the solution is easy, right? Make MethodToDoThings SafeCritical, then the transparent code running in the sandbox can call the api:

public static void MethodToDoThings() { ... }

However, this doesn't solve the problem. When you try again, exactly the same exception is thrown; MethodToDoThings is still running as Critical code. What's going on?

By default, a fully-trusted assembly always runs Critical code, irregardless of any security attributes on its types and methods. This is because it may not have been designed in a secure way when called from transparent code - as we'll see in the next post, it is easy to open a security hole despite all the security protections .NET 4 offers. When exposing an assembly to be called from partially-trusted code, the entire assembly needs a security audit to decide what should be transparent, safe critical, or critical, and close any potential security holes.

This is where AllowPartiallyTrustedCallersAttribute (APTCA) comes in. Without this attribute, fully-trusted assemblies run Critical code, and partially-trusted assemblies run Transparent code. When this attribute is applied to an assembly, it confirms that the assembly has had a full security audit, and it is safe to be called from untrusted code. All code in that assembly runs as Transparent, but SecurityCriticalAttribute and SecuritySafeCriticalAttribute can be applied to individual types and methods to make those run at the Critical or SafeCritical levels, with all the restrictions that entails.

So, to allow the sandboxed assembly to call the full-trust API assembly, simply add APCTA to the API assembly:

[assembly: AllowPartiallyTrustedCallers]

and everything works as you expect. The sandboxed dll can call your API dll, and from there communicate with the rest of the application.


That's the basics of running a full-trust assembly in a sandboxed appdomain, and allowing a sandboxed assembly to access it. The key is AllowPartiallyTrustedCallersAttribute, which is what lets partially-trusted code call a fully-trusted assembly. However, an assembly with APTCA applied to it means that you have run a full security audit of every type and member in the assembly. If you don't, then you could inadvertently open a security hole. I'll be looking at ways this can happen in my next post.

Posted on Thursday, May 16, 2013 5:52 PM | Back to top

Comments on this post: .NET Security Part 3

# re: .NET Security Part 3
Requesting Gravatar...

This is a great series. Will you be discussing how MEF and MAF intersect with sandboxing for plugins? Assuming that they do, I haven't taken a serious look at either technology but they seem to be focused on the same general problem set.

Thanks for a very informative set of posts. I look forward to the rest of the series.

Left by Terence Craig on May 18, 2013 2:14 AM

# re: .NET Security Part 3
Requesting Gravatar...
Thank you for this series, I might have a good use for this kind of sandboxing soon. However, I have a question about this part of the article:

"But there needs to be an interaction between the code running in the sandbox and the code that created the sandbox, so the sandboxed code can control or react to things that happen in the controlling application."

Is this actually necessary? I imagine it would be easier to just run your entire application in the sandboxed appdomain, give full trust to your own assemblies and create one (or more) plugin API assemblies with AllowPartiallyTrustedCallers attribute and SecuritySafeCritical methods.

Is that a viable solution too? If so, what are the downsides? Looking forward to your answer.
Left by Medo on May 21, 2013 9:55 AM

# re: .NET Security Part 3
Requesting Gravatar...
You need to have something creating the sandboxed appdomain in the first place. Of course, you could have the majority of your application running fully-trusted in the sandboxed appdomain, but there's some restrictions on what even fully-trusted code can do in a non-homogenous (sandboxed) appdomain, for example, you can't use the DLR.

There's also some benefits in putting an addin in its own sandbox - you can unload it at any time, and you can put each addin in a separate sandbox so they can't interfere with each other.

At the end of the day, it depends on your specific application needs and requirements. I've only covered the basics here, you'll need to do your own research into the pros and cons of each option. Sandboxed appdomains are just a tool, you choose how to use them.
Left by Simon Cooper on May 21, 2013 10:12 AM

# re: .NET Security Part 3
Requesting Gravatar...
Ok, thanks for clarifying.
Left by Medo on May 21, 2013 11:12 AM

Your comment:
 (will show your gravatar)

Copyright © simonc | Powered by: