在 ASP.NET 中保护配置文件






4.87/5 (12投票s)
在 ASP.NET 中保护配置文件
问题
大多数 .NET 应用程序和网站将配置设置存储在 .config 文件中。该文件是一个 XML 文件,可以使用 System.Configuration
库从任何 .NET 应用程序轻松使用。该文件经常包含敏感的配置设置,例如身份验证和授权。
例如,数据库连接字符串通常作为键/值对存储在应用程序设置部分中
<add key="MyConnectionString1" value="server=db1;
uid=readonlyuser;pwd=secret;database=topsecret;/>
默认情况下,.config 文件以纯文本存储,并且任何有权访问服务器/应用程序的人都可以轻松读取。
“那又怎样!”,“这有什么问题吗?”
以下是一些例子,说明为什么这实际上可能是一个巨大的问题。
- 许多 Web 应用程序的数据库服务器位于与 Web 服务器不同的机器上。这样做是为了安全原因并提高性能。由于任何访问您的 Web 服务器的人都可以轻松读取您的配置文件,因此他将获得对您的数据库服务器的访问权限。这可能非常不希望,因为数据库可能包含用户名、电子邮件和其他敏感信息。
- 需要在很多人都可以访问的计算机上安装需要数据库访问权限的应用程序。
当然,可以将连接字符串硬编码到源代码中,但即使是混淆的代码也可以很容易地反编译。此外,更改连接字符串可能非常麻烦,因为必须重新编译和重新安装应用程序。
解决方案
在本文中,我将介绍上述问题的三个解决方案。我将介绍前两个解决方案的简短版本,并将介绍第三个解决方案的详细版本。我还会附上第三个解决方案的源代码。
第一个解决方案
在应用程序发布之前加密密码,并在数据库服务器中创建一个服务,该服务将拦截任何登录尝试并解密密码。这可能是最安全的解决方案,但问题在于此解决方案不易实现。
第二个解决方案
使用受保护的部分机制(由 .NET Framework 提供)。
static public void ProtectSection()
{
// Get the current configuration file.
System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None);
// Get the section.
AppSettingsSection section = (AppSettingsSection)config.GetSection("appSettings");
// Protect the section.
section.SectionInformation.ProtectSection(
"RsaProtectedConfigurationProvider");
// Save the encrypted section.
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Full);
}
这种方法可以比作把整个橘子树带回家,而你只需要一袋橘子,因为整个部分都会被加密。
如果这就是你想要的,这里有一篇很好的文章描述了这种方法。
第三个解决方案(我首选)
在此解决方案中,只有某个部分的某些设置使用 DPAPI 加密。
不幸的是,DPAPI 并不是一个完美的解决方案
DPAPI 是一种基于密码的数据保护服务。它需要密码才能提供保护。当然,缺点是 DPAPI 提供的所有保护都依赖于提供的密码。 DPAPI 使用经过验证的加密例程(特别是强大的 Triple-DES 算法)和强密钥来抵消这一点,我们将在后面更详细地介绍这些密钥。由于 DPAPI 专注于为用户提供保护,并且需要密码才能提供这种保护,因此它在逻辑上使用用户的登录密码进行保护。
尽管如此,DPAPI 为您的敏感设置提供了相当好的保护。
值得注意的是,DPAPI 安全性是特定于机器的,这意味着在一台机器上加密的数据无法在另一台机器上解密。
代码演练
要加密的设置在配置文件的 <appSettings>
部分中指定。
<add key="KeysToEncrypt" value="ValuableKey1,ValuableKey2" />
其中,value
是一组要加密的键。
以下是我们在本例中要加密的设置
<add key="ValuableKey1" value="Top Secret 1" />
<add key="ValuableKey2" value="Top Secret 2" />
由于 DPAPI 安全性是特定于机器的,因此我们需要在它将运行的机器上(对于 Web 应用程序,是 Web 服务器)加密配置文件。
ConfigUtility.ConfigEncryptToFile();
ConfigUtility.ConfigDecryptToMemory();
ConfigEncryptToFile
方法加密在“KeysToEncrypt
”中指定的键并更新配置文件
public static void ConfigEncryptToFile()
{
string encrypted = ConfigurationManager.AppSettings.Get("Encrypted");
if (encrypted == null || !encrypted.Equals("True"))
{
string keysToEncrypt = ConfigurationManager.AppSettings.Get("KeysToEncrypt");
if (keysToEncrypt != null && keysToEncrypt.Length > 0)
{
Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
AppSettingsSection appSettings = config.AppSettings;
string[] keys = keysToEncrypt.Split(',');
foreach (string key in keys)
{
KeyValueConfigurationElement kv = appSettings.Settings[key];
if (kv != null)
{
kv.Value = EncryptUtility.EncryptString
(EncryptUtility.ToSecureString(kv.Value));
}
}
appSettings.Settings.Add("Encrypted", "True");
config.Save(ConfigurationSaveMode.Modified);
}
}
}
ConfigDecryptToMemory
重新加载配置文件并解密这些键
public static void ConfigDecryptToMemory()
{
ConfigurationManager.RefreshSection("appSettings");
string encrypted = ConfigurationManager.AppSettings.Get("Encrypted");
if (encrypted != null && encrypted.Equals("True"))
{
string keysToDecrypt = ConfigurationManager.AppSettings.Get("KeysToEncrypt");
string[] keys = keysToDecrypt.Split(',');
foreach (string key in keys)
{
string value = ConfigurationManager.AppSettings.Get(key);
value = EncryptUtility.ToInsecureString
(EncryptUtility.DecryptString(value));
ConfigurationManager.AppSettings.Set(key, value);
}
}
}
重要的是要注意,如果我们真的非常担心安全性,最好只在我们实际使用加密密钥时才解密它们,并将它们作为内存中的 SecureStrings
保留。
还值得注意的是,EncryptUtility
类包含熵,并且可以通过生成伪随机“熵”来进一步提高安全性。
EncryptUtility
方法由 Jon Galloway 编写。