System.Addin - Creating secured ASP.NET MVC plugins
Asked Answered
P

2

26

Lately my focus has been on creating an ASP.NET MVC application that can host 3rd party MVC plugins. Ideally, development of those plugins would follow these rules:

  1. Plugins can be developed in a standard MVC project, and be able to use all the existing infrastructure of the ASP.NET MVC framework.
  2. The complexity of compiling the plugin MVC project and including into a host MVC application should not be severe.
  3. Any changes to the normal development flow of an MVC application would be the bear minimum

After some research I've come up with the following approaches to achieve this, each with their own advantages and disadvantages.

Approach 1 - MVC plugin assembly loaded into main MVC AppDomain

Work Flow

  • Develop plugin inside a separate MVC project.
  • Compile the assembly and load it and any dependencies into the host application via PreApplicationStartMethodAttribute, MEF or a basic assembly reference within the host project (if possible).
  • Map a route to the plugin controllers so that the plugin is treated as an Area within the host.
  • Place the plugin views into the correct area folder. The layout file will need to be changed so that the layout path points to an area based location, rather than the root of the application (which would have been the case in the development MVC project)
  • When a request comes in for the plugin, ASP.NET will use the existing areas functionality to route the request to the correct controllers and look in the correct location for the view files.

Advantages

  1. Will work as seamlessly as if the controllers were embedded in the host MVC application assembly.
  2. Simple to include the assemblies into the host App Domain before application start (PreApplicationStartMethodAttribute, project reference) and after application start (MEF)

Disadvantages

  1. No sandboxing - the controllers will have the same trust level as host.

Conclusion

This is the easiest approach, however it is also the least secure. It essentially removes the possibility of allowing untrusted developers to create plugins because those plugins will have the same trust level as the host application (meaning that if the host application can execute methods such as System.IO.File.Delete, so can the plugin)

Approach 2 - MVC plugin assembly running in own AppDomain via MAF

This approach intends to allow the creation of MVC plugins that can be sandboxed into their own AppDomains and used by the host via the System.Addin libraries.

Structure

  1. A route is setup in the host that determines whether the url being processed is targeting a plugin. May have a pattern such as example.com/p/{plugin}/{controller}/{action}/{id}

  2. All routes that have the above pattern are mapped to a host controller that has a module routing action. That action looks at any given route and determines the appropriate plugin to process the request based on the {plugin} segment.

  3. The plugin view is a receiver/sender object that acts as a gateway to the plugin controllers. It has a method called AcceptRequest that receives a RequestContext from the host, and that returns an ActionResult.

  4. The plugin pipeline contains adapters that can serialise RequestContext and ActionResult for transmission across the isolation boundary of the pipeline.

Execution Flow

  1. A route for a plugin is matched and the plugin routing controller is called.

  2. The controller loads the required plugin into it's own AppDomain and calls AcceptRequest, passing through the RequestContext (which is serialised via the pipeline)

  3. AcceptRequest receives the context and determines the appropriate controller to execute based on that request (using a custom controller factory).

  4. Once the controller has finished executing the request, it returns an ActionResult to the receiver object which then passed that ActionResult (also serialised via the pipeline) back to the host AppDomain.

  5. The controller that initially called AcceptRequest can then return the ActionResult to the host MVC execution pipeline, as if it processed the request itself. The plugin AppDomain can then be unloaded if so wished.

Advantages

Plugin will be sandboxed in it's AppDomain, thus any permission set can be used that suits the host.

Disadvantages

  • Would have to be possible to serialise RequestContext and ActionResult.
  • Will possibly break other ASP.NET MVC functionality within the isolated AppDomain.

Conclusion

This approach sounds good on paper, but I'm not sure if it's possible/feasible to serialise the RequestContext and ActionResult objects, as well as run an MVC controller in isolation.

Questions

The first approach is fine if the code is being created by trusted developers. I know that I'm not going delete all the host view files or it's web.config file. But ultimately, if you want third party devs to create plugins for your MVC app, you need to be able to sandbox their code.

From all my research, the System.Addin library makes it easy to implement a host/plugin environment when you are using simple API based class libraries. However it seems that it isn't easy to do this when MVC is involved.

Some questions I have are:

  1. Is the second approach I've outlined here feasible?
  2. Is there a better way to do this?
  3. Is there going to be easier ways to achieve MVC plugin isolation in the future?
Poultryman answered 17/5, 2012 at 2:29 Comment(3)
MAF is a really heavy-handed tool to use. Mind you, you have some pretty lofty goals attempting to do this in the first place. I would have you ask yourself, 'What am I trying to achieve by giving the MVC-host-site control over the plugin AppDomain'? I would instead go the route of making plugins separate websites (with sep. AppPools), and facilitate some kind of out-of-band way of getting the plugin to inter-communicate with the host. Defer all your complexity to deployment time, and make the actual runtime as close to standard MVC as possible. (I've just used this approach on a job, in-fact)Orphaorphan
@ChrisPaynter -- Did you ever find a good solution to this? I'm looking for one, too, and the only thing I've come across is the Umbraco way (shadow copy DLLs when in medium trust).Jello
No i ended up abandoning it. It was overkill for what I needed. Mind you I learnt a lot about trust in the framework, so it was worth it.Poultryman
B
1

You're going to end up making separate sites for each plugin. This way you can create reduced-privilege users for each AppPool and a systems administrator can install the "plugin" as a website running under that user.

Any alternatives are going to exhibit the Inner Platform antipattern. Unless you have a lot of time and money to spend developing a plugin-hosting system, you're going to become mired in it and resent it. I speak from experience.

The sites can share AspnetIdentity user repos and you can provide your core objects as a dll that can be referenced. Host your content (script files, css, images) on a CDN so they can be referenced. If you want to share views with your child sites, compile them in as resources:

Including Pre-Compiled Views in an ASP.NET MVC Web Application

Good luck!

Bufordbug answered 30/6, 2015 at 16:49 Comment(0)
G
0

IMHO System.AddIn is a a bit overkill for what you are trying to do.

Are you familiar with the System.Security.Permissions namespace? If not you could have a look at the FileIOPermission. Perhaps you could sandbox your extensible system by using (and why not, even extending) the Code Access Security mechanism of .NET.

Goldfinch answered 21/5, 2012 at 15:45 Comment(1)
I've looked into it, however from my understanding this approach isn't plausible, as if all the code is in the same AppDomain it is subject to the same security trust level anyway. I don't want to go down the path of extending CAS.Poultryman

© 2022 - 2024 — McMap. All rights reserved.