简体   繁体   中英

How secure is it to dynamically load assemblies in ASP.NET Core 2?

at the moment I am working on a Webproject with ASP.NET Core 2.2 and was doing some research how to make the application extendable for changes in the future without building and deploying a new version all the time. So my idea was to implement some kind of plugin system into the web app and load new features dynamically in a "hot plug" mode with the app still running.

I did successfully find the ApplicationPart and ApplicationPartManager that gives the possibility to inject parts from external sources.

What I did not find, indeed, was how secure this approach is. Let's say my web app comes with an installer and is made to be shared. And plugin programming would be an open option for the community using the application.

How secure is it to load application parts dynamically? Is there a risk of injecting malicious code that is provided as a plugin? Can these risks be covered somehow?

Edit: It seems that I did not express my questions clearly.

Is it possible to crash the application with this approach?

Is it possible to destroy or change the database in a way that it becomes not usable anymore?

Is it possible to inject plugin code that sends possible sensitive data to third party endpoints, eg identity information?

And in the end, is it possible to limit the "access" plugin code has within the application? Something like letting it only query database tables that are related to the plugin?

I agree with Fildor, the definition of 'secure' is up to discussion. Although you've nicely edited your posts with some questions, which gives us something to talk about.

First of, lets start by saying whenever the CLR loads the assemblies which contains the ApplicationPart s, these are loaded into the default AppDomain of your application. Now we could debate whether .NET Core contains AppDomain s. The point here is to understand that the assemblies that are loaded, are all able to communicate with each other. So now that we know this, and that at some point the main application (webapp) will invoke some code that is inside your custom (plugin) assembly, lets take a look at your questions:

Is it possible to crash the application with this approach?

Yes. Lets say this code is executed whenever the plugin is loaded:

public void Run()
{
    this.Run();
}

This would result in a stackoverflow exception, which cannot be handled by usercode and by default the CLR will terminate the process, meaning your application will crash.

Is it possible to destroy or change the database in a way that it becomes not usable anymore? And is it possible to inject plugin code that sends possible sensitive data to third party endpoints, eg identity information?

I grouped these questions as in essence, what you want to know is: what are the limitations and capabilities of dynamically loaded and invoked code?

The answer is anything . They can access all your assemblies, so also your DatabaseManager type (for example). One could instantiate this type from within the plugin and use it to manipulate your database. Besides that, they can do anything that the .NET framework offers, if they want to sent something to a website, they could easily instantiate a WebClient and make the request.

Lets forget the ApplicationPart feature and imagine we created our own plugin system and this is my main application:

    class Program
    {
        private static string secretUser = "SecretUser";

        static void Main(string[] args)
        {
            Console.WriteLine($" > Hello {secretUser}");

            LoadPlugins();

            Console.WriteLine($" > System fully loaded! Hello again {secretUser}");
        }

    }

LoadPlugins will search through the currently executing assembly's directory to discover dynamically linked libraries (DLLs) and it will try to load these using some data contract. Lets say the contract enforces a plugin to have a void Run . I could have the following code inside this method to modify the original application and for example, modify the secretUser variable like this:

public void Run()
        {
            var mainClass = System.AppDomain.CurrentDomain.GetAssemblies()
                .Select(asm => asm.EntryPoint?.DeclaringType)
                .Single(x => x?.Name == "Program");

            var fieldDef = mainClass
                .GetField("secretUser",
                    BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Static);

            fieldDef.SetValue(mainClass, "HackerPlugin");
        }

Now whenever the code runs, at first, SecuretUser is displayed, but whenever the malicious plugin is loaded, the application will display HackerPlugin , as the plugin uses reflection to access the instance of the main application and modify its contents. This may sound like a security issue, but in fact, this is by design and this made the .NET ecosystem as popular as it is today because it solves a lot of other problems that otherwise we would've required to handle.

So basically; if security is a concern you have, you shouldn't use the ApplicationPart approach, as your code will be shared (and thus accessible) with the plugins.

What you want is to isolate the plugin code from your applications' code. This can be achieved by either; using multiple AppDomain s (this can still cause process termination) or use multiple processes and have these processes communicate with each other. Processes are standalone and cannot (by default) read each others memory regions.

Do note that the ApplicationPart does a lot of this for your, like discovering and challenging the loading of (external) dependencies, if you decide to built your custom solution, you also need to built this yourself -- this doesn't mean you can look into the .NET repository and use their solution as reference though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM