13

I'd like to develop an application that accepts plugins from the user community, similar to how Chrome or Firefox does. This would be a web application, so each "instance" of the application for individual users would run different plugins (plugins would be loaded as singleton instances, but would be "active" only for certain users). I'm planning on implementing the application in .NET, and trying to come up with an architecture for the plugin model.

Here are my desired attributes:

  • Plugins are built entirely outside of my core application, as separate assemblies.
  • Plugins run in their own "locked down", low-trust environment. Probably a separate AppDomain.
  • Plugins can only act through an API that I provide. E.g. I will pass them some sort of facade as an interface, and they can only call to that, not call to any other assemblies. I can't have plugins that can arbitrarily take actions on the web server, e.g. affect the file system.
  • A fatal crash in the addin cannot affect the stability of the core application.

It seems like System.AddIn is my best bet, but I am unclear on how I can force the loaded addins to only work through the API that I provide, and not load any other assemblies. Does System.AddIn provide that functionality? Also, can System.AddIn be used with ASP.NET / IIS?

What other options do I have besides System.Addin?

RationalGeek
  • 9,425
  • 11
  • 62
  • 90
  • Have you looked at MEF? – Daniel Mann Apr 03 '13 at 20:04
  • 1
    You could have a look at this .NET game "Terrarium" that was created by Microsoft to illustrate the capabilities of .NET. It has the notion of secured plugin, as each creature in the game is some sort of plugin: http://terrarium2.codeplex.com/ – Simon Mourier Apr 04 '13 at 05:46
  • @SimonMourier I will take a look at that. You could turn that into an answer if you'd like to summarize the technique they use and post it here. – RationalGeek Apr 04 '13 at 15:06
  • @DanielMann MEF does not provide the isolation or sandboxing that I need. MEF is really only applicable in a "trusted plugin" scenario. I'm looking to implement "untrusted plugins". – RationalGeek Apr 04 '13 at 15:06

3 Answers3

5

It looks like your primary concern is isolation (for security as well as for robustness).

As such your best option here is to activate your addin in a separate AppDomain. Within this domain you can control what assemblies you would allow to be loaded (see AppDomainSetup class).

Your main code will be also protected from anything untoward happening inside a plugin: All the plugin methods will have to work with are copies of the core objects (unless objects you pass over are inherited from MarshalByRefObject, in which case all bets are off). The exceptions in the addin methods can be handled by wrapping all calls in try except, or through AppDomain's UnhandledExcption event.

Keep in mind that there is a performance penalty for crossing the AppDomain boundaries. The call is essentially a remoting call.

Another potential problem is how do you plan to manage the AppDomains. I never tried to create more than a few AppDomains in a process, but I would expect trouble here. Combining Addins for multiple users in a single domain wold challenge the protection you are trying to build into this

In response to @RationalGeek question - UnhandledException event allows to do that - sort of. You can subscribe to this event in both your core domain as well as the plugin domain, but there is a lot of uncertainties in doing it this way - see the article I referenced for more details. May be a better option would be to wrap every call to your API in try/except

mfeingold
  • 7,094
  • 4
  • 37
  • 43
  • Can I adequately protect my core application from crashing if one of the other appdomains has an unhandled exception? – RationalGeek Apr 04 '13 at 15:03
1

You could try using AppDomains and handle the unhandled exceptions. To avoid appdomain crashes, you will have to handle the AppDomain.UnhandledException

In the above link, note the following statement

Starting with the .NET Framework 4, this event is not raised for exceptions that corrupt the state of the process, such as stack overflows or access violations, unless the event handler is security-critical and has the HandleProcessCorruptedStateExceptionsAttribute attribute.

So you may need to explicitly handle some configuration.

I have read a lot of issues claiming that when unhandled exceptions occur on different threads in the child domain, the bubble up and bring down the parent domain. If so, then loading all the plugins into a separate process with a appdomain per plugin or loading the plugins into separate process may be advisable.

I also came across the following question on SO that I believe you would find very helpful

Community
  • 1
  • 1
Patrick D'Souza
  • 3,491
  • 2
  • 22
  • 39
-1

Why not just use reflection and discover available plug-ins at run time? Assembly.LoadFile to get assembly, GetExportedTypes to get your objects Create an instance with Activator.CreateInstance

Then just run the plug-in in a try loop to make sure it does not affect the system. Though you will need some significant architecture to provide actual stability.

Stay away from MEF unless you have complex scenarios for plug-in management. If it's just allowing users to use theirs and including a few of your own, just use reflection.

Dahlia
  • 21
  • 1