DPAPI 和 Triple DES:加密连接字符串和其他应用程序设置的强大组合






4.77/5 (46投票s)
2005年8月26日
6分钟阅读

140087

1691
本文展示了如何使用 DPAPI 和 Triple DES 来加密连接字符串和其他敏感字符串,以便存储在 ASP.NET 的 web.config 文件中。
引言
我认为,所有 ASP.NET 开发者在某个时候都会面临如何保护存储在 web.config 文件中的敏感数据的问题。我记得几年前,在我第一次 ASP.NET 面试中,我被问到:“你会如何保护包含密码的连接字符串在 .config 文件中?” 我可能有点天真地回答说,你可以直接将连接字符串放在那里,不加密,因为 .config 文件反正也不会被 IIS 提供。
尽管以上说法或多或少是正确的,但一旦有安全意识的人开始仔细审查你的代码,你很快就会意识到这种策略根本行不通。在本文中,我将努力从我“可耻”的陈述中挽回颜面,并提供一个易于实现的解决方案,该方案尽可能接近微软的最佳实践。
一个常见的解决方案
解决这个问题的简单方法是使用 .NET 来加密存储在 .config 文件中的连接字符串,使用像 Triple DES 或类似的算法。应用程序将读取 .config 文件中的加密值,然后再次解密以检索明文。
这个解决方案效果很好,在这篇文章中我基本上做的也是同样的事情。这种方法唯一的问题是,你最终总是会遇到密钥管理问题。你的解密密钥到底存储在哪里才能保证安全?在这篇文章中,我将展示如何使用 Microsoft Data Protection API 来相对安全地保管密钥。
冒充身份
通常,我们希望保护 .config 文件中的用户名和密码组合,以便在尝试访问 SQL Server 或需要身份验证的 Web 服务时,用该组合来模拟一个具有适当权限的用户。在可以使用集成 Windows 身份验证访问受保护资源的情况下,可以实现以下优雅的解决方案:
在 web.config 文件的 <system.web>
部分添加以下行:
<identity impersonate="true" />
这一行告诉 ASP.NET 模拟访问网站的用户。如果你为应用程序关闭了匿名访问,那么这将是用户访问网站时提供的凭据。如果你知道所有用户都将使用集成 Windows 身份验证来访问网站,并且用户的凭据将有权访问应用程序将要访问的所有 SQL Server 和 Web 服务,那么这就足够了。
然而,一个更好的解决方案可能是使用一个专门的用户来代表 ASP.NET 应用程序访问受保护的资源。在这种情况下,只需允许网站匿名访问,并将匿名用户的用户帐户更改为一个具有适当访问权限的帐户,以访问受保护的资源。
Aspnet_setreg
微软认识到我们不喜欢在 web.config 文件中存储未加密的凭据,并提供了 aspnet_setreg 工具来加密并安全地存储模拟身份以及会话状态的连接字符串在注册表中。
此实现使用 DPAPI 来加密和解密需要保护的设置。请注意,此实现将 CRYPTPROTECT_LOCAL_MACHINE
标志传递给 CryptProtectData
和 CryptUnprotectData
函数,这意味着使用的是本地计算机存储。因此,您应该意识到这不是最安全的解决方案,因为任何拥有本地计算机访问权限的人都有可能解密这些值。然而,使此实现安全的是应用于注册表项的强离散访问控制列表 (DACL)。
使用此工具非常简单,首先从 这里 下载它,并将其提取到您方便的位置。解压后运行应用程序,并为其提供注册表位置以及您希望应用程序模拟的用户名和密码。
aspnet_setreg.exe -k:SOFTWARE\MY_SECURE_APP\identity
-u:"yourdomainname\username" -p:"password"
执行此命令后,您将收到类似以下的输出,其中将指导您如何使用密钥并保护它。
Please edit your configuration to contain the following:
userName=
"registry:HKLM\SOFTWARE\MY_SECURE_APP\identity\ASPNET_SETREG,
userName"
password=
"registry:HKLM\SOFTWARE\MY_SECURE_APP\identity\ASPNET_SETREG,
password"
The DACL on the registry key grants Full Control to System,
Administrators, and Creator Owner.
If you have encrypted credentials for the <IDENTITY />
configuration section, or a connection string for the <sessionState/>
configuration section, ensure that the process identity has
Read access to the registry key. Furthermore, if you have
configured IIS to access content on a UNC share, the account used
to access the share will need Read access to the registry key.
Regedt32.exe may be used to view/modify registry key permissions.
You may rename the registry subkey and registry value in order to
prevent discovery.
下一步必须是为注册表项设置 DACL,以允许 ASP.NET 读取它。
最后,您可以将模拟行添加到您的 web.config 中,该行使用您在上一步中收到的值。还要确保您所有的连接字符串都使用集成 Windows 安全性。
<identity impersonate="true"
userName=
"registry:HKLM\SOFTWARE\MY_SECURE_APP\identity\ASPNET_SETREG,
userName"
password=
"registry:HKLM\SOFTWARE\MY_SECURE_APP\identity\ASPNET_SETREG,
password" />
目前,这种方法似乎是微软推荐的最佳实践,但不幸的是,这种安全机制在允许您安全存储模拟凭据和 Windows 集成安全性方面非常有限。有时需要一种更通用的方法,允许您在 web.config 文件中存储任何敏感的加密数据。
我提出的解决方案是结合前面讨论的方法和加密字符串的方法。我的方法假设凭据在注册表中存储的方式并通过 DACL 进行保护,这确实是微软提供给我们的最安全保护。
DPAPI 和 Triple DES
我解决方案背后的想法非常简单:使用 DPAPI 和 DACL,与 aspnet_setreg.exe 的方式完全相同,将一个主密钥存储在注册表中,该主密钥将用于加密和解密敏感数据。然后,这个安全的 OS 主密钥与 Triple DES 算法一起,用于加密和解密 .config 中的值。
第一步是在注册表中存储主密钥。只需运行 Windows 应用程序可执行文件 Foulds.Security.UserInterface.DataProtection.exe,并使用它来配置注册表项位置并将其存储在注册表中。点击 Save
按钮后,点击 Load
按钮以确保密钥可以从注册表中加载。
主密钥存储在数据库中后,您可以加密需要在 web.config 文件中存储的值。在明文框中输入值并点击加密,然后可以将 Cipher Text 框中的结果复制到注册表中。
与 aspnet_setreg 工具一样,您必须为创建的注册表项配置 DACL,以允许 ASP.NET 读取它。
已经创建了一个特殊的节处理器,以简化敏感数据的加密和解密。要启用此节处理器,请将 Foulds.Security.dll
程序集添加到您的 Web 项目中。
接下来,将节处理器添加到您的 web.config 文件中:
<configSections>
<sectionGroup name="fouldsSettings">
<section name="encrypted"
type=
"Foulds.Security.SectionHandlers.DataProtection.SectionHandler,
Foulds.Security"/>
</sectionGroup>
</configSections>
然后,您必须将配置节添加到您的 web.config 文件中,该节将指定注册表中主密钥的位置以及应用程序的加密密钥值对。下面是一个配置节的示例:
<fouldsSettings>
<encrypted registryKey="SOFTWARE\HannesFoulds\encryption,
masterkey">
<add key="secret1" value="aVvjoiS42p8=" />
<add key="secret2" value="mWMSMkNFLSM=" />
<add key="secret3" value="xwc3jnQ9Zfw=" />
</encrypted>
</fouldsSettings>
当您想从此配置节中获取未加密的值时,可以使用 Foulds.Security
程序集中提供的帮助类,如下所示:
[编辑提示:换行以避免滚动]
string secret2 =
Foulds.Security.SectionHandlers.DataProtection.
ConfigSettings.Encrypted["secret2"];
结论
这个解决方案是我目前想到的保护 web.config 中敏感数据的最佳方法。请随时审查我的代码并帮助我改进它。如果您希望我进一步充实本文的技术细节,请告诉我,现在是星期五下午了,我很快就要回家了 ;-)