Antaris / RazorEngine

Open source templating engine based on Microsoft's Razor parsing engine
http://antaris.github.io/RazorEngine
Other
2.14k stars 577 forks source link

Any working example of IsolatedTemplateService with a sandbox domain? #144

Closed imran1231 closed 9 years ago

imran1231 commented 10 years ago

I have tried a huge number of combination sandbox domain with IsolatedTemplateService but nothing works? Only works if I set

var permissionSet = new PermissionSet(PermissionState.Unrestricted);

Any working example/sample will be appreciated? I am using RazorEngine 3.2.0.0

spetryjohnson commented 10 years ago

I tried a sandboxed appdomain and had problems as well. Was not able to resolve them, eventually gave up.

I wish you the best of luck, but I don't have high hopes for you :)

chrfin commented 9 years ago

As I was not able to do this with RazorEngine I created my own version: https://isolatedrazor.codeplex.com/ (IsolatedRazor via NuGet)

It is heavily based on RazorEngine and if Matthew is interessted I am happy to help integrate this as an alternative template service into RazorEngine.

matthid commented 9 years ago

@chrfin please feel free to merge your changes back and send a pull request!

matthid commented 9 years ago

OK this is now working again, however this is still work in progress. If you want to test it you should checkout 754d517106fb3976d3ad94cebd71f722382478c5 and play with that. https://github.com/Antaris/RazorEngine/blob/754d517106fb3976d3ad94cebd71f722382478c5/src/test/Test.RazorEngine.Core/IsolatedRazorEngineServiceTestFixture.cs#L36 is the method creating the (working) sandbox. In line 71 you can see the usage within a testcase. There are other testcases to check that this sandbox is actually restricting things.

chrfin commented 9 years ago

Just checked your work and this looks great already (in many parts better designed than my stuff), but for the following:

It's also something we can check should we ever find a way to support dynamic/anonymous objects across application domain boundaries.

I found a solution in my project. Mainly you need to allow

new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess)

I'll try to create a pull request with that stuff over the holidays - I just had'nt had the time so far...

chrfin commented 9 years ago

I also did not see some of my additional "security measures", like render timeout or forbidden types. Did you just not implement them (yet) or don't you like that concept at all. Just asking not that I create a pull request for stuff you don't like and will not accept...

matthid commented 9 years ago

Thanks for looking over it and your ideas :). Yeah currently I'm trying to get it back to how it's supposed to work. A timeout sounds like a great idea, would definitely accept a pull request for it. However to save you from a major headache: The branch I linked above is work in progress and the API can definitely change (which would possibly result in a lot of rebasing on your part). I also want to discuss the new API (with @Antaris) before merging it: https://groups.google.com/forum/#!topic/razorengine/_BNFwOOAl3I.

For forbidden types the CodeInspector API could already be used (https://github.com/Antaris/RazorEngine/blob/master/src/source/RazorEngine.Core/Compilation/Inspectors/ICodeInspector.cs)?

The anonymous/dynamic types are already on my radar (the comment was just copied over from an old unit test). I'm wondering why that permission would help when for example anonymous types are not even marked as serializable? I will definitely test this, my current idea was to wrap them into another type (kind of the same trick we use to make them work without isolation).

chrfin commented 9 years ago

I'm sorry it was late last night - here some more info: The permission is needed for a sandboxed AppDomain to support dynamic types. I use the following "dynamic base type":

https://isolatedrazor.codeplex.com/SourceControl/latest#Main/IsolatedRazor/DynamicViewBag.cs

Which is marked serializable and then I can do the following:

dynamic viewBag = new DynamicViewBag(); viewBag.Message = "Hello from ViewBag!"; return templater.ParseTemplateAsync("ViewBag", "@ViewBag.Message", timestamp, viewBag);

Anonymous types are not working AFAIK.

@ Forbidden types: Didn't know about the ICodeInspector, but that does not work for this, as the type references are not available in the CodeCompileUnit (see http://stackoverflow.com/questions/26562708/analyze-codecompileunit-for-used-types-classes). I use Mono.Cecil to analyze the generated assemblies TypeRef table.

@ pull request: I will wait until you merged the new stuff into a "stable branch", before I will fork it of. Maybe you can tell me as soon as you did so?

matthid commented 9 years ago

No Problem :). Ah that's how the dynamics work, thanks! I thought of something similar... I will test a bit and see how we can make anonymous types work as well.

@ ICodeInspector: I'm wondering who uses this API. As we are planing to use Roslyn in the long rung: Maybe we can make this more useful with Razor4 & Roslyn (Maybe its a good idea to release a beta version without that API and see if anyone complains).

@ Task & Thread: Another thing that came into my mind: With Razor4 there is support for C# asyncs within templates (which will use Task under the cover) so we can't really restrict Task anymore, and that means restricting Thread doesn't do much either. (We can't restrict Task as the code we generate will make use of that already)

@ pull request: Sure, I hope this won't take too long.

chrfin commented 9 years ago

@ Task & Thread: As long as you do not use async / await actively in your template it shouldn't be a problem, because the TypeRef table only includes "directly referenced types" AFAIK (I will check and report back). And using that in template which is not allowed to do any IO anyhow this should be no problem...

matthid commented 9 years ago

As far as I understood the changes in Razor4: We will generate an Execute method with Task return type (instead of void currently), so I guess that's directly referenced?

