3

This is an update to THIS question as V3 is now available since some time:

I am also thinking about using RazorEngine for mail-templates (and possibly even more).
Is there now a "sufficient secure mode" available to let users define templates?

With the IsolatedTemplateService I can avoid users beeing able to access my domain objects, but what about the other stuff also discussed in the old question like editing/deleting files?

Also whats about accessing the DB "manually" (by reading the connection-string from config file if file access is possible or in the worst case simple bruteforce) and e.g. add a user as an admin?

Is there a way to "disable" all this stuff for the custom AppDomain created by/for the IsolatedTemplateService?

Community
  • 1
  • 1
Christoph Fink
  • 22,727
  • 9
  • 68
  • 113
  • As the accepted answer to that question says: "A cshtml Razor file is able to execute any. NET code in the context of the site so yes, it is a security risk to permit them to be supplied by users." A code injection attack on your server waiting to happen. – Kris Vandermotten May 28 '14 at 13:12
  • @KrisVandermotten: Yes, but with a custom `AppDomain` you should be able to restrict the access to only "execute code" and no "file system access" or "network access". I am trying different stuff at the moment, but `RazorEngine` seems to not run in a not `PermissionState.Unrestricted` `AppDomain`... – Christoph Fink May 28 '14 at 13:23
  • In the old days, you could create an AppDomain with very restricted Code Access Security permissions. A lot of that has been simplified, and is less powerful now. But even then, what if someone wrote `while (true) ;` ? I call that a denial of service attack. I guess it really boils down to: who are these 'users' and do you trust them? – Kris Vandermotten May 28 '14 at 13:35

1 Answers1

4

I was able to solve this by NOT using RazorEngine, but make my own implementation (based on some code frome there).

The main problem with RazorEngine is the following:
For the CodeDomProvider needed to compile the razor template you need a "Full Trust" AppDomain, but the IsolatedTemplateService does execute "everything" in the new AppDomain so it fails compiling the template.

I solved this by splitting this up into two parts:

  1. Compiling the template
  2. Rendering the template

Step one I do in the "main AppDomain" and only step 2 in the "restricted AppDomain".

There was then one additional problem:
If you compile the template "in memory" (=> without generating a *.dll file), which is done by RazorEngine, it is directly loaded in the current AppDomain which I did not want as it "inherits" that domains permissions then.
Because of this I disabled this and generate a *.dll in a "common templates folder".
So after the compilation I have a *.dll file containing the template which is not loaded anywhere yet.

The second AppDomain is created with the following permission set:

PermissionSet permSet = new PermissionSet(PermissionState.None);
permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
permSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, templatePath));
permSet.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess));

The first permission is needed to execute any code, the second is to allow the AppDomain to load the generated assembly from my common templates folder and the third is for limited support for reflection (only in the loaded assembly, but not i.e. in System.*) to support dynamic so I can use a ViewBag.
Every thing else is prohibited:

  • no file access outside templatePath
  • no network access
  • no SQL Server access

I then call a Render method in this restricted domain which loads the appropriate assembly and renders the template. This method also supports a timeout, after which the rendering is killed, to avoid while (true) ; DOS attacks like suggested by Kris.

This seems to work fine so far. I did now release this as IsolatedRazor including a NuGet package. May RazorEngine can incorperate this as e.g. RestrictedIsolatedTemplateService or so...

Kris Vandermotten
  • 10,111
  • 38
  • 49
Christoph Fink
  • 22,727
  • 9
  • 68
  • 113
  • Very nice, but you are still executing code on your server from an untrusted source, so remain careful. I haven't tried, but what about `Task.Run(() => while (true) ;);`? The thing is, you open a giant gate and then try to close it as much as possible. What about opening just the small window you need? Admittedly, that a lot more work, since you need to parse the inputs and validate them against an allowed grammar. But it would be inherently safer. – Kris Vandermotten Oct 19 '14 at 18:37
  • `Task.Run(() => while (true) ;);` - did not think of that - need to check, thanks. But in general, yes you are right, but this is not intended for "everyone" - the templates I use it for are only defined in my pages backend where you "normaly" do not have users trying to bring down the server... – Christoph Fink Oct 19 '14 at 18:43
  • That's why I said a long time ago: "I guess it really boils down to: who are these 'users' and do you trust them?". If they don't have malicious intent, then something that protects your server from accidental mistakes may be good enough. This is probably more than good enough. On the other hand, if you do validate the input, you may be able to provide better error messages when they make a mistake, and you could avoid the overhead of your current solution. – Kris Vandermotten Oct 19 '14 at 18:48
  • 1
    @KrisVandermotten: Newest version now also protects against the usage of `Task` and `Thread` - I am sure still not "100%", but way secure enought for me for now... – Christoph Fink Oct 27 '14 at 17:18