ASP.NET 2.0 中 Web 服务器群集连接字符串的管理






4.79/5 (17投票s)
2006 年 4 月 27 日
9分钟阅读

153177

607
如何在 ASP.NET 2.0 中管理 Web 服务器群集的数据库连接字符串和连接字符串加密。
引言
我的连接字符串该怎么办?如何加密它们以保护我的登录信息?我应该把它们存放在哪里?如何在服务器之间共享它们?这些问题困扰开发人员已久。对于 .NET 1.0/1.1,人们普遍认为将这些信息存储在 web.config 文件中。使用框架加密库并让 Windows 内部管理加密密钥,加密相当简单。
但是 Web 服务器群集问题更多。加密算法通常与其他加密需求(例如,用户密码)共享,因此所有机器都必须共享一个公共加密密钥。密钥管理随后成为一个问题,因为如果有人找到您的密钥,只有少数算法可以测试,然后您的数据就会被泄露。微软建议将密钥或连接字符串存储在受保护的注册表项中,但在实践中我发现这很麻烦,因为它需要以特殊权限用户身份执行您的 ASP.NET 应用程序。将密钥存储在 web.config 文件中更容易,但这样您就将加密数据和解密密钥存储在同一个文件中——这不是一个好主意。机器配置是一个更好的选择,但偶尔会给同一台机器上的其他应用程序(如 SharePoint)带来问题。所以有解决方案,但没有完美的答案。
简单示例:web.config 中未加密的连接字符串
我们将从一些简单的测试代码开始检查我们的连接。这是计算机经典 Hello World,使用数据库来问好。
使用 IIS 创建一个新网站进行托管
- 文件 > 新建 > 网站 >
- 在对话框中,选择以下设置
- 模板 = ASP.NET 网站
- 位置下拉列表 = HTTP
- URL = https:///connectionTest
接下来,添加下面列出的代码。连接字符串假定 SQL Server 2005 作为命名实例安装在 myserver 上。对于 SQLExpress,服务器将类似于“localhost\SQLExpress”。
测试代码列表
这是一些为 .NET 1.1 编写的简单代码。我们将以此为起点。
Web.config
在这里,我们定义了连接字符串。您可以看到我为 AdventureWorks 添加了一个名为 test 的 SQL 用户,密码为 test。
<configuration>
<appSettings>
<add key="myconnection"
value="server=myserver\sql2005;initial cata
log=AdventureWorks;user id=test;pwd=test;"/>
</appSettings>
...
</configuration>
Default.aspx.cs
这是打开连接并让数据库问好的代码片段。页面上有一个名为“Label1
”的 ASP Label
控件和一个名为“Button1
”的 ASP Button
控件。
...
using System.Data.SqlClient;
...
protected void Button1_Click(object sender, EventArgs e)
{
string constr = ConfigurationSettings.AppSettings["myconnection"];
SqlConnection sqlConn = new SqlConnection(constr);
sqlConn.Open();
SqlCommand cmd = sqlConn.CreateCommand();
cmd.CommandText = "SELECT 'Hello World'";
try
{
SqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
this.Label1.Text = reader.GetString(0);
}
else
{
this.Label1.Text = "No Result";
}
}
catch (Exception ex)
{
this.Label1.Text = "Error: " + ex.Message;
}
finally
{
if(sqlConn.State != ConnectionState.Closed)
sqlConn.Close();
}
}
ASP.NET 2.0 的测试代码更新
Web.config
我的 <appSettings>
配置节怎么了?它仍然在那里并且仍然可用,但微软已决定将连接字符串提升到它们自己的节中。更新后的代码如下所示
<configuration>
<appSettings/>
<connectionStrings>
<add name="myconnection"
connectionString="server=myserver\sql2005;initial
catalog=AdventureWorks;
user id=test;pwd=test;"/>
</connectionStrings>
</configuration>
Default.aspx.cs
访问配置设置的方式也略有改变。旧的 System.Configuration.AppSettings
对象已被弃用,取而代之的是新的 System.Configuration.ConfigurationManager
。对于连接字符串,我们访问 ConnectionStrings
集合以获取 ConnectionStringSettings
对象。
protected void Button1_Click(object sender, EventArgs e)
{
ConnectionStringSettings conn =
System.Configuration.ConfigurationManager.
ConnectionStrings["myconnection"];
string constr = conn.ConnectionString;
...
}
这就是连接字符串的简单、未加密方式。它可以在 Web 服务器群集中正常运行,但您已经暴露了 SQL 凭据,这并不特别安全。那么 Web 服务器群集的加密呢?
救命!我的代码不工作了!
如果您收到以下错误
System.Data.SqlClient.SqlException: 建立到服务器的连接时发生错误。连接到 SQL Server 2005 时,此失败可能是由于在默认设置下,SQL Server 不允许远程连接。 (提供程序:SQL Network Interfaces,错误:26 - 找不到指定的服务器/实例)
您可能需要修改您的 SQL Server (2005) 以允许通过 SQL Server 外围应用配置器 工具(在 开始 > 程序 > Microsoft SQL Server 2005 > 配置工具 下)进行外部连接。或者您的连接字符串不正确。
加密连接字符串
加密 web.config 的 connectionStrings
节既简单又有趣!只需按照 MSDN 中概述的 几个简单步骤操作(单击“配置”链接)。您可以使用 aspnet_regiis 实用程序来加密应用程序的 web.config 中的一个节(因此我们使用 IIS 来托管我们的开发站点,而不是 VS2005 内置的 Web 服务器)。
- 在 .NET 2.0 框架目录中打开命令提示符。在大多数计算机上,这将是
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
- 根据 MSDN 说明,键入以下命令
aspnet_regiis.exe -pe "connectionStrings" -app "/connectionTest"
这使用默认的 RSAProtectedConfigurationProvider
来加密数据。这也是设置 Web 服务器群集时推荐使用的提供程序。完成此任务后,web.config 中的 <connectionStrings>
节将被重写,如下所示
<connectionStrings
configProtectionProvider="RsaProtectedConfigurationProvider">
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>Rsa Key</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>S9M1K/Dx9nX4nYJO6E7kz+ttLKFo+uvO+VsNwUFyQSEJnkEY
mjUUMr9JvjHoOaiFZv/fKz61BBP6kY6HcGDTxClXlCFGdWP5ElVlZ02
PfKrB2tIeQuFGieNGztBtiPHh7DxS4W2fnr0cyRoXi0WLT
6GHOfskptcV03SY3LrSVSk=</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>Wo48uaYxUROulI587vC4vgOk//6cJD
h70f3t8Izn53zQKJAilC/4HHHQREjLRFaClgj
SdjALGiCh/gb6ioAHdMYNP5NMaNARvtLGjLEbbm6
u09YSbvTFlSyucZJca/HZquib5FuT+slXUw+PijnlX
BHU/CtuMZ+90N32NxO5bSDjeN6yZQfa2H7wMXtb6uaBx
6pXl/v5OoF2pTiJjGMTMZwCm9+zRPMU6Y8ix+kntp
zHmb5SRKGDoQ==</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
现在,一切都已加密、受保护且安全。尝试执行您的页面以证明这一点。
- 解析器错误消息: 使用提供程序“
RsaProtectedConfigurationProvider
”解密失败。来自提供程序的错误消息:无法打开 RSA 密钥容器。
哎呀!看来 MSDN 文章漏掉了一个重要的步骤。应用程序用户需要被授予访问密钥容器的权限。什么权限?什么密钥容器?好吧,让我们查明。
解密 Web.Config
首先,让我们回到我们开始的地方。虽然在进行此加密之前备份 web.config 文件是一个好主意,但它是可逆的。打开命令提示符,像以前一样进入 .NET Framework 安装目录。发出以下命令以解密并将您的 web.config 恢复到其以前的状态
aspnet_regiis.exe -pd "connectionStrings" -app "/connectionTest"
-pd 是该实用程序的解密标志。您的配置文件应该已恢复到以前的状态。
为 Web 服务器场正确地重新加密您的 Web.Config
要使加密工作,密钥必须在加密和解密期间都可访问。我们之前的示例失败是因为它在解密期间不可用。事实证明,关于如何加密 connectionStrings
的简单示例充其量是误导性的。这是一个三步过程。
- 在 web.config 中添加一个
<configProtectedData>
节,以标识加密提供程序和您的密钥容器。 - 在服务器上创建您自己的密钥容器。
- 授予对此密钥容器的访问权限给所有相关帐户。
- 加密 web.config 的
<connectionStrings>
节。 - 导出密钥容器,以便在群集中的其他服务器上使用。
那么我们一步一步地来做吧。
步骤 1:修改 web.config 以标识密钥容器
编辑您的 web.config 文件以包含一个提供程序定义,该定义标识用于加密/解密的密钥容器。在 <configuration>
节下,添加以下内容
<configProtectedData >
<providers>
<add name="ConnectionTestProvider"
type="System.Configuration.RsaProtectedConfigurationProvider,
System.Configuration, Version=2.0.0.0,
Culture=neutral, processorArchitecture=MSIL"
keyContainerName="connectionTestKey"/>
</providers>
</configProtectedData>
您可能还需要修改配置标签以标识正确的 XML 命名空间,以避免 XML 验证错误。这对于编译和执行不是必需的,但是让开发环境告诉您所需的属性无效是很烦人的。修改 <configuration>
标签以添加 xmlns
属性,如下所示。
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
步骤 2:在服务器上创建密钥容器
在命令提示符下使用 aspnet_regiis
生成一个新的 RSA 加密密钥容器,名为 connectionTestKey
。
> aspnet_regiis -pc "connectionTestKey" -exp
这将在以下位置创建一个新的机器级别密钥容器
C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys
强烈建议您不要手动操作这些文件(删除、更改权限、重命名等)。我这样做时弄坏了我的 IIS 安装,不得不从头重新安装 IIS 以及 1.1 和 2.0 .NET Framework。因此,我的 VS2005 安装仍然不太对劲。
步骤 3:授予密钥容器的访问权限
> aspnet_regiis -pa "connectionTestKey" "ASPNET"
这会授予指定用户(在本例中为 ASPNET 内置帐户)对加密密钥文件的读取权限。请注意:您可能还需要授予 ASPNET 帐户对目录 C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys 的读取权限。我为此遇到了无数问题,这是安全模型的一个未记录的“特性”。
步骤 4:加密 web.config 文件的 <connectionStrings>
节
> aspnet_regiis -pe "connectionStrings" -app "/connectionTest"
这将加密并重写您的 web.config 文件。您现在(在此服务器上)可以正常使用了。如果您执行代码,您的“Hello World”应用程序应该可以正常工作。
步骤 5:导出密钥容器以供以后在群集服务器上使用
> aspnet_regiis -px "connectionTestKey" \temp\mykeyfile.xml
这会将密钥写入 XML 文件,以便在群集中的其他机器上使用。密钥文件的内容将如下所示
<RSAKeyValue>
<Modulus>4E/ykYJHOR/kkLuxeG9pV68eelZo9xkxhX
MgaUZN4LCqw8iCq8N7B1vYiackmagdvF4STN8OihNPSi1562/ZGNn/CDxkf
PkpzIax0sbtxl8fcF030qpRhUrZ6IfZPsFW/czNd1Xpm0bw+pu6YowBTsE
iLYGRe2IdKqgT1RTxBHE=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
步骤 6:在群集服务器上导入密钥容器
> aspnet_regiis -pi "connectionTestKey" mykeyfile.xml
部署应用程序时,您需要将密钥作为安装过程的一部分导入。将密钥文件放在服务器上可访问的位置,并运行上述命令导入密钥。您现在就可以开始了!
需要避免的陷阱
大多数问题,可预测地,都围绕着密钥容器访问权限的正确配置。确保您为应用程序运行的帐户添加了对密钥容器和包含它的文件夹的访问权限。
如果出现问题,不要随意删除您的密钥容器;请使用 aspnet_regiis 工具。我曾这样做(手动删除了容器)来移除一个我因为移除了所有人的权限而搞砸的密钥容器。在此过程中,我删除了一些其他重要的密钥容器。重新启动 IIS 后,我发现我无法重新启动 IIS。相反,我收到 IISADMIN 服务的以下错误
System error 8 - Not enough storage is available to process this command
这需要我卸载并重新安装 IIS 来解决问题。还有两个 .NET 框架也是如此。而且它仍然没有修复。我继续遇到以下错误
Safe handle has been closed
当我尝试进行除创建密钥之外的任何操作时。欢迎提供评论/线索。我只希望它不需要完整的操作系统安装来修复。我猜想 IIS 元数据库在处理加密密钥时也会被触及,但我仍在调查此事。
结论
总而言之,利用 web.config 中的新 <connectionStrings>
节以及新的 System.Configuration.ConfigurationManager.ConnectionStrings
集合,从配置文件中获取连接字符串。使用 aspnet_regiis 实用程序创建密钥容器,并通过添加 <configProtectedData>
节在 web.config 中标识正确的容器。使用 aspnet_regiis
实用程序加密,并导出您的密钥以供 Web 服务器群集中的其他服务器使用。
对不起,为什么这又如此复杂?虽然我赞扬微软认识到处理连接字符串时对安全性和一致性的需求,但这个多步骤、文档稀疏的过程让我有点怀疑他们的理智。在某些方面,我赞赏像我这样的人继续赚大钱的理由。在其他方面,我怀疑为什么没有为 VS2005 编写一个简单的附加组件或安装程序选项来处理 Web 服务器群集连接字符串。这几乎就像他们聘请了一些 UNIX 怪才或 Java 开发人员来完成这个加密实用工具工作。我闻到了某个大胆开发人员的机会。
另外,为什么 aspnet_regiis 被挪用用于这么多非 ASP.NET 框架注册与 IIS 的功能?aspnet_regiis 没有成为一个安全密钥配置工具,反而成为了这个功能的垃圾场。在一个设计令人印象深刻的框架和 SDK 中,这是一个考虑不周的实用工具决策(恕我直言)。