在配置系统中安全地存储数据是一个难以解决的问题。早年,当我在 ASP.NET 团队的时候,这一特定功能(安全地存储连接字符串)好像不会得到实现了。它被无数个问题(如密钥存储)团团包围,阻碍了解决之路。幸运的是,这个问题不但最终得到了解决,而且还融入到 ASP.NET 2.0 强大的最新 API 组合中,从而使您得以通过编程方式管理 ASP.NET 配置文件。
不过,在深入探究 ASP.NET 2.0 之前,让我们先研究一下这个问题,了解一下 ASP.NET 1.x 中的各种解决方案。只要您用过 ASP.NET,那么,您无疑会理解在 web.config 文件中存储共享设置的好处。例如,您不必在每次创建新数据库连接时都指定连接字符串,而是可以将该字符串存储在 ASP.NET 配置文件的 <appSettings /> 节中。这样就可以通过 ConfigurationSettings.AppSettings 属性访问连接字符串。下面是一个 <appSettings/> 节的示例:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="ConnectionString"
value="server=.;database=demo;uid=db;pwd=*u%a" />
</appSettings>
</configuration>
这样,每当您需要更改连接字符串时,只需打开该文件,执行更改,即告完成。
从传统 ASP 转向 ASP.NET 的许多开发人员都对此功能深有体会,因为大多数全局值都是以应用程序变量的形式存储的。实际上,对于 ASP.NET 1.x,在 <appSettings/> 中存储连接字符串是一种推荐的做法。还有一点也值得注意,就是您也可以在 <appSettings/> 中存储其他常用数据,包括 LDAP 路径、常用应用程序设置以及应用程序所需要的其他数据。<appSettings/> 的目标是简化自定义配置节处理程序的编写,这是一项与 ASP.NET 配置系统交互的更高级的技术。自定义配置节处理程序允许您在配置系统内创作和处理自己的 XML 节。
您也许已经注意到,在 <appSettings/> 中存储的内容没有加密,而是以纯文本的形式存储。<sessionState/> 节也是如此,该节支持在进程外存储 Session 数据。一种备选的存储方式是使用 SQL Server?,并在 <sessionState/> 配置位置中以纯文本形式存储凭据。
等一等,您不能这样做
在 <appSettings/> 中存储连接字符串的缺点是不能保证安全性,因为该文件没有经过加密,也没有经过编译。并不是说编译配置信息就会对增强安全性有所帮助;DVD 制造商使用基于密钥的加密方法来保护知识产权,方法是简单地让 DVD 播放器软件供应商将解密密钥存储在他们的编译代码中。某些黑客编写的一小段代码会轻而易举地找到解密密钥。我们常常会在个人网络日志中看到这样的帖子,有人想弄清某种东西在 Microsoft? .NET Framework 中的工作机理,这时就会有人建议:“试试 Reflector。”
ASP.NET 1.1 — 更好地保守秘密
在 ASP.NET 1.0 中,如果没有额外的自定义代码,则无法在配置文件中安全地存储连接字符串。ASP.NET 团队在 ASP.NET 1.1 中解决了这个问题,方法是对几个配置条目启用加密。该解决方案是通过 Windows 数据保护 API (DPAPI) 完成的,从而能够加密下列配置条目:
<identity/> 用来存储 ASP.NET 辅助进程的 Windows? 标识以供模拟之用。
<processModel/> 用来控制在其下执行 ASP.NET 辅助进程的 Windows 帐户。IIS 6.0 中不使用(请参阅以下注释)。
<sessionState/> 具体包含 stateConnectionString 和 sqlConnectionString 属性,用于控制 ASP.NET 对进程外状态服务器的验证方式。
需要注意的很重要的一点是,IIS 6.0(Windows Server? 2003 附带的 Web 服务器)提供了它自己的辅助进程管理子系统,而 ASP.NET 会顺从该系统。因此,ASP.NET 在由 IIS 6.0 托管时,配置系统中的有些辅助进程设置将不被使用。
随 ASP.NET 1.1 提供了名为 aspnet_setreg.exe 的工具,用以加密配置文件中的数据并将解密密钥存储在 Windows 注册表项中。得到的注册表项具有一个访问控制列表 (ACL),该列表配置为限制有权访问该密钥的 Windows 帐户。“知识库”文章 Q329290“如何使用 ASP.NET 实用工具加密凭据和会话状态连接字符串”中详细介绍了这项技术。
不过,这个解决方案也有一些缺点。它破坏了 ASP.NET 团队所钟爱的 xcopy 部署,该功能允许在无需访问服务器的情况下部署 ASP.NET 应用程序。运用上文提到的技术,开发人员或系统管理员必须具有本地计算机的访问权限,才能运行命令行工具,进而加密配置数据并将密钥存储在注册表中。
ASP.NET 2.0 秘密s
接下来我们可以开始讨论该团队为s在 ASP.NET 2.0 中解决这一问题而做的所有工作了。这次又有一个用于管理配置数据加密的命令行工具:aspnet_regiis.exe。Aspnet_regiis.exe 存在于 ASP.NET 的早期版本中,主要用于手动向 IIS 注册 ASP.NET。例如,使用它向 IIS 添加 aspnet_isapi.dll 以及配置 ASP.NET 应用程序使用的脚本目录。您可在 \Windows\Microsoft.NET\Framework\版本号\ 目录中找到此工具。
使用 aspnet_regiis.exe 加密配置节的过程同它所生成的结果一样扑朔迷离!请看图 1 中 aspnet_regiis.exe /help 的执行结果,您就明白我的意思了。
正如您所看到的那样,除非您非常熟悉安全术语,否则您很快就会被大量的选项和各种设置弄的眼花缭乱。不幸的是,虽然功能强大,但各种工具十分混乱。现在我们暂不研究 aspnet_regiis.exe 的使用方法,先来看一个 ASP.NET 示例页面,该页面使用新的配置 API 来实现配置节的加密。此页面,即 ConnectionEncryption.aspx(可以从 MSDN?Magazine 网站上获得),包含一个 GridView,用所有配置节的列表填充。如图 2 所示。
在研究 ConnectionEncryption.aspx 内部的工作方式之前,先看看这个页面的结果。但是先提出一个警告:使用本工具要求托管 ASP.NET 的进程对当前应用程序的 web.config 文件具有写入权限。默认情况下,IIS 中运行的 ASP.NET 应用程序没有必要的权限。不过,托管在 ASP.NET Development Web Server 中的应用程序使用已登录用户的权限集运行。您在此处看到的该工具的所有用法都是在 ASP.NET Development Web Server 中显示的。建议您在全面了解更改 IIS 权限设置所带来的影响的情况下再执行更改。
下面是 web.config 中用于存储连接字符串的全新 <connectionStrings> 节的示例条目。<connectionStrings> 节与 <appSettings> 节几乎相同,目前建议在前者中存储连接字符串数据,因为有新的 API 可以专门处理散布在 ASP.NET 中的连接字符串:
<connectionStrings>
<add name="Northwind" providerName="System.Data.SqlClient"
connectionString="Server=localhost;Integrated
Security=True;Database=Northwind" />
</connectionStrings>
请注意,在这种情况下,仍然使用 Windows 验证连接到数据库。
单击 ConnectionEncryption.aspx 中的 Encrypt(加密)链接,将 web.config 中连接字符串的值更改为如图3 中所示的内容。加密后,ConnectionEncryption.aspx 页面会将该条目的状态报告为已加密(链接会更改为“Decrypt”(解密),正如您在图 4 中所见)。
既然页面已经生效,让我们来看一下代码。请看 ConnectionEncryption.aspx.cs 中的第 18 行,在 Page_Load 中,当新的 ASP.NET 2.0 WebConfigurationManager 类检索到本地路径的 Configuration 类的实例时,即填充 GridView:
Configuration config =
WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
使用从配置变量中检索到的数据填充数组列表,然后将其绑定到 GridView。源中的大多数其他方法(请参阅下载的代码)用于与实际数据绑定操作有关的业务逻辑规则,如确定节的范围和状态(加密或取消加密)。当单击 Encrypt(加密)或 Decrypt(解密)链接时,GridView1_RowCommand 事件中便会发生神奇的事情。当 GridViewCommandEvent.CommandName 的值为“Encrypt”(加密)时,执行以下代码:
section.SectionInformation.ProtectSection(
"DataProtectionConfigurationProvider");
config.Save();
当 GridViewCommandEvent.CommandName 的值为“Decrypt”(解密)时,则执行以下代码:
section.SectionInformation.UnprotectSection();
config.Save();
这样,就将加密和解密数据的实际工作转交给了提供程序。一个内置提供程序是 DataProtectionConfigurationProvider。它使用内置的 DPAPI 来存储安全数据,这与 ASP.NET 命令行工具所使用的相同。
您应该注意,加密数据时,可以指定提供程序,但解密时,却不需要指定提供程序(如果为 ProtectSection 提供的是 null 或空字符串,则将使用在配置文件的 configProtectedData 节中指定的默认提供程序)。这是因为 API 将另一条目写入了配置文件中,该条目用于指定所使用的加密保护提供程序:
<connectionStrings configProtectionProvider=
"DataProtectionConfigurationProvider">
<EncryptedData>...</EncryptedData>
</connectionStrings>
此配置条目不仅供 API 用以确定如何解密各个节,而且在 ASP.NET 需要将值(如连接字符串)读入内存但必须先加以解密时,供 ASP.NET 在内部使用。
ASP.NET 2.0 的新增加密功能的一大优点是,您不但可以加密许多内置配置节,还可以编写自定义的加密提供程序!提供程序是 ASP.NET 2.0 中一个令人惊叹的新扩展模型,它使得开发人员能够实现他们自己的核心功能,如成员身份、个性化等等。此外,鉴于“配置加密”的实现方式,自定义配置节也可以轻松得到加密,因而,对数据的保护不会象 ASP.NET 1.1 中那样,仅仅局限于少数配置节。
说点更高明的秘密
关于加密机密数据的专栏如果不提出几条警告,就不算是完整的专栏。首先,如果能避免保密,就尽量避免保密。如果您使用的是 SQL Server,而且不想在配置系统中存储敏感的连接字符串信息,则使用 SQL Server 附带的集成验证。Windows 验证用于应用服务器与数据库之间的连接。利用此技术,与 SQL Server 之间的连接会直接通过 Windows 进行验证和授权。使用 Windows 验证时,SQL Server 会向验证服务器(无论本地还是远程的)请求令牌,该令牌包含用户的安全标识符 (SID) 及其他信息,然后将这些信息与 SQL Server 所维护的列表进行比较,确定是准许还是拒绝访问。配置文件中不存储密码或用户名。
另外一件需要切记的事情是,仅仅安全存储了配置文件中的数据并非意味着您可以逃开各种各样的攻击。任何称职(安全双关语)的攻击者,只要他获得了访问您的服务器上的 web.config 文件的足够权限,都可能会对您造成巨大危害。获得系统访问权限、能够处理 web.config 的攻击者可以执行其他操作来操纵数据库,而无需知道连接字符串是什么。因此,安全存储您的连接字符串或其他应用程序数据只是防线之一。另外一项有效防御措施是,使用存储过程并且对那些有权访问您的数据库的帐户设置更加严密的控制。例如,如果存在 SQL 注入漏洞,加密的连接字符串将毫无价值(有关 SQL 注入攻击的详细信息,请参阅 msdn.microsoft.com/msdnmag/issues/04/09/SQLInjection)。
我曾构建并运行过很多高访问量的网站(如 http://www.asp.net/),因此我受到攻击的机率并不比常人更低。一个常见的特征是攻击常常与最初的漏洞毫无关系。攻击者不会通过最初漏洞攻击,而是会努力保护通过最初的漏洞而制造的后门。
结束语
虽然 ASP.NET 的任一版本都可以保护 ASP.NET 应用程序中的机密数据,但这在 ASP.NET 1.1 中较容易实现,而在 ASP.NET 2.0 中更是轻而易举。使用 ASP.NET 2.0,配置加密不再是亡羊补牢,它已经内置于新的配置 API 之中。使用 ASP.NET 2.0,您不但可以使用 aspnet_regiis.exe 工具来加密配置节,还可以编写您自己的自定义代码(以及自定义提供程序,如果需要)来加密和解密配置数据。
文章来源于领测软件测试网 https://www.ltesting.net/