在ASP.NET Core 2中动态加载程序集的安全性如何?

时间:2019-07-12 08:15:34

标签: c# security asp.net-core-mvc

目前,我正在使用ASP.NET Core 2.2进行一个Web项目,并且正在做一些研究,以使应用程序可扩展以适应将来的更改,而无需始终构建和部署新版本。 因此,我的想法是在Web应用程序中实现某种插件系统,并在应用程序仍在运行的情况下以“热插拔”模式动态加载新功能。

我确实成功找到了ApplicationPart和ApplicationPartManager,从而可以从外部来源注入零件。

实际上,我没有发现这种方法的安全性。假设我的网络应用带有安装程序,可以共享。插件编程将是使用该应用程序的社区的一个开放选项。

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

编辑: 看来我没有清楚地表达我的问题。

是否可以通过这种方法使应用程序崩溃?

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

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

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

1 个答案:

答案 0 :(得分:2)

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

首先,让我们先说一下,只要CLR加载了包含ApplicationPart的程序集,它们就会被加载到应用程序的默认AppDomain中。现在我们可以讨论.NET Core是否包含AppDomain。这里的重点是要了解已加载的程序集都能够相互通信。因此,现在我们知道了这一点,并且主应用程序(webapp)会在您的自定义(插件)程序集中调用 some 代码,让我们看一下您的问题:

  

是否可以通过这种方法使应用程序崩溃?

是的。可以说,每当插件加载时,就会执行以下代码:

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

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

  

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

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

答案是一切。他们可以访问您的所有程序集,也可以访问您的DatabaseManager类型(例如)。可以从插件中实例化此类型,然后使用它来操作数据库。除此之外,他们可以执行.NET框架提供的任何功能,如果他们想向网站发送内容,则可以轻松地实例化WebClient并发出请求。

让我们忘记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,因为该插件使用反射来访问main的实例。应用程序并修改其内容。这听起来像是一个安全问题,但实际上,这是设计使然,它使.NET生态系统像今天一样流行,因为它解决了许多其他我们需要处理的问题。

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

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

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