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