配置与静态属性、安全问题

本文关键字:安全 问题 属性 静态 配置 | 更新日期: 2023-09-27 18:20:47

我正在开发一个类库,我需要提供一种设置配置参数的方法。我可以创建一个配置部分,也可以公开静态属性。我关心的静态属性是安全性。是什么防止恶意组件在运行时进行更改?例如,在ASP.NET MVC中,使用静态属性配置路由。这安全吗?恶意组件是否可以添加/删除路由?


"不受信任的组件"最初是如何进入我的应用程序的?例如NuGet。我们不知道是什么,是谁干的,也不知道它是否包含一些不希望的状态变化。

"不受信任的组件"将如何运行?在ASP.NET中,您只需要PreApplicationStartMethodAttribute就可以在应用程序启动时运行一些代码。

配置与静态属性、安全问题

当你认为某件事是安全威胁时,你还应该考虑你试图保护谁。

为了让"恶意代码"更改static属性的值,需要将此代码加载到AppDomain中并运行。现在想想,一个恶意攻击者已经设法让他的代码在你的AppDomain中运行了——你真正关心的是static属性吗?这样的攻击者可能会做得最糟糕。

除非你有一个需要加载来自外部不受信任来源的程序集/代码的场景,否则我认为你真的不需要防御用户访问你的属性(无论如何,不是从安全角度来看——可用性是另一回事)。

EDIT-关于外部不可信代码

我仍然认为这不是你真正关心的问题。如果我理解正确的话,您正在开发和提供一个库,供第三方在其应用程序中使用。

如果应用程序所有者决定使用他不信任的某个外部库,将其添加到应用程序中,并允许其运行,那么这不是您的问题,而是应用程序所有者的问题。

在这种情况下,我上面所说的一切仍然适用。恶意代码可能会比设置属性更糟糕。它可能会扰乱内存、损坏数据、淹没线程池,甚至很容易使AppDomain崩溃。

关键是,如果您因为只提供类库而不拥有该应用程序,则不需要在加载类的AppDomain内部运行的代码。

注:关于NuGet,我不会太担心的。NuGet是一种静态工具。如果我理解正确的话,它不会在运行时做一些事情,比如下载代码和运行它。它只用于设计时下载二进制文件、添加引用,以及可能添加代码。我认为,假设使用NuGet下载包的应用程序所有者会尽职调查以确保包的安全是完全合理的。而且在开发过程中,他只需要做一次。

正如前面的答案所指出的,这里没有太大区别。

恶意代码可以设置静态属性,恶意代码可以更改配置文件。后者可能更容易从外部理解,并且无论代码以何种方式运行都可以完成(它不必是.NET,不必在应用程序域中运行,而且如果有人能够手动更改文件,它也不必是代码),因此使用静态属性有一点安全优势,尽管这是一个相当虚假的问题,考虑到我们很可能只是将问题转移了一点,因为调用代码很可能使用配置本身来决定将属性设置为什么!

还有第三种可能性,即您有一个实例,该实例具有设置属性的实例成员,而正是调用代码使该实例成为静态的。这可能与你正在做的事情完全无关,但值得考虑的情况是,有人可能希望在同一应用程序域中使用两组配置参数运行你的代码。作为一个安全问题,它与静态成员的问题基本相同,只是它可能会影响序列化问题。

因此,到目前为止,配置文件的缺点在于,它们可能会受到与您的代码完全独立的代码的攻击,但需要注意的是,这些信息可能最终会出现在其他地方的配置文件中。

无论采取哪种方法,访问的安全性都取决于加载部分受信任代码的方式。

代码应该加载到它自己的应用程序域中,并且该应用程序域上的安全性应适当设置为它的可信程度。如果可能的话,不应该是你的库在做这件事,而是让调用代码来决定它加载的任何部分受信任的代码要设置的策略。当然,如果它加载部分受信任代码是你库的固有目的,那么它必须这样做,但一般来说,对于代码是完全信任还是部分信任,它应该保持不可知,除非在适当的时候要求某些权限。如果由您的库加载此代码,那么您需要决定授予应用程序域的适当权限。实际上,这应该是仍然可以执行加载作业的最低权限。这可能不包括FileIOPermission,因此阻止它写入配置文件。

