保护 ADO.NET 应用程序 - 第一部分
保护 ADO.NET 应用程序
引言
本文基于我在 Microsoft 虚拟学院 (MVA) 上学到的概念。
编写安全的 ADO.NET 应用程序不仅仅是避免常见的编码陷阱,例如不验证用户输入。访问数据的应用程序存在许多潜在的故障点,攻击者可以利用这些故障点来检索、修改或破坏敏感数据,因此我们需要了解所有安全方面。
.NET Framework 提供了许多类、服务和工具,这些工具对于保护和管理数据库应用程序非常有用。公共语言运行时 (CLR) 为代码的运行提供了类型安全的环境,并结合代码访问安全性 (CAS) 进一步限制了托管代码的权限。
请注意,安全的代码不能防御在处理数据库等非托管资源时出现的自我造成的安全漏洞。
保护应用程序是一个持续的过程。开发人员永远无法保证应用程序能免受所有攻击,因为我们无法预测新技术的出现会带来哪些类型的未来攻击。反之,仅仅因为没有人发现(或公开)过某个系统中的安全漏洞,并不意味着不存在或不存在可能存在的安全漏洞。您需要在项目设计阶段就考虑安全性,同时还要规划如何在应用程序的整个生命周期中维护安全性。
安全设计
开发安全应用程序的最大问题之一是安全性常常是事后诸葛亮,是在项目编码完成后才实施的东西。从一开始就没有将安全性融入应用程序会导致不安全的应用程序,因为很少有人仔细考虑如何使应用程序变得安全。
最后一刻的安全性实施会导致更多的错误,因为软件在新限制下会中断,或者不得不重写以适应意外的功能。每一行修改过的代码都可能引入新的错误。因此,您应该在开发过程的早期就考虑安全性,以便它可以与新功能的开发同步进行。

最小权限原则
在设计、构建和部署应用程序时,您必须假设您的应用程序会受到攻击。这些攻击通常来自以运行代码的用户权限执行的恶意代码。
您可以采用的一种对策是,通过以最小权限运行来尽可能地为您的代码竖起高墙。最小权限原则是指,任何给定的权限都应该授予完成工作所需的最少量代码,并在最短的时间内授予。
创建安全应用程序的最佳实践是,一开始不授予任何权限,然后为正在执行的特定任务添加最狭窄的权限。相比之下,从授予所有权限开始,然后逐个拒绝权限,会导致不安全的应用程序,这些应用程序难以测试和维护,因为可能无意中授予了比必需更多的权限而导致安全漏洞。
安全数据库
最小权限原则也适用于您的数据源。数据库安全的一些一般性指导原则包括:
- 创建具有尽可能低权限的帐户。
- 不要为了让代码正常工作而允许用户访问管理帐户。
- 不要将服务器端错误消息返回给客户端应用程序。
- 在客户端和服务器端验证所有输入。
- 使用参数化命令,避免动态 SQL 语句。
- 为正在使用的数据库启用安全审计和日志记录,以便在发生安全漏洞时收到警报。
保护连接信息 (ADO.NET)
使用 Windows 身份验证
为了限制对数据源的访问,您必须保护连接信息,例如用户 ID、密码和数据源名称。为了避免暴露用户信息。推荐的操作是使用 Windows 模式身份验证;Windows 身份验证在连接字符串中使用 `Integrated Security` 或 `Trusted_Connection` 关键字进行指定,从而无需使用用户 ID 和密码。
使用 Windows 身份验证时,用户由 Windows 进行身份验证,对服务器和数据库资源的访问由授予 Windows 用户和组的权限决定。
但是,如果我们有任何原因不使用 Windows 模式身份验证,那么我们需要采取极端预防措施。使用 ASP.NET,您可以在 web.config 中启用模拟。
让我们看一个例子
<identity impersonate="true"
userName="Domain\UserAccount"
password="*****" />
不要使用通用数据链接 (UDL) 文件
原因很简单,UDL 文件无法加密。
通过连接字符串编译防止注入攻击
当使用动态字符串连接来根据用户输入构建连接字符串时,可能会发生连接字符串注入攻击。如果用户输入未经验证且未转义恶意文本或字符,攻击者可能会访问服务器上的敏感数据或其他资源。
为防止这种情况,这里有一些编译器类可供您使用:
提供商 |
ConnectionStringBuilder 类 |
System.Data.SqlClient |
System.Data.SqlClient。 |
System.Data.OleDb |
System.Data.OleDb。 |
System.Data.Odbc |
System.Data.Odbc。 |
System.Data.OracleClient |
System.Data.OracleClient。 |
这是一个示例,展示了如何使用 SqlConnectionStringBuilder
。
Dim builder As New System.Data.SqlClient.SqlConnectionStringBuilder
builder("Data Source") = "(local)"
builder("Integrated Security") = True
builder("Initial Catalog") = "Mybd;NewValue=Bad"
Console.WriteLine(builder.ConnectionString)
结果是:
data source=(local);Integrated Security=True;
initial catalog=" Mybd;NewValue=Bad"
使用 Persist Security Info=False
Persist Security Info 的默认值为 false
;您应该在所有连接字符串中使用此默认值。将 Persist Security Info 设置为 true
或 yes 会允许在连接打开后从连接中获取安全敏感信息,包括用户 ID 和密码。当 Persist Security Info 设置为 false 或 no 时,安全信息在用于打开连接后会被丢弃,确保不受信任的源无法访问安全敏感信息。
从配置文件创建连接字符串
如果我们知道一些连接参数,我们可以使用配置文件来存储和恢复这些参数,以构建完整的连接字符串。
示例
Private Sub BuildConnectionString(ByVal dataSource As String, _
ByVal userName As String, ByVal userPassword As String)
' Retrieve the partial connection string named databaseConnection
' from the application's app.config or web.config file.
Dim settings As ConnectionStringSettings = _
ConfigurationManager.ConnectionStrings("partialConnectString")
If Not settings Is Nothing Then
' Retrieve the partial connection string.
Dim connectString As String = settings.ConnectionString
Console.WriteLine("Original: {0}", connectString)
' Create a new SqlConnectionStringBuilder based on the
' partial connection string retrieved from the config file.
Dim builder As New SqlConnectionStringBuilder(connectString)
' Supply the additional values.
builder.DataSource = dataSource
builder.UserID = userName
builder.Password = userPassword
Console.WriteLine("Modified: {0}", builder.ConnectionString)
End If
End Sub
加密配置文件
您还可以将连接字符串存储在配置文件中,这样就不需要在应用程序代码中嵌入它们了。配置文件是标准的 XML 文件,.NET Framework 为这些文件定义了一组通用元素。
在接下来的文章中,我将写关于用户、数据库等内容……