Sandboxing Gotcha in a Partially Trusted App
By Don Kiely
There are three basic ways to build an ASP.NET Web site with partial trust, where the app doesn t automatically get the full set of permissions available in the .NET Framework:
- use a standard trust level, such as the Medium trust level;
- create a custom trust level; and
- sandbox dangerous code in a separate assembly that is heavily protected.
The sandboxing method is commonly used with a custom trust level, giving you enormous flexibility about how you protect your application.
I recently had occasion to use this combination technique, using a custom trust level along with sandboxing to build a Web site. It was a more painful experience than it should have been, even given that security is just plain too hard. I d like to share my pain and my key mistake with you in the hopes that you avoid this particular bit of pain. I admitted my sins in a conference session at DevTeach in Montreal last month; I put together the code associated with this article for that conference session (see end of article for download details).
Here s the scenario. The Web app didn t do anything extraordinary in its environment. It hit a database for some important data, but otherwise just got user input and did various things with that input. The interesting thing, though, is that the database it had to use was a Microsoft Access database. Access isn t typically a good use for a Web app, but in this case it made sense in the overall scheme of enterprise operations to put some of the read-only data there.
What s the big deal of using an Access database in terms of security? The problem is that it requires using the OleDb data provider in .NET, which uses COM which means that the Web app has to call out to unmanaged code. Uh oh. That s both a security and performance problem. I certainly didn t want to give the entire app permission to call unmanaged code this is a huge security vulnerability, particularly for a Web app. And only a small section of the app had to get at that Access data.
This meant that the app was a perfect candidate for a custom trust level, with the dangerous data access code sandboxed in its own assembly. The standard Medium trust level would be a good starting point, as it usually is for custom trust levels.
I don t have room here for the full treatment of the solution, but you can download the code to see how to put together the solution. Here are the high points in developing a custom trust level with sandboxed code.
The starting point is a custom trust level config file. I named mine web_customtrust.config, copying the web_mediumtrust.config file from C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG (your exact path may vary a bit). For my app, I added a couple of permissions, such as to read a file from a non-standard location. But the important change will come later, when I grant the unmanaged code and OleDbPermission permissions to the sandbox assembly.
Then I created an assembly to hold the sandbox code. In this case, the assembly consists of a single class with a single GetSecret method to read data from the Access database. This is boring ADO.NET net code, nothing interesting other than it being in a separate assembly.
The assembly must have elevated permissions in the Web application. You can put it in the Global Assembly Cache (GAC), but because this assembly is going to be used by only a single application, this isn t a good idea. The other alternative is to put the assembly in the Web app s bin directory and grant it the permissions it needs. That s the option I used.
Because the assembly will be called by partially trusted applications, it has to be decorated with the AllowPartiallyTrustedCallers attribute, like this:
The normal stack walk that code access security uses to make sure all callers in the call stack have required permissions won t work. The caller (the Web app) isn t going to have permission to call unmanaged code. So the sandbox assembly must make an Assert to stop the stack walk for the required permission. This is the code in the GetSecret method:
OleDbPermission oledbPerm = new OleDbPermission(PermissionState.Unrestricted);
Next, I had to specify the permission set that the sandbox assembly would get. This requires a couple of changes to the custom trust config file. The first defines a permission set for the assembly, allowing calls to unmanaged code and permission to use OLE DB:
Flags="UnmanagedCode, Assertion, Execution, ControlThread, ControlPrincipal"/>
Next, I had to define a code group that matches the assembly and associates the AccessDBSet permission set with it. In this case, I used a strong name on the assembly to identify it:
There are a few other niceties to flesh out the solution, but these are the important points. So let s run this thing and then go get a beer and pizza to celebrate yet another finished app!
But...aargh! Running the app and the part where the secret is read from the Access database throws a security exception. Specifically, Request for permission of type ... OleDbPermission ... failed. Huh? But the custom trust level is configured correctly, the sandboxed code is written right, everything is perfect.
So I decided to crack open the gasp! documentation. Hidden in far too small type is this crucial piece of information in the documentation for the OleDbPermission class: The .NET Framework Data Provider for OLE DB currently requires FullTrust permission. Currently, using the OleDbPermission class has no effect. Aargh! So, basically, Microsoft punted on this permission and requires assemblies that access Access databases have full trust, a huge security vulnerability. Even code that can call unmanaged code can t use OleDbPermission unless it is running under full trust. Which means that OleDbPermission is useless. Darn you, Microsoft!
So the sandbox assembly must run under full trust, which requires a change to the custom config file, the CodeGroup element:
But all is not lost. At least the now horribly dangerous code is still sandboxed in its own assembly, and we can control access to that code. So it is still worthwhile to use a custom trust level with sandboxed code. Microsoft, though, makes us write code that is more vulnerable to attack than it should be.
The moral of the story is to thoroughly check out the permissions used by your partially trusted application Microsoft may force you to do bad things.
Sample code accompanying this article is available for download.
Don Kiely, MVP, MCSD, is a senior technology consultant, building custom applications as well as providing business and technology consulting services. His development work involves tools such as SQL Server, Visual Basic, C#, ASP.NET, and Microsoft Office. He writes regularly for several trade journals, and trains developers in database and .NET technologies. You can reach Don at mailto:[email protected] and read his blog at http://www.sqljunkies.com/weblog/donkiely/.