保护连接字符串和其他配置信息 (C#)
作者 :斯科特·米切尔
ASP.NET 应用程序通常将配置信息存储在 Web.config 文件中。 其中一些信息很敏感,需要保护。 默认情况下,此文件不会提供给网站访问者,但管理员或黑客可能会访问 Web 服务器的文件系统并查看文件的内容。 在本教程中,我们了解到,ASP.NET 2.0 允许通过加密 Web.config 文件的节来保护敏感信息。
简介
ASP.NET 应用程序的配置信息通常存储在名为 Web.config
的 XML 文件中。 在这些教程的过程中,我们更新 Web.config
了几次。 在第一个教程中创建Northwind
类型化数据集时,连接字符串信息会自动添加到Web.config
该<connectionStrings>
部分中。 稍后,在 母版页和网站导航 教程中,我们手动更新 Web.config
,并添加一个 <pages>
元素,指示项目中的所有 ASP.NET 页都应使用 DataWebControls
主题。
由于Web.config
可能包含敏感数据(如连接字符串),Web.config
因此请务必将内容安全且隐藏在未经授权的查看者中。 默认情况下,对扩展名为 .config
文件的任何 HTTP 请求都由 ASP.NET 引擎处理,该引擎返回 此类型的页面未送达 图 1 中显示的消息。 这意味着访问者只需输入http://www.YourServer.com/Web.config浏览器的地址栏即可查看文件Web.config
内容。
图 1:通过浏览器访问 Web.config
返回此类型的页面未提供消息(单击以查看全尺寸图像)
但是,如果攻击者能够找到允许她查看 Web.config
文件内容的一些其他攻击,该怎么办? 攻击者可以使用此信息做什么,以及可以采取哪些步骤进一步保护其中的 Web.config
敏感信息? 幸运的是,大多数 Web.config
部分不包含敏感信息。 如果攻击者知道 ASP.NET 页面使用的默认主题的名称,攻击者会受到什么伤害?
但是,某些Web.config
部分包含可能包含连接字符串、用户名、密码、服务器名称、加密密钥等敏感信息。 此信息通常在以下 Web.config
部分中找到:
<appSettings>
<connectionStrings>
<identity>
<sessionState>
在本教程中,我们将介绍保护此类敏感配置信息的技术。 正如我们所看到的,.NET Framework 版本 2.0 包括一个受保护的配置系统,使以编程方式加密和解密所选配置部分是微风。
注意
本教程总结了Microsoft从 ASP.NET 应用程序连接到数据库的建议。 除了加密连接字符串之外,还可以通过确保以安全方式连接到数据库来帮助强化系统。
步骤 1:探索 ASP.NET 2.0 s 受保护的配置选项
ASP.NET 2.0 包括用于加密和解密配置信息的受保护配置系统。 这包括 .NET Framework 中可用于以编程方式加密或解密配置信息的方法。 受保护的配置系统使用提供程序模型,使开发人员可以选择使用哪种加密实现。
.NET Framework 附带两个受保护的配置提供程序:
RSAProtectedConfigurationProvider
- 使用非对称 RSA 算法 进行加密和解密。DPAPIProtectedConfigurationProvider
- 使用 Windows 数据保护 API (DPAPI) 进行加密和解密。
由于受保护的配置系统实现了提供程序设计模式,因此可以创建自己的受保护的配置提供程序并将其插入应用程序。 有关此过程的详细信息,请参阅 “实现受保护的配置提供程序 ”。
RSA 和 DPAPI 提供程序使用密钥进行加密和解密例程,这些密钥可以存储在计算机或用户级别。 计算机级密钥非常适合 Web 应用程序在其自己的专用服务器上运行的情况,或者如果服务器上有多个应用程序需要共享加密信息。 用户级密钥是共享托管环境中的更安全选项,在同一服务器上其他应用程序不应解密应用程序的受保护配置部分。
在本教程中,我们的示例将使用 DPAPI 提供程序和计算机级密钥。 具体而言,我们将介绍加密 <connectionStrings>
部分 Web.config
,尽管受保护的配置系统可用于加密大多数部分 Web.config
。 有关使用用户级密钥或使用 RSA 提供程序的信息,请参阅本教程末尾的“进一步阅读”部分中的资源。
注意
提供程序RSAProtectedConfigurationProvider
和DPAPIProtectedConfigurationProvider
提供程序分别在machine.config
文件中注册了提供程序名称和RsaProtectedConfigurationProvider
DataProtectionConfigurationProvider
提供程序。 加密或解密配置信息时,我们需要提供适当的提供程序名称(RsaProtectedConfigurationProvider
或 DataProtectionConfigurationProvider
),而不是实际的类型名称(RSAProtectedConfigurationProvider
和 DPAPIProtectedConfigurationProvider
)。 可以在文件夹中找到 machine.config
该文件 $WINDOWS$\Microsoft.NET\Framework\version\CONFIG
。
步骤 2:以编程方式加密和解密配置部分
通过几行代码,可以使用指定的提供程序加密或解密特定配置节。 正如我们很快就会看到的代码,只需以编程方式引用相应的配置部分,调用其 ProtectSection
或 UnprotectSection
方法,然后调用 Save
该方法来保留更改。 此外,.NET Framework 还包含一个有用的命令行实用工具,可用于加密和解密配置信息。 我们将在步骤 3 中探索此命令行实用工具。
为了以编程方式说明如何保护配置信息,让我们创建一个 ASP.NET 页,其中包含用于加密和解密部分中Web.config
的<connectionStrings>
按钮。
首先打开 EncryptingConfigSections.aspx
文件夹中的页面 AdvancedDAL
。 将 TextBox 控件从工具箱拖到设计器上,将其ID
属性MultiLine
设置为WebConfigContents
,将Width
属性TextMode
分别设置为 95% 和 Rows
15。 此 TextBox 控件将显示 Web.config
允许我们快速查看内容是否已加密的内容。 当然,在实际应用程序中,你永远不想显示其内容 Web.config
。
在 TextBox 下面,添加两个名为 EncryptConnStrings
和 DecryptConnStrings
. 将其文本属性设置为“加密连接字符串”和“解密连接字符串”。
此时,屏幕应类似于图 2。
图 2:向页面添加一个 TextBox 和两个按钮 Web 控件(单击以查看全尺寸图像)
接下来,我们需要编写在首次加载页面时加载和显示 TextBox 中WebConfigContents
内容Web.config
的代码。 将以下代码添加到页面代码隐藏类。 此代码添加一个命名DisplayWebConfig
的方法,并在以下false
情况下Page.IsPostBack
从Page_Load
事件处理程序调用该方法:
protected void Page_Load(object sender, EventArgs e)
{
// On the first page visit, call DisplayWebConfig method
if (!Page.IsPostBack)
DisplayWebConfig();
}
private void DisplayWebConfig()
{
// Reads in the contents of Web.config and displays them in the TextBox
StreamReader webConfigStream =
File.OpenText(Path.Combine(Request.PhysicalApplicationPath, "Web.config"));
string configContents = webConfigStream.ReadToEnd();
webConfigStream.Close();
WebConfigContents.Text = configContents;
}
该方法DisplayWebConfig
使用File
类打开应用程序Web.config
文件、StreamReader
将类内容读取到字符串中,以及Path
生成文件的物理路径的Web.config
类。 这三个类都位于命名空间中System.IO
。 因此,需要将语句添加到using
System.IO
代码隐藏类的顶部,或者用这些类名System.IO.
作为前缀。
接下来,我们需要为两个 Button 控件 Click
事件添加事件处理程序,并添加必要的代码,以便通过 DPAPI 提供程序使用计算机级密钥加密和解密 <connectionStrings>
节。 在设计器中,双击每个 Buttons 以在代码隐藏类中添加 Click
事件处理程序,然后添加以下代码:
protected void EncryptConnStrings_Click(object sender, EventArgs e)
{
// Get configuration information about Web.config
Configuration config =
WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
// Let's work with the <connectionStrings> section
ConfigurationSection connectionStrings = config.GetSection("connectionStrings");
if (connectionStrings != null)
// Only encrypt the section if it is not already protected
if (!connectionStrings.SectionInformation.IsProtected)
{
// Encrypt the <connectionStrings> section using the
// DataProtectionConfigurationProvider provider
connectionStrings.SectionInformation.ProtectSection(
"DataProtectionConfigurationProvider");
config.Save();
// Refresh the Web.config display
DisplayWebConfig();
}
}
protected void DecryptConnStrings_Click(object sender, EventArgs e)
{
// Get configuration information about Web.config
Configuration config =
WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
// Let's work with the <connectionStrings> section
ConfigurationSection connectionStrings =
config.GetSection("connectionStrings");
if (connectionStrings != null)
// Only decrypt the section if it is protected
if (connectionStrings.SectionInformation.IsProtected)
{
// Decrypt the <connectionStrings> section
connectionStrings.SectionInformation.UnprotectSection();
config.Save();
// Refresh the Web.config display
DisplayWebConfig();
}
}
两个事件处理程序中使用的代码几乎完全相同。 它们首先通过WebConfigurationManager
类方法OpenWebConfiguration
Web.config
获取有关当前应用程序文件的信息。 此方法返回指定虚拟路径的 Web 配置文件。 接下来,Web.config
通过Configuration
返回对象的类方法ConfigurationSection
访问文件<connectionStrings>
部分。GetSection(sectionName)
该 ConfigurationSection
对象包含一个 SectionInformation
属性,该属性 提供有关配置部分的其他信息和功能。 如上面的代码所示,我们可以通过检查 SectionInformation
属性的属性 IsProtected
来确定配置节是否已加密。 此外,可以通过属性和ProtectSection(provider)
UnprotectSection
方法对节进行加密或解密SectionInformation
。
该方法 ProtectSection(provider)
接受为输入字符串,指定加密时要使用的受保护配置提供程序的名称。 在 Button 事件处理程序中 EncryptConnString
,我们将 DataProtectionConfigurationProvider 传递到方法, ProtectSection(provider)
以便使用 DPAPI 提供程序。 该方法 UnprotectSection
可以确定用于加密配置节的提供程序,因此不需要任何输入参数。
调用 ProtectSection(provider)
或 UnprotectSection
方法后,必须调用 Configuration
对象 Save
方法 来保留更改。 加密或解密配置信息并保存更改后,我们调用 DisplayWebConfig
将更新 Web.config
的内容加载到 TextBox 控件中。
输入上述代码后,通过浏览器访问 EncryptingConfigSections.aspx
页面对其进行测试。 最初应看到一个页面,其中列出了以纯文本形式显示的节的内容Web.config
<connectionStrings>
(请参阅图 3)。
图 3:向页面添加一个 TextBox 和两个按钮 Web 控件(单击以查看全尺寸图像)
现在,单击“加密连接字符串”按钮。 如果启用了请求验证,则从 WebConfigContents
TextBox 回发的标记将生成一个显示消息的消息,从客户端检测到一个 HttpRequestValidationException
潜在的危险 Request.Form
值。 请求验证在 ASP.NET 2.0 中默认启用,禁止包含未编码 HTML 的回发,旨在帮助防止脚本注入攻击。 可以在页面或应用程序级别禁用此检查。 若要关闭此页面,请将 ValidateRequest
设置设置为 false
指令中 @Page
。 该 @Page
指令位于页面声明性标记的顶部。
<%@ Page ValidateRequest="False" ... %>
若要详细了解请求验证及其用途、如何在页面和应用程序级别禁用它,以及如何对 HTML 编码标记进行禁用,请参阅 请求验证 - 防止脚本攻击。
禁用页面请求验证后,请尝试再次单击“加密连接字符串”按钮。 在回发时,将访问配置文件及其部分,并使用 <connectionStrings>
DPAPI 提供程序进行加密。 然后更新 TextBox 以显示新 Web.config
内容。 如图 4 所示, <connectionStrings>
信息现已加密。
图 4:单击“加密连接字符串”按钮加密 <connectionString>
分区(单击以查看全尺寸图像)
在我的计算机上生成的加密 <connectionStrings>
部分遵循,尽管元素中的 <CipherData>
某些内容已被删除,但为简洁起见:
<connectionStrings
configProtectionProvider="DataProtectionConfigurationProvider">
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/...zChw==</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
注意
该 <connectionStrings>
元素指定用于执行加密的提供程序(DataProtectionConfigurationProvider
)。 单击“解密连接字符串”按钮时,此方法将使用 UnprotectSection
此信息。
从我们编写的代码、SqlDataSource 控件或 Typed DataSets 中的 TableAdapters 自动生成的代码访问连接字符串信息Web.config
时,它会自动解密。 简言之,我们不需要添加任何额外的代码或逻辑来解密加密 <connectionString>
部分。 若要演示这一点,请访问当前前面的教程之一,例如“基本报告”部分中的“简单显示”教程(~/BasicReporting/SimpleDisplay.aspx
)。 如图 5 所示,本教程的工作方式与预期完全相同,指示加密连接字符串信息将由 ASP.NET 页面自动解密。
图 5:数据访问层自动解密连接字符串信息(单击以查看全尺寸图像)
若要将分区还原 <connectionStrings>
回其纯文本表示形式,请单击“解密连接字符串”按钮。 在回发时,应会看到纯文本中的Web.config
连接字符串。 此时,你的屏幕在首次访问此页面时应如下所示(如图 3 所示)。
步骤 3:使用 aspnet_regiis.exe 加密配置节
.NET Framework 包含文件夹中的各种命令行工具 $WINDOWS$\Microsoft.NET\Framework\version\
。 例如,在“使用 SQL 缓存依赖项”教程中,我们查看了如何使用aspnet_regsql.exe
命令行工具添加 SQL 缓存依赖项所需的基础结构。 此文件夹中的另一个有用的命令行工具是 ASP.NET IIS 注册工具 (aspnet_regiis.exe
) 。 顾名思义,ASP.NET IIS 注册工具主要用于向Microsoft专业级 Web 服务器 IIS 注册 ASP.NET 2.0 应用程序。 除了与 IIS 相关的功能外,ASP.NET IIS 注册工具还可用于加密或解密指定的 Web.config
配置部分。
以下语句显示了用于使用命令行工具加密配置节的 aspnet_regiis.exe
常规语法:
aspnet_regiis.exe -pef section physical_directory -prov provider
节是加密的配置部分(如 connectionStrings),physical_directory是 Web 应用程序根目录的完整物理路径,提供程序是要使用的受保护配置提供程序的名称(如 DataProtectionConfigurationProvider)。 或者,如果 Web 应用程序在 IIS 中注册,则可以使用以下语法输入虚拟路径,而不是物理路径:
aspnet_regiis.exe -pe section -app virtual_directory -prov provider
以下示例 aspnet_regiis.exe
使用 DPAPI 提供程序和计算机级密钥加密 <connectionStrings>
该部分:
aspnet_regiis.exe -pef
"connectionStrings" "C:\Websites\ASPNET_Data_Tutorial_73_CS"
-prov "DataProtectionConfigurationProvider"
同样, aspnet_regiis.exe
命令行工具可用于解密配置部分。 而不是使用 -pef
开关,请使用 -pdf
(或而不是 -pe
使用 -pd
)。 此外,请注意,解密时不需要提供程序名称。
aspnet_regiis.exe -pdf section physical_directory
-- or --
aspnet_regiis.exe -pd section -app virtual_directory
注意
由于我们使用特定于计算机的密钥的 DPAPI 提供程序,因此必须从提供网页的同一台计算机运行 aspnet_regiis.exe
。 例如,如果从本地开发计算机运行此命令行程序,然后将加密的 Web.config 文件上传到生产服务器,则生产服务器将无法解密连接字符串信息,因为它使用特定于开发计算机的密钥进行加密。 RSA 提供程序没有此限制,因为可以将 RSA 密钥导出到另一台计算机。
了解数据库身份验证选项
在任何应用程序可以向Microsoft SQL Server 数据库发出SELECT
、INSERT
UPDATE
或DELETE
查询之前,数据库必须先标识请求者。 此过程称为 身份验证 ,SQL Server 提供两种身份验证方法:
- Windows 身份验证 - 运行应用程序的进程用于与数据库通信。 通过 Visual Studio 2005 s ASP.NET Development Server 运行 ASP.NET 应用程序时,ASP.NET 应用程序假定当前登录用户的标识。 对于Microsoft Internet Information Server(IIS)上的 ASP.NET 应用程序,ASP.NET 应用程序通常假定其标识
domainName``\MachineName
domainName``\NETWORK SERVICE
,也可以自定义。 - SQL 身份验证 - 用户 ID 和密码值作为身份验证凭据提供。 使用 SQL 身份验证时,连接字符串中提供了用户 ID 和密码。
Windows 身份验证优先于 SQL 身份验证,因为它更安全。 使用Windows 身份验证连接字符串可从用户名和密码中释放,如果 Web 服务器和数据库服务器位于两个不同的计算机上,则不会以纯文本形式通过网络发送凭据。 但是,使用 SQL 身份验证时,身份验证凭据在连接字符串中硬编码,并且以纯文本形式从 Web 服务器传输到数据库服务器。
这些教程使用了Windows 身份验证。 可以通过检查连接字符串来判断正在使用哪种身份验证模式。 教程中的Web.config
连接字符串是:
Data Source=.\SQLEXPRESS; AttachDbFilename=|DataDirectory|\NORTHWND.MDF; Integrated Security=True; User Instance=True
集成安全性=True 且缺少用户名和密码表示正在使用Windows 身份验证。 在某些连接字符串使用术语“受信任的连接=是”或“集成安全性=SSPI”,而不是“集成安全性=True”,但这三个都表示使用Windows 身份验证。
以下示例演示使用 SQL 身份验证的连接字符串。 $CREDENTIAL_PLACEHOLDER$
是密码密钥值对的占位符。 请注意,凭据嵌入在连接字符串中:
Server=serverName; Database=Northwind; uid=userID; $CREDENTIAL_PLACEHOLDER$
假设攻击者能够查看应用程序 Web.config
文件。 如果使用 SQL 身份验证连接到可通过 Internet 访问的数据库,攻击者可以使用此连接字符串通过 SQL Management Studio 或自己的网站上的 ASP.NET 页连接到数据库。 为了帮助缓解此威胁,请使用受保护的配置系统加密连接字符串信息Web.config
。
注意
有关 SQL Server 中可用的不同类型的身份验证的详细信息,请参阅 生成安全 ASP.NET 应用程序:身份验证、授权和安全通信。 有关说明 Windows 和 SQL 身份验证语法差异的进一步连接字符串示例,请参阅 ConnectionStrings.com。
总结
默认情况下,无法通过浏览器访问 ASP.NET 应用程序中扩展名 .config
的文件。 这些类型的文件不会返回,因为它们可能包含敏感信息,例如数据库连接字符串、用户名和密码等。 .NET 2.0 中的受保护配置系统允许加密指定的配置节,从而进一步保护敏感信息。 有两个内置保护的配置提供程序:一个使用 RSA 算法,一个使用 Windows 数据保护 API(DPAPI)。
本教程介绍了如何使用 DPAPI 提供程序加密和解密配置设置。 这可以通过编程方式完成,正如我们在步骤 2 中看到的那样,以及通过 aspnet_regiis.exe
步骤 3 中介绍的命令行工具来实现。 有关改用用户级密钥或使用 RSA 提供程序的详细信息,请参阅“进一步阅读”部分中的资源。
快乐编程!
深入阅读
有关本教程中讨论的主题的详细信息,请参阅以下资源:
- 构建安全 ASP.NET 应用程序:身份验证、授权和安全通信
- 加密 ASP.NET 2.0 应用程序中的配置信息
- 加密
Web.config
ASP.NET 2.0 中的值 - 如何:使用 DPAPI 加密 ASP.NET 2.0 中的配置节
- 如何:使用 RSA 加密 ASP.NET 2.0 中的配置节(可能为英文网页)
- .NET 2.0 中的配置 API
- Windows 数据保护
关于作者
斯科特·米切尔,七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自1998年以来一直在与Microsoft Web 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0。 他可以通过他的博客联系到mitchell@4GuysFromRolla.com他,可以在该博客中找到http://ScottOnWriting.NET。
特别感谢
本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Teresa Murphy 和 Randy Schmidt。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请把我扔一条线。mitchell@4GuysFromRolla.com