首页 > 解决方案 > 在 ASP.NET Core 2 中动态加载程序集的安全性如何?

问题描述

目前我正在使用 ASP.NET Core 2.2 开发一个 Web 项目,并且正在研究如何使应用程序可扩展以适应未来的变化,而无需一直构建和部署新版本。所以我的想法是在 Web 应用程序中实现某种插件系统,并在应用程序仍在运行的情况下以“热插拔”模式动态加载新功能。

我确实成功地找到了 ApplicationPart 和 ApplicationPartManager,它们可以从外部源注入部件。

事实上,我没有发现这种方法有多安全。假设我的网络应用程序带有一个安装程序并且可以共享。对于使用该应用程序的社区来说,插件编程将是一个开放的选择。

动态加载应用程序部件有多安全?是否存在注入作为插件提供的恶意代码的风险?这些风险能以某种方式被覆盖吗?

编辑:似乎我没有清楚地表达我的问题。

是否有可能使用这种方法使应用程序崩溃?

是否可以以不再可用的方式破坏或更改数据库?

是否可以注入将可能的敏感数据发送到第三方端点的插件代码,例如身份信息?

最后,是否可以限制应用程序中的“访问”插件代码?比如让它只查询与插件相关的数据库表?

标签: c#securityasp.net-core-mvc

解决方案


我同意 Fildor,“安全”的定义有待讨论。尽管您已经很好地编辑了您的帖子并提出了一些问题,这给了我们一些话题。

首先,让我们先说每当 CLR 加载包含 s 的程序集时,这些程序集都会加载到您的应用程序ApplicationPart的默认值中。AppDomain现在我们可以讨论 .NET Core 是否包含AppDomains。这里的重点是要了解加载的程序集都能够相互通信。所以现在我们知道了这一点,并且在某些时候主应用程序(webapp)将调用您的自定义(插件)程序集中的一些代码,让我们看看您的问题:

是否有可能使用这种方法使应用程序崩溃?

是的。假设每当加载插件时都会执行此代码:

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

这将导致堆栈溢出异常,用户代码无法处理该异常,默认情况下 CLR 将终止进程,这意味着您的应用程序将崩溃。

是否可以以不再可用的方式破坏或更改数据库?是否可以注入插件代码,将可能的敏感数据发送到第三方端点,例如身份信息?

我将这些问题归类为本质上,您想知道的是:动态加载和调用代码的限制和能力是什么?

答案是任何东西。他们可以访问您的所有程序集,也可以访问您的DatabaseManager类型(例如)。可以从插件中实例化这种类型并使用它来操作您的数据库。除此之外,他们可以做任何 .NET 框架提供的事情,如果他们想向网站发送内容,他们可以轻松地实例化 aWebClient并发出请求。

让我们忘记这个ApplicationPart功能,想象我们创建了自己的插件系统,这是我的主要应用程序:

    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将搜索当前正在执行的程序集的目录以发现动态链接库 (DLL),并将尝试使用某些数据协定加载这些库。假设合同强制插件具有void Run. 我可以在此方法中使用以下代码来修改原始应用程序,例如,secretUser像这样修改变量:

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");
        }

现在每当代码运行时,首先SecuretUser会显示,但每当加载恶意插件时,应用程序都会显示HackerPlugin,因为插件使用反射来访问主应用程序的实例并修改其内容。这听起来像是一个安全问题,但事实上,这是设计使然,这使得 .NET 生态系统像今天一样流行,因为它解决了许多其他我们需要处理的问题。

所以基本上;如果您担心安全性,则不应使用该ApplicationPart方法,因为您的代码将与插件共享(因此可访问)。

您想要的是将插件代码与应用程序代码隔离开来。这可以通过以下任一方式实现;使用多个AppDomains(这仍然会导致进程终止)或使用多个进程并让这些进程相互通信。进程是独立的,不能(默认情况下)读取彼此的内存区域。

请注意,ApplicationPart这为您做了很多事情,例如发现和挑战(外部)依赖项的加载,如果您决定构建您的自定义解决方案,您还需要自己构建它——这并不意味着您可以查看进入 .NET 存储库并使用他们的解决方案作为参考。


推荐阅读