chrfin commented 9 years ago

Yes, thats also how I understud it, but that execute method can be in the "template base class" not in the compiled template, so the compiled template does not contain a direct reference to Task - but I am not sure how this behaves with inheritance and I will try this out over the weekend and report back...

matthid commented 9 years ago

@ pull request: I merged my changes back to develop, feel free to open a pull request on the develop branch. Feel also free to give your opinion on the updated API.

@ Anonymous types: Those are now working in an isolation scenario, even when they are only type arguments of a generic type (#67). Some sick stuff is going on to make that work. DynamicViewBag is now serializable.

The documentation for the new API is currently here: http://matthid.github.io/RazorEngine/ (until we merge back to master)

chrfin commented 9 years ago

A little late, but I now finally had some time to try this out: If the base class uses Task like the following:

public abstract class BaseClass
{
    public Task ExecuteAsync() { Execute();  return Task.FromResult(0); }
    public abstract void Execute();
}

and in another library you inherit that like:

public class SubClass : BaseClass
{
    public override void Execute() { var str = "Hello world!"; }
}

only the library containing the ExecuteAsync has a TypeRef to Task, but the second one only has a TypeRef to BaseClass => for cases like mine you can still prohibit the usage of Task even if the template base class uses them...

matthid commented 9 years ago

Did you try to use Razor4 (4.x-beta)? Razor will just override the Task based method and therefore you have a Task reference. Of course you can work around that by allowing this one instance and so on. What I'm trying to say is that it is error prone and I don't see the value :). Isolation gives you a more isolated environment to work with, without introducing strange limitations.

chrfin commented 9 years ago

No, I did not try it with the actual Razor 4. And I am not planning to use such a feature as an alternative to isolation, but additional to it as I want "all three layers of security/isolation":

I'm using Razor-templates for user-generated content (e.g. mail-templates in my CMS) so "simple isolation" is not secure enought IMO. Because of that I also do not need async/await in my templates as there is no IO which would benefit from it so I could easily work around an async-execute with an "intermediate template" built like my test-app.

BUT I completely understand if you do not want to introduce such a "strange" limitation logic into the main library. I try to find the time over the next few weeks (hopefully) to finally propose you a pull-request for the timeout-feature. Maybe I also find a way to make my third limitation "pluggable" so I can add it "externally" just for my project...

chrfin commented 9 years ago

Just forked the repo to take a look at it and I am not sure where exactly I should look at: Can you point me to the place/class where at the moment or in future the compilation will happen?

matthid commented 9 years ago

Ah now I see, as an additional restriction it makes more sence.

Thread / Task

BUT I completely understand if you do not want to introduce such a "strange" limitation logic into the main library.

It's actually quite the opposite, if such a thing is possible (If we need another dependency I think the best would be to add another library to this repository and another nuget package). What I'm questioning however is: Is there is a way to make this happen reliable? I still don't think so. Especially with future version of the Razor parser in mind (Razor4).

The problem is you can't even guarantee to get your initial Thread back:

The thread is not guaranteed to abort immediately, or at all.

From https://msdn.microsoft.com/en-us/library/ty8d3wta%28v=vs.110%29.aspx

Because we talking about security here we must ensure a possible attacker can't ever use such corner cases in any way. What about Garbage collection and the finalizer? Those happen in another Thread, so the attacker can possibly use it as their background thread and viola there is your background operation :) (I agree that it might be difficult to get there, but Razor 4 gives a lot more potential which I didn't look into)

I'd love to be proven wrong, but it looks there are a lot of attack vectors to consider (maybe to much to handle all). And Razor4 complicates the situation dramatically!

It might be a lot of easier to implement what you need with process encapsulation...

Timeout

I would accept the timeout in the core library, because it is not really a security feature, but something people might want for various other reasons. And we don't need a new external dependency.

Code:

To get started I would take a look into the compilation process: https://github.com/Antaris/RazorEngine/tree/master/src/source/RazorEngine.Core/Compilation After everything is compiled the Assembly is loaded (the result of this process). So maybe after loading the assembly (or before?) is a good time to check it. It always happens here: https://github.com/Antaris/RazorEngine/blob/master/src/source/RazorEngine.Core/Compilation/CompilerServiceBase.cs#L206

Depending on the compiler you use:

The reason you don't have a general location to access the Assembly-location is that I hope to get support for dynamic Assemblies in Roslyn (but I'm not sure if this is happening now that they removed this feature: please see https://github.com/Antaris/RazorEngine/issues/239 and vote for http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/6120992-support-for-collectible-assemblies)

For Timeout you need to look at: https://github.com/Antaris/RazorEngine/blob/master/src/source/RazorEngine.Core/Templating/RazorEngineCore.cs#L132 Every running template is called through this RunTemplate method. The only other way is if the user calls ITemplate.Run itself, but this way of using the API is not encouraged (obsolete) and doesn't work in connection with Isolation

chrfin commented 9 years ago

Thanks for your input: I see I need to investigate more on that topic and definitely wait for Razor 4 to be out... But I'll be looking at the timeout possibilities.

P.S.: I do not need "absolute security" as the templates in question can't be edited by "everyone", but just by users of the CMS which have the appropriate rights, so its more a question of how far I trust such users. But I may will also look into process encapsulation instead of just an isolated AppDomain - but I need to check the performance for that in comparisson to just an isolated AppDomain...