现在,无论您的库还是调用代码已经加载了部分受信任的代码,您都需要考虑对公开的类及其成员需要哪些权限。这涵盖了静态setter属性,但如果您采用配置文件方法,则仍然是必要的,因为您的场景仍然涉及到有部分受信任的代码访问您的库。

在某些情况下,这些方法不需要更多的保护,因为它们本身就有保护。例如,如果你试图访问一个文件,但调用代码没有这样做的权限,那么你的代码将失败,并出现一个安全异常,该异常将传给调用代码。事实上,您可能必须采取相反的做法,并采取措施允许部分受信任的代码调用您的方法(如果您以安全的方式访问文件,因为调用者无法影响访问哪个文件或访问方式,则此时您可能希望Assert文件访问权限)。

在其他情况下,您可能需要添加保护,因为调用代码不会立即尝试安全限制操作,但可能会导致受信任的代码以不适当的方式运行。例如,如果您的代码存储稍后操作使用的路径,那么本质上调用该代码允许以特定方式进行文件访问。例如:

public string TempFilePath{get;set;}
public void WriteTempData(string data)
{
  using(sw = new StreamWriter(TempFilePath, true))
    sw.Write(data);
}

这里,如果恶意代码设置TempDirPath,则可能会导致受信任代码稍后对WriteTempData的调用通过重写来损坏重要文件。这里的一个明显方法是在适当的FileIOPermission对象上调用Demand,使得唯一可以设置它的代码将是已经被信任无论如何都可以写入任意位置的代码(这当然可以通过限制TempDirPath的可能值和要求在允许的位置集合内写入的能力来组合)。

您也可以要求某些权限的联合,当然也可以创建自己的权限,尽管使用框架定义的权限的联合有更好地适应现有代码的优势。

是什么防止恶意组件在运行时进行更改?

这取决于"恶意组件"的定义。配置实际上是为了允许在运行时进行更改。

如果您通过代码(无论是静态属性还是实例属性等)来处理此问题,那么您确实具有直接控制允许设置的明显优势,因为您的属性设置器可以根据您的意愿进行控制。如果应用程序需要,您还可以添加某种形式的安全性,因为您可以控制设置方式。

有了配置部分,你唯一的控制就是读取值——你不能控制写入,但必须在读取时验证设置。

当然,它可以被提供这些抽象的底层类更改,即使是在被定义为私有成员的情况下。

想象一个安全拦截器,它根据已验证或匿名用户的定义权限提供每个请求。

我通常将配置文件和静态变量一起使用。我将静态变量定义为private,并且只使用"get"方法来公开值。所以它是不能在课堂之外改变的。

我创建了一个类来处理实现"IConfigurationSectionHandler"接口的配置。我的实现是针对ASP.NET Web应用程序的。

步骤1:在web.config文件中创建一个节,以便稍后处理。

<configuration>
    <configSections>
        <section name="XXXConfiguration" type="Company.XXXConfiguration, Company"/>
        ...
    </configSections>
    <XXXConfiguration>
        <Variable>Value to set static variable</Variable>
    </XXXConfiguration>
    ...
<configuration>

步骤2:创建一个类来处理以前的配置部分。

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Xml;
using System.Configuration;
namespace Company{
    public class XXXConfiguration : IConfigurationSectionHandler
    {
        /// <summary>
        /// Initializes a new instance of LoggingConfiguration class.
        /// </summary>
        public XXXConfiguration() {}
        private static string _variable;
        public static string Variable
        {
            get {return XXXConfiguration._variable; }
        }
        public object Create(object parent, object configContext, XmlNode section)
        {
            // process config section node
            XXXConfiguration._variable = section.SelectSingleNode("./Variable").InnerText;
            return null;
        }
    }
}

步骤3:在应用程序启动时使用System.Configuration.ConfigurationManager的GetSection方法。在Global.asax 中

void Application_Start(object sender, EventArgs e) 
{
      // Code that runs on application startup
      System.Configuration.ConfigurationManager.GetSection("LoggingConfiguration");
      ...
}