数据访问安全性

发布日期: 10/8/2004 | 更新日期: 10/8/2004
pponline

浏览全部的安全性指导主题

Microsoft Corporation

在本章中

数据存储,例如 Microsoft_ SQL Server™ 2000,在大多数分布式Web应用程序中起着至关重要的作用。数据存储可以包含各种类型的数据,包括用户应用程序参数、个人私密数据和医疗记录、审核以及安全日志,甚至还包括用户访问应用程序所需的凭据。很明显,这些数据都应该受到保护 — 不论是在存储期间还是在读/写的操作过程中 — 都要确保这些数据只能被拥有相应权限的用户访问。

本章描述了与访问数据存储相关的关键安全性问题的解决方案,并提供了从分布式 Web 应用程序中访问数据的建议。本章主要专注于 SQL Server 2000,但其中的许多主题在其它数据存储中也是同样适用的。

目标

学习本章:

创建安全的数据访问组件。

使用 Windows 身份验证或 SQL Server 身份验证从 ASP.NET 连接到 SQL Server。

选择适合您应用程序的 SQL Server 2000 的授权机制。

使用 SSL 或 IPSec以保护数据在客户端应用程序和数据源之间传送过程中的安全。

创建在 SQL Server 2000 中使用的拥有最少权限的数据库帐户。

安全地存储数据库连接字符串和用户凭据。

使用密码哈希和 salt 值将用于身份验证的用户凭据安全地存储到数据库中。

防止 SQL 数据库受到 SQL注入式攻击。

在 SQL Server 2000 上启动审核机制。

适用于:

本章适用于下列产品和技术:

Microsoft_ Windows_ XP 或 Windows 2000 Server(带有Service Pack 3)及其后的操作系统

.NET Framework 1.0 版本(带有Service Pack 2)及其后的版本

Microsoft Visual C# .NET 开发工具

SQL Server 2000(带有Service Pack 2)及其后的版本

如何使用本章

要从本章中得到最大的收获:

您必须拥有使用 Visual C# .NET进行编程的经验。

您必须拥有开发和配置 ASP.NET Web 应用程序的经验。

您必须拥有配置 SQL Server 2000 安全性的经验。

您必须拥有使用 Windows 管理工具配置 Windows 安全性和创建用户帐户的经验。

阅读文章“Introduction to Building Secure ASP.NET Applications”,该文详细说明了身份验证、授权以及分布式 Web 应用程序中的安全通信的重要性。

阅读文章“Security Model for ASP.NET Applications”,该文概述了用于创建分布式 ASP.NET Web 应用程序的体系结构和技术,并且着重指出了身份验证、授权以及安全通信在此体系结构中所处的位置。

阅读文章“Authentication and Authorization”,该文提供了有关模拟、受信任子系统和 SQL Server 2000 角色的信息,这些主题都会在本章中进一步讨论。

阅读文章“Communication Security”,该文介绍了 SSL 和 IPSec,您可以使用它们来保护数据存储和客户应用程序之间的通信信道。

阅读文章“ASP.NET Security”,该文涉及深入的与 ASP.NET 相关的安全问题。尤其是进程标识、调用方标识和模拟的问题,这些问题影响了您的 ASP.NET 应用程序进行数据存储的身份验证时可用的选项。

请阅读下列文章,这些文章介绍了如何实现在本章中提到的许多技巧:

How To Create a Custom Account to run ASP.NET

How To Use IPSec to Provide Secure Communication Between Two Servers

How To Use SSL to Secure Communication with SQL Server 2000

How To Create a DPAPI Library

How To Use DPAPI (Machine Store) from ASP.NET

How To Use DPAPI (User Store) from ASP.NET with Enterprise Services

How To Store an Encrypted Connection String in the Registry

How To Use Forms Authentication with SQL Server 2000

*
本页内容
介绍数据访问安全性介绍数据访问安全性
身份验证身份验证
授权授权
安全通信安全通信
用最少的权限连接用最少的权限连接
创建具有最少权限的数据库帐户创建具有最少权限的数据库帐户
安全存储数据库连接字符串安全存储数据库连接字符串
为数据库验证用户身份为数据库验证用户身份
SQL 注入式攻击SQL 注入式攻击
审核审核
SQL Server 的进程标识SQL Server 的进程标识
小结小结

介绍数据访问安全性

图 1 显示与数据访问相关的重要安全性问题。


1 重要的数据访问安全性问题

下面总结图 1 中显示的重要问题(本章的其余部分将详细讨论这些问题):

安全存储数据库连接字符串。如果您的应用程序使用 SQL 身份验证连接到 SQL Server,或者连接到需要显式登录凭据的非 Microsoft 数据库,则这一点尤其重要。在这些情况中,连接字符串包含明文形式用户名和密码。

使用正确的标识来访问数据库。利用调用进程的进程标识、一个或多个服务标识或者原调用方的标识(使用模拟/委派)可以执行数据访问。选择什么样的标识由数据访问模型确定–受信任的子系统或模拟/委派。

保护通过网络传递的数据的安全。例如,保护登录凭据以及传入和传出 SQL Server 的机密数据。

仅在您使用 SQL 身份验证而非 Windows 身份验证的网络上公开登录凭据。

SQL Server 2000 利用服务器证书支持 SSL。IPSec 还可以用于对客户端计算机(例如 Web 或应用程序服务器)和数据库服务器之间的通信进行加密。

对数据库调用方进行身份验证。SQL Server 支持 Windows 身份验证(利用 NTLM 或 Kerberos)和 SQL 身份验证(利用 SQL Server 内置的身份验证机制)。

向数据库调用方授权。权限与单独的数据库对象关联,也可以与用户、组或角色关联。

SQL Server 网关守卫

图 2 重点说明 SQL 服务器数据访问的重要网关守卫。

a

2. SQL Server 网关守卫

重要的网关守卫包括:

选择的用于保存数据库连接字符串的数据存储。

SQL Server 登录(由在连接字符串中指定的服务器名确定)。

数据库用户(SQL Server 登录所映射到的数据库中的用户上下文)及其相关的数据库角色。

与单独的数据库对象关联的权限。

权限可分配给用户、组或角色。

受信任的子系统与模拟/委派

数据库访问权限的粒度是一个需要考虑的关键因素。您必须考虑您是需要对数据库进行用户级授权(这需要模拟/委派模型),还是可以在应用程序中间层使用应用程序角色逻辑向用户授权。这就是受信任的子系统模型。

如果数据库要求用户级授权,那么您需要模拟原调用方。尽管这种模拟/委派模型受支持,但是建议您使用受信任的子系统模型;在该模型中,在 IIS/ASP.NET 关口对原调用方进行检查,将它映射到某个角色,然后根据角色成员身份向它授权。然后,在应用程序或角色级别上使用服务帐户,或者使用应用程序的进程标识(比如 ASPNET 帐户),向应用程序的系统资源授权。

图 3 显示这两个模型。


3 用于数据库访问的受信任的子系统模型和模拟/委派模型

当连接 SQL Server 进行数据访问时,还有许多应当考虑的重要因素。下面总结这些因素(后面各节中将详细阐述):

应当使用什么类型的身份验证?虽然 Windows 身份验证提供增强的安全功能,但是防火墙和非信任域可能会迫使您使用 SQL 身份验证。如果情况果真如此,您应当确保应用程序尽可能安全地使用 SQL 身份验证,这将在本章内下文中的“SQL 身份验证”一节中介绍。

单用户角色与多用户角色。根据应用程序的用户,您的应用程序是需要单独使用一个在数据库内拥有一组固定权限的帐户访问 SQL,还是需要多个(基于角色的)帐户?

调用方标识。数据库需要通过调用上下文来接收原调用方的标识才能执行授权或审核,还是您可以在应用程序级别使用一个或多个受信任的连接来传递原调用方标识吗?

操作系统要传递原调用方的标识,它需要在中间层使用模拟/委派模型。这会大大降低连接池的效力。虽然连接池仍处于启用状态,但是它会生成许多小池(分别为每个安全上下文),几乎不重用任何连接。

您正在向数据库服务器发送或从该服务器发出机密数据吗?虽然 Windows 身份验证可以让您不必在网络上向数据库传递用户凭据,但是,如果您的应用程序数据属于机密数据(比如,雇员详细资料和工资表数据),则应当使用 IPSec 或 SSL 予以保护。

身份验证

本节介绍在连接到 SQL Server 前您应当如何对 SQL Server 的客户端进行身份验证以及应当如何在客户端应用程序中选择用于数据库访问的标识。

Windows 身份验证

Windows 身份验证比 SQL 身份验证更安全,原因是前者有以下优点:

为您管理凭据,并且不在网络上传递凭据。

使您避免在连接字符串中嵌入用户名和密码。

通过设置密码失效期限、最小长度和在多次无效登录请求后锁定帐户,登录安全性得以提高。这减轻了字典攻击带来的威胁。

在以下方案中应使用 Windows 身份验证:

您使用了受信任的子系统模型并且要使用单个固定标识连接到 SQL Server。如果您从 ASP.NET 进行连接,这会假定 Web 应用程序配置中没有设置模拟。

在这一方案中,请使用 ASP.NET 进程标识或服务组件标识(从用来运行 Enterprise Services 服务器应用程序的帐户中获得)。

您打算利用委派来委派原调用方的安全上下文(并准备通过放弃数据库连接池来牺牲应用程序的可伸缩性)。

当您使用 Windows 身份验证来连接到 SQL Server 时,请考虑以下关键点:

使用 ASP.NET 进程帐户的最少权限原则。不要通过将“充当操作系统的一部分”这一权限授予 ASP.NET 进程帐户来启用 LogonUser API 调用。

确定哪些代码需要更多权限,然后将这些代码放到在进程外 Enterprise Services 应用程序中运行的服务组件内。

更多信息

有关从 ASP.NET 访问网络资源以及选择和配置正确的帐户以运行 ASP.NET 的更多信息,请参见文章:“ASP.NET 安全性”。

使用 Windows 身份验证

使用 Windows 身份验证从 ASP.NET 应用程序(或 Web 服务,或者 ASP.NET 承载的远程组件)连接到 SQL Server 时,您可以从以下方法中选择:

使用 ASP.NET 进程标识。

使用 ASP.NET 中的固定标识。

使用服务组件。

使用 LogonUser API 并模拟特定标识。

使用原调用方的标识。

使用匿名 Internet 用户帐户。

建议

建议通过在 Web 服务器上将密码更改为某个已知值来配置本地 ASP.NET 进程标识,然后在数据库服务器上通过创建具有相同用户名和密码的本地用户创建镜像帐户。下面详细介绍这一方法以及其他方法。

使用 ASP.NET 进程标识

如果您直接从 ASP.NET 应用程序(或 Web 服务,或者 ASP.NET 承载的远程组件)连接到 SQL Server 时,请使用 ASP.NET 进程标识。这是一种常用方法,并且该应用程序定义了信任界线,也就是说,数据库信任 ASP.NET 帐户访问数据库对象。

您有三个选项:

使用镜像的 ASPNET 本地帐户。

使用镜像的自定义本地帐户。

使用自定义的域帐户。

使用镜像的 ASPNET 本地帐户

这是一种最简单的方式,一般在您拥有目标数据库(并且可以控制本地数据库服务器帐户的管理)时使用。如果选择此方法,您应使用 ASPNET 最少权限本地帐户来运行 ASPNET,然后在数据库服务器上创建一个重复帐户。

这种方法还有一个优点是,它可以跨非信任域和通过防火墙发挥作用。防火墙打开的端口数可能不足以支持 Windows 身份验证。

使用镜像的自定义本地帐户

此方法与上一方法基本相同,不过使用此方法时您不使用默认的 ASPNET 帐户。这意味着两件事:

您需要创建一个自定义的、拥有适当权限和特权的本地帐户。

有关详细信息,请参见 “How To Create a Custom Account to Run ASP.NET”。

您不再使用 .NET Framework 安装进程创建的默认帐户。公司的某项制度可能不允许使用默认安装帐户。这有可能提高应用程序的安全门槛。

有关详细信息,请参见 Sans Top 20:“G2–没有密码或密码隐密性最弱的帐户” (http://www.sans.org/top20.htm)。

使用自定义的域帐户

这种方法与前一方法相似,不同之处在于您使用最少权限的域帐户而非本地帐户。该方法假设客户端计算机和服务器计算机位于同一个域或信任域内。它的主要优点是不在计算机与计算机间共享凭据,计算机只提供访问域帐户的权限。并且,域帐户的管理也更为容易。

实现镜像的 ASPNET 进程标识

为使用镜像的帐户从 ASP.NET 连接到数据库,您需要执行以下操作:

使用 Web 服务器上的“用户管理器”将 ASPNET 帐户的密码重新设置为一个已知的强密码值。

重要如果您将 ASPNET 密码更改为已知值,则本地计算机的“本地安全颁发机构” (LSA) 中的密码将不再与 Windows 安全帐户管理器 (SAM) 数据库中存储的帐户密码匹配。如果您需要还原为 AutoGenerate 默认值,必须执行以下操作:

运行 ASPNET_regiis.exe,将 ASP.NET 重置为它的默认配置。有关详细信息,请参见“Microsoft知识库”中的文章 Q306005:“HOWTO:Repair IIS Mapping After You Remove and Reinstall IIS”。当您完成重置后,您会获得新帐户和新的 Windows 安全标识符 (SID)。该帐户的权限设置为它们的默认值。这样,您需要显式重新应用您原来为旧的 ASPNET 帐户设置的权限和特权。

在 Machine.config 中显式设置密码。使用实用工具 ASPNET_setreg.exe 在受保护的注册表项中对 <processModel> 凭据进行加密。有关详细信息,请参见“Microsoft知识库”中的文章 Q329290:“HOWTO:Use the ASP.NET Utility to Encrypt Credentials and Session State Connection Strings”。

<processModel
       userName="registry:HKLM/SOFTWARE/YourSecureApp/
                 processModel/ASPNET_SETREG,userName"
       password="registry:HKLM/SOFTWARE/YourSecureApp/
                 processModel/ASPNET_SETREG,password" . . . />
   .  

您应当使用 Windows ACL 来防止 Machine.config 被非法访问。例如,限制 IIS 匿名 Internet 用户帐户访问 Machine.config。

在数据库服务器上创建镜像帐户(使用相同的用户名和密码)。

在 SQL 数据库中,为本地 ASPNET 帐户创建服务器登录,并将该登录映射到所需数据库中的一个用户帐户。然后创建一个数据库用户角色,将数据库用户添加给该角色,并为该角色配置适当的数据库权限。

有关详细信息,将在本章内下文中的“创建具有最少权限的数据库帐户”一节中介绍。

连接到使用 Windows 身份验证的 SQL Server

要连接到使用 Windows 身份验证的 SQL Server,请执行以下步骤:

在客户端应用程序中,使用包含“Trusted_Connection=Yes”,或“Integrated Security=SSPI”的连接字符串。这两个字符串是等效的,都产生 Windows 身份验证(假定 SQL Server 已配置为使用 Windows 身份验证)。例如:

SqlConnection conn = new SqlConnection(
          "server=YourServer; database=YourDatabase;" +
          "Trusted_Connection=Yes;");

- 或者 -

SqlConnection conn = new SqlConnection(
          "server=YourServer; database=YourDatabase;" +
          "Integrated Security=SSPI;");

发出请求的客户端(即由 SQL Server 验证其身份的客户端)的标识由该客户端的线程模拟令牌(如果线程当前正在进行模拟)或该客户端的当前进程令牌来确定。

使用 ASP.NET 内的固定标识

使用此方法,您应使用 Web.config 中的 <identity> 元素来将 ASP.NET 应用程序配置为模拟指定的固定标识。下面的例子就显示了 <identity>元素,其中用户凭据在注册表中以文章“ASP.NET Security”所讨论的方式使用 ASPNET_setreg.exe 进行加密。

<identity
   impersonate="true"
   userName="registry:HKLM/SOFTWARE/YourSecureApp/
             identity/ASPNET_SETREG,userName"
   password="registry:HKLM/SOFTWARE/YourSecureApp/
             identity/ASPNET_SETREG,password" />

这成为您在连接网络资源(包括数据库)时使用的默认标识。

当在 Windows 2000 服务器上使用 .NET Framework 1.0 时不推荐使用固定的模拟标识。这是因为 ASP.NET 需要“充当操作系统的一部分”这一权限。ASP.NET 进程之所以需要这一权限是因为它需要使用您所提供的用户凭据来执行 LogonUser 调用。

Microsoft Windows .NET Server 2003 执行 LogonUser 调用时不需要“充当操作系统的一部分”这一权限。

并且,.NET Framework 1.1 版将在 Windows 2000 上为此方案提供增强功能。由 IIS 进程执行登录,这样 ASP.NET 就不需要“充当操作系统的一部分”这一权限。

有关这一强特权的详细信息,请参见 Microsoft Systems Journal(Microsoft 系统期刊)1999 年 8 月期的“Security Briefs”(安全摘要)栏目 (http://www.microsoft.com/msj/0899/security/security0899.aspx)。

.NET Framework 1.1 版将在 Windows 2000 上为此方案提供增强功能。由 IIS 进程执行登录,这样 ASP.NET 就不需要“充当操作系统的一部分”这一权限。

使用服务组件

您可以开发专门用于包含数据访问代码的服务组件。利用服务组件,您可以通过将组件驻留在使用特定标识运行的 Enterprise Services (COM+) 服务器应用程序中来访问数据库,也可以编写使用 LogonUser API 的代码来执行模拟。

使用进程外的服务组件可以提高安全门槛,因为进程跳跃增加攻击者进行攻击的难度,特别是进程以不同的标识运行时攻击更难以得手。另一个优点是您可以将需要更多权限的代码与应用程序的其余部分隔开。

调用 LogonUser 并模拟特定的 Windows 标识

您不应当直接从 ASP.NET 调用 LogonUser。在 Windows 2000 中,这种方法要求您给 ASP.NET 进程标识提供“充当操作系统的一部分”这一权限。

首选的方法是使用 Enterprise Services 服务器应用程序中的服务组件从 ASP.NET 进程外调用 LogonUser,如上所述。

使用原调用方的标识

要使用这种方法,您需要直接从 ASP.NET 或服务组件使用 Kerberos 委派并模拟数据库的调用方。

从 ASP.NET 中,将以下代码添加到应用程序的 Web.config 文件中。

<identity impersonate="true" />

从服务组件中,调用 CoImpersonateClient。有关更多关于 CoImpersonateClient的使用,参见文章:“Enterprise Services Security”。

使用匿名 Internet 用户帐户

作为上一方法的一种变化形式,对于应用程序使用表单或 Passport 身份验证(这意味着 IIS 匿名身份验证)的情形,您可以在应用程序的 web.config 中启用模拟,以便使用匿名 Internet 用户帐户来访问数据库。

<identity impersonate="true" />

通过将 IIS 配置为匿名身份验证后,该配置导致您的 Web 应用程序代码使用匿名 Internet 用户模拟令牌运行。在 Web 托管环境中,这种方式的优点是允许您分别审核和跟踪从多个 Web 应用程序进行的数据库访问。

更多信息

有关使用原调用方的标识的详细信息和实施细节,请参见文章“Securing .NET Web Applications in an Intranet Environment”中的“Flowing the Original Caller to the Database”。

有关如何将 IIS 配置为使用匿名用户帐户的详细信息,请参见文章:“ASP.NET Security”。

何时不能使用 Windows 身份验证?

某些应用程序方案可能禁止使用 Windows 身份验证。例如:

您的数据库客户端和数据库服务器被一个禁止使用 Windows 身份验证的防火墙隔开。

您的应用程序需要使用多个标识来连接到一个或多个数据库。

您要连接到非 SQL Server 数据库。

您在 ASP.NET 中没有一种安全方式来以特定的 Windows 用户身份运行代码。或者您无法(或不想)传递原调用方的安全上下文,或者您想使用专用的服务帐户(而不是向最终用户授予登录权限),或者同时存在以上两种事实。

在这些方案中,您需要使用 SQL 身份验证(或数据库的本机身份验证机制),并且必须:

在应用程序服务器上保护数据库用户凭据。

在从服务器传输到数据库过程中保护数据库用户凭据。

如果您使用 SQL 身份验证,有多种方法可以确保 SQL 身份验证更加安全。这些方法将在下一节详细介绍。

SQL 身份验证

如果您的应用程序需要使用 SQL 身份验证,您需要考虑以下关键点:

使用具有最少权限的帐户连接到 SQL。

凭据通过网络传递,因此必须对它们进行保护。

必须保护 SQL 连接字符串(它包含凭据)。

连接字符串类型

如果您使用凭据(用户名和密码)来连接到 SQL Server 数据库,则连接字符串形式如下:

Using the SQL Server .NET Data Provider:
SqlConnection conn = new SqlConnection(
          "server=YourServer; uid=YourUserName; pwd=YourStrongPwd;" +
          "database=YourDatabase");

Using the OLE DB .NET Data Provider:
OleDbConnection conn = new OleDbConnection(
          "Provider=SQLOLEDB; Data Source=YourServer;" +
          "uid=YourUserName; pwd=YourStrongPwd; Initial Catalog=YourDatabase");

如果您需要连接到安装在同一计算机上的 SQL Server 的特定实例(只在 SQL Server 2000 或以后版本中提供的功能),则连接字符串形式如下:

Using the SQL Server .NET Data Provider:
SqlConnection conn = new SqlConnection(
          "server=YourServer/Instance; uid=YourUserName; pwd=YourStrongPwd;" +
          "database=YourDatabase");

如果您利用显式凭据(用户名和密码)连接到 Oracle 数据库,则连接字符串形式如下:

OleDbConnection conn = new OleDbConnection(
          "Provider=MSDAORA; Data Source=YourDatabaseAlias;" +
          "User ID=YourUserName; Password=YourStrongPwd;");

更多信息

更多有关在连接中使用通用数据链接 (UDL) 文件的详细信息,请参见“Microsoft 知识库”中的文章 Q308426:“HOW TO:Use Data Link Files with the OleDbConnection Object in Visual C# .NET”。

选择用于连接的 SQL 帐户

不要使用内置的 sa 帐户或任何属于 SQL Server sysadmin 固定服务器角色或 db_owner 固定数据库角色的成员的帐户进行数据访问。sysadmin 的成员在SQL Server中能执行任何动作。db_owner 的成员在数据库中具有不受限制的权限。您应该使用具有最少权限的、采用强密码的帐户。

避免使用以下连接字符串:

SqlConnectionString = "Server=YourServer/Instance;
                       Database=YourDatabase; uid=sa; pwd=;"

使用具有最少权限的、采用强密码的帐户,例如:

SqlConnectionString= "Server=YourServer/Instance;
                      Database=YourDatabase;
                      uid=YourStrongAccount; 
                      pwd=YourStrongPassword;"

注意,这并没有解决在 Web.config 文件中以明文形式存储凭据的问题。您到目前为止所做的一切是利用具有最少权限的帐户限制在发生危害时可能造成的损失范围。要进一步提高安全门槛,您应当对凭据加密。

如果您在安装 SQL Server 时选择区分大小写的排序顺序,那么您的登录 ID 也区分大小写。

在网络上传递凭据

当您连接到使用 SQL 身份验证的 SQL Server 时,用户名和密码在网络上以明文形式传递。这可能意味着一种严重的安全隐患。有关如何保护应用程序或 Web 服务器和数据库服务器之间的信道的详细信息,将在本章内下文中的“安全通信”一节中介绍。

保护 SQL 连接字符串

用户名和密码不应当以明文形式存储在配置文件中。有关如何安全存储连接字符串的详细信息,将在本章内下文中的“安全存储数据库连接字符串”一节中介绍。

对非 SQL Server 数据库进行身份验证

您在连接到非 SQL 数据库时可能遇到的典型问题与您需要使用 SQL 身份验证时的情形相似。如果目标资源不支持 Windows 身份验证,那么您需要提供明确的凭据。要保护这种类型的应用安全,您必须安全存储连接字符串,还必须保护网络上的通信安全(防止凭据被截取)。

授权

SQL Server 提供了许多基于角色的授权方法。这些方法都围绕 SQL Server 支持的以下三种类型的角色:

用户定义的数据库角色。这些角色用于将数据库中拥有相同安全权限的用户归集成组。创建 SQL Server 登录,然后与特定的数据库用户进行匹配。接着向用户数据库角色添加数据库用户,并使用这些角色分别建立各个数据库对象(存储过程、表、视图等等)的权限。

应用程序角色。这些角色与用户数据库角色的相似之处是:在创建对象权限时需要使用它们。但是,与用户数据库角色不同的是,它们不包含用户或组,而是必须由一个使用内置存储过程的应用程序激活。激活后,授予角色的权限决定应用程序的数据访问能力。

应用程序角色允许数据库管理员向指定的数据库对象授予选定的应用程序访问权限。这与向用户授予权限不同。

固定的数据库角色。SQL Server 还提供固定的服务器角色,比如 db_datareader db_datawriter。这些内置的角色存在于所有数据库中,可用于将数据库中的一组特定的(和其他常用的)读取权限快速授予某个用户。

有关这些不同角色类型(以及与固定的数据库角色相似但在服务器级别而非数据库级别上应用的固定服务器角色)的详细信息,请参见“SQL Server 联机图书”(http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp)。

使用多个数据库角色

如果您的应用程序有多个用户类别,同一类别内的用户需要数据库中的相同权限,那么您的应用程序需要多个角色。各个角色分别需要数据库中的一组不同的权限。例如,Internet 用户角色的成员需要对数据库中的大多数表具有只读权限,而管理员或操作员角色的成员需要读/写权限。

在这方案中,可以使用多个用户定义的 SQL Server 数据库角色。这些角色用于向那些在数据库内拥有相同安全权限的用户组授予数据库对象权限。使用这种方法,则必须:

创建用于数据库访问的多个服务帐户。

为每个帐户创建SQL Server登录。创建数据库用户,授予其对数据库的登录访问权限。

将每个数据库用户添加到用户定义的数据库角色。

为数据库中的每个角色建立必要的数据库权限。

在应用程序(ASP.NET Web 应用程序、Web 服务或中间层组件)中向用户授权,然后使用数据访问层中的应用逻辑来确定让哪一个帐户连接到数据库。这是基于调用方的角色成员身份。

您可以用声明方式分别配置不同的方法来仅向那些属于一组角色的用户授予权限。然后,在方法代码中添加强制性角色检查以确定角色成员身份(该成员身份决定要使用的连接)。

图 4 显示了这一方法。


4. 使用多个 SQL 用户数据库角色连接到 SQL Server

针对此方案,若要使用首选的 Windows 身份验证,您可以在进程外服务组件中开发代码(使用 LogonUser API),以模拟一组 Windows 标识中的某个标识。

对于 SQL 身份验证,您应根据应用程序中基于角色的逻辑来使用不同的连接字符串(包含不同的用户名和密码)。

安全通信

在大多数应用程序方案中,您需要保护应用程序服务器和数据库之间的通信链接安全。您需要能够确保:

消息保密性。必须对数据进行加密,确保其保持私密。

消息完整性。必须为数据签名,确保其不被篡改。

有的方案中,需要保护在应用程序服务器和数据库服务器之间传递的所有数据;而有的方案中,需要保护通过特定连接发送的数据中的选定数据。例如:

在一个 Intranet人力资源应用中,在客户端和数据库服务器之间传递的一些雇员资料属于机密数据。

在 Internet应用(比如安全银行业务应用程序)中,在应用程序服务器和数据库服务器之间传递的所有数据都必须得到安全保护。

如果您使用的是 SQL 身份验证,您还应当保护通信链接安全,确保用户名和密码不会被网络监视软件破坏。

选项

应用程序服务器和数据库服务器之间的网络链接安全保护有两种选择:

IPSec

SSL (在 SQL Server 计算机上使用服务器证书)

您必须运行 SQL Server 2000 才可以支持使用 SSL。较早的版本不支持 SSL。客户端必须已经安装了 SQL Server 2000 客户机库。

选择一种方法

是否应当使用 IPSec 或 SSL 取决于许多重要的环境因素,比如防火墙原因、操作系统和数据库版本等等。

IPSec 不用于替代应用程序级别的安全机制。现在它用于以下几方面:用作一种纵深防御机制;用于在不改变不安全应用程序的情况下为它们提供保护;保护非 TLS(例如 SSL)协议,抵制网络攻击。

更多信息

有关配置 IPSec 的详细信息,请参见文章 “How To Use IPSec to Provide Secure Communication Between Two Servers”。

有关配置 SSL 的详细信息,请参见文章 “How To Use SSL to Secure Communication with SQL Server 2000”。

有关 SSL 和 IPSec 的更多通用信息,请参见文章:“通信安全性”。

用最少的权限连接

用最少权限连接数据库意味着您建立的连接在数据库中仅拥有您需要的最少权限。简单地说,您不要用 sa 帐户,sysadmin 角色或 db_owner角色的成员进行数据库连接。理想状态下,如果未授权当前用户添加或更新记录,则用于建立连接的相应帐户(这可以合成为代表特定角色的某个标识)就无法在数据库中添加或更新记录。

当您连接到 SQL Server 时,您所使用的方式必须支持数据库授权所要求的粒度。您需要考虑数据库信任什么。它可以信任:

应用程序。

应用程序定义的角色。

原调用方。

数据库信任应用程序

考虑一个您授权其使用数据库的财务应用程序。财务应用程序负责管理用户身份验证和授予访问权。在这种情况下,您可以通过单个受信任的帐户管理连接(该帐户对应于一个 SQL 登录或一个映射到 SQL 登录的 Windows 帐户)。如果您正在使用 Windows 身份验证,这通常意味允许使用执行调用的应用程序的进程标识(比如 ASP.NET 辅助进程或 Enterprise Services 服务器应用程序标识)来访问数据库。

从授权的角度来看,这个方法非常粗糙,因为连接所使用的标识对应用程序需要的所有数据库对象和资源都有访问权限。这个方法的优点是您可以使用连接池,并可简化管理,因为您只向单个帐户授权。缺点是所有用户都以相同的连接权限运行。

数据库信任不同的角色

您可以使用独立的受信任的连接组成的连接池来连接到数据库,这些连接与您的应用程序定义的角色相对应;例如,一个连接供出纳员使用,而另一个供经理使用,等等。

这些连接可以使用 Windows 身份验证,也可以不使用。Windows 身份验证的优点是它处理凭据管理,并且不在网络上发送凭据。但是,尽管 Windows 身份验证可以在进程级别或应用程序级别上使用,但是如果您需要多个标识(每个角色一个标识)就会带来其他问题。

许多应用程序使用 LogonUser API 建立线程级的 Windows 访问令牌来建立上下文的安全性,但这种方法要面对进行凭据管理的问题,因为应用程序必须安全存储帐户用户名和密码。而且此类应用程序也需要面对权限问题,因为它要求调用进程帐户必须具有“充当操作系统的一部分”这一权限。

使用 SQL 身份验证的应用程序同样面临着凭据管理的问题,而且需要一条连接到数据库的安全通信信道来保护凭据。

数据库信任原调用方

在这种情况下,您需要将原调用方通过多个层传送到数据库。这意味着客户端需要网络凭据,以便能够从一个计算机跳到另一个计算机。这就需要 Kerberos 委派。

虽然这一解决方案提供数据库的细化的授权,但是,由于您知道原调用方的标识并能够给数据库对象建立每个用户权限,所以它会影响应用程序的性能和伸缩性。连接池(虽然仍处于启用状态)变得无效。

创建具有最少权限的数据库帐户

下面的步骤是一个简单示例,用于向您说明如何创建具有最少权限的数据库帐户。虽然大多数数据库管理员已经熟悉这些步骤,但是许多开发人员可能不熟悉,而依靠使用 sa 帐户来强制他们的应用程序运行。

在从一个开发环境转移到测试环境然后再进入生产环境时,这可能会带来难题,因为应用程序从一个高度开放的环境转移到了一个控制得较严格的设置中,而这样的设置妨碍应用程序正常运行。

您应通过为 SQL 帐户或 Windows 帐户(用户或组)创建 SQL 登录来启动。然后,通过创建一个数据库用户授予对该数据库的登录访问权限,将数据库用户添加到用户自定义的数据库角色中,并给该角色分配权限。

设置 SQL 的数据访问帐户

1.

创建新的用户帐户并将该帐户添加到 Windows 组中。如果您要管理多个用户,应使用用户组。如果您要处理单个应用程序帐户(比如重复的 ASP.NET 进程帐户),则可以选择不向 Windows 组添加该帐户。

2.

为用户/组创建 SQL Server 登录。

启动“Enterprise Manager”,找到您的数据库服务器,然后展开“Security”文件夹。

右键单击“Logins”,然后单击“New Login”。

Name 字段中输入 Windows 帐号名称,然后单击“OK”以关闭“SQL Server Login Properties”对话框。

3.

在有关数据库中新建一个数据库用户以允许登录访问该数据库。

使用“企业管理器”并展开“数据库”文件夹,然后展开该登录要求访问的数据库。

右键单击“用户”,然后单击“新建数据库用户”。

选择以前创建的登录名。

输入一个用户名。

4.

创建一个新的数据库角色,其后将对该角色分配权限。

在“数据库”文件夹下右键单击“角色”,然后单击“新建数据库角色”。

输入一个角色名。

单击“添加”,将数据库用户添加到角色中。

按下面介绍的方法配置权限。

5.

对于需要访问的表,向该数据库用户授予“Select”权限;对于任何相关的存储过程,向该用户授予“Execute”权限。

如果该存储过程和表都由同一个人拥有,并且只通过存储过程访问该表(并不需要直接访问表),则仅授予存储过程的执行权限就足够了。其原因在于所有者关系链这一概念。有关详细信息,请参见“SQL Server 联机图书”。

6.

如果您希望用户帐户拥有对数据库中的所有视图和表的访问权限,请将这些视图和表添加到 db_datareader 角色中。

public 角色存在于每个数据库中,所有其它的数据库用户和角色都属于该角色而且不能删除。您应该撤消或拒绝在该角色上的权限,这样授权就完全由与用户定义的数据库角色相关的权限来确定了。

安全存储数据库连接字符串

可以在多个位置使用多种方式来存储数据库连接字符串,这些位置和方式的安全程度和配置灵活程度各不相同。

选项

下面的列表介绍了用于存储连接字符串的主要选项:

用 DPAPI 加密

在 Web.config 或 Machine.config 中采用明文形式

UDL 文件

自定义的文本文件

注册表

COM+ 目录

使用 DPAPI

Windows 2000 和更高版本的操作系统都提供 Win32 数据库保护 API (DPAPI) 来对数据进行加密和解密。DPAPI 是密码 API (Crypto API) 的一部分,它在 Crypt32.dll 中实施。它由两个方法构成–CryptProtectDataCryptUnprotectData

DPAPI 特别有用,因为它能够消除使用密码的应用程序所带来的密钥管理问题。虽然加密能确保数据安全,但您必须采取额外的步骤来确保密钥的安全。 DPAPI 使用与 DPAPI 函数的调用代码关联的用户帐户的密码,以便派生加密密钥。因此,操作系统(而不是应用程序)管理密钥。

为什么不用 LSA?

许多应用程序使用本地安全颁发机构 (LSA) 来存储机密。DPAPI 在以下方面优于 LSA 方法:

要使用 LSA,进程需要管理权限。这会造成安全隐患,因为它极大地增加了设法破坏进程的攻击者可能造成的损失。

LSA 只为机密存储提供数量有限的插槽,而许多插槽已经被系统占用。

机器存储与用户存储

DPAPI 能够与机器存储或用户存储(需要一个已加载的用户配置文件)配合使用。 DPAPI 默认情况下用于用户存储,但您可以通过将 CRYPTPROTECT_LOCAL_MACHINE 标志传递给 DPAPI 函数来指定使用机器存储。

这种用户配置文件方式提供了一个额外的安全层,因为它限制了哪些用户能访问机密内容。只有加密该数据的用户才能解密该数据。但是,当通过 ASP.NET Web 应用程序使用 DPAPI 时,使用用户配置文件需要您执行额外的开发工作,因为您需要采取明确的步骤来加载和卸载用户配置文件(ASP.NET 不会自动加载用户配置文件)。

机器存储方式更容易开发,因为它不需要用户配置文件管理。但是,除非使用一个附加的熵参数,否则并不安全,因为该计算机的任何用户都可以解密数据。熵是一个设计用来使解密机密内容更为困难的随机值。使用附加的熵参数出现的问题在于它必须由应用程序安全地存储起来,这带来了另一个密钥管理问题。

如果您将 DPAPI 和机器存储一起使用,那么加密字符串对于给定的计算机是特定的,因此您必须在每台计算机上生成加密数据。不要在场或群集中跨计算机复制加密数据。

如果将 DPAPI 和用户存储一起使用,则可以用一个漫游的用户配置文件在任何一台计算机上解密数据。

DPAPI 实施解决方案

本节介绍两个实施解决方案,说明如何从 ASP.NET Web 应用程序使用 DPAPI 来保护连接字符串(或任何类型的机密信息)。本节介绍的实施方案包括:

Enterprise Services 使用 DPAPI。此方案允许您将 DPAPI 用于用户存储。

直接从 ASP.NET 使用 DPAPI。此方案允许将 DPAPI 用于机器存储,这样便更容易开发解决方案,因为可以直接从 ASP.NET Web 应用程序调用 DPAPI。

Enterprise Services 使用 DPAPI

ASP.NET Web 应用程序无法调用 DPAPI 和使用用户存储,因为这需要有一个已加载的用户配置文件。常用来运行 Web 应用程序的 ASPNET 帐户是非交互式帐户,而非交互式帐户没有用户配置文件。此外,如果 ASP.NET 应用程序正在进行模拟,则 Web 应用程序线程作为一个当前已验证身份的用户运行,该线程会因请求而异。

这里介绍有关要使用 DPAPI 的 ASP.NET 应用程序的以下问题:

通过在默认的 ASPNET 帐户下运行的ASP.NET应用程序调用 DPAPI 将会失败。这是因为 ASPNET 帐户没有用户配置文件,它不能用于交互式登录。

如果将 ASP.NET Web 应用程序配置为模拟其调用方,ASP.NET 应用程序线程会有一个关联的线程模拟令牌。与该模拟令牌关联的登录会话是一种网络登录会话(在服务器上用来表示调用方)。网络登录会话不能使用户配置文件被加载。

要解决这个问题,您可以创建一个服务组件(在进程外 Enterprise Services (COM+) 服务器应用程序内)来调用 DPAPI。您可以确保用来运行组件的帐户具有用户配置文件,并且可以使用 Win32 服务来自动加载配置文件。

通过在该服务组件中调用 Win32 配置文件管理函数(LoadUserProfileUnloadUserProfile)可以避免使用 Win32 服务。

该方法有两个缺点。其一,一个请求一个请求地调用这些 API 将会严重影响性能;其二,这些 API 要求执行调用的代码在本地计算机上具有管理权限,这破坏了 Enterprise Services 进程帐户的最少权限原则。

图 5 显示 Enterprise Services DPAPI 解决方案。


5. ASP.NET Web 应用程序使用 COM+ 服务器应用程序与 DPAPI 交互

在图 5中,事件的运行时顺序如下:

1.

Windows 服务控制管理器启动 Win32 服务,并自动加载与该服务运行帐户关联的用户配置文件。同一 Windows 帐户用于运行 Enterprise Services 应用程序。

2.

Win32 服务对服务组件调用启动方法,该方法启动 Enterprise Services 应用程序并加载服务组件。

3.

该 Web 应用程序从 Web.config 文件检索加密字符串。

您可以利用 Web.config 中的 <appSettings> 元素来存储加密的字符串,如下所示。该元素支持任意的关键字-值对。

<configuration>
 <appSettings>
  <add key="SqlConnString"
       value="AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAABcqc/xCHxki3" />
 </appSettings>
</configuration>

您可以用以下代码行检索加密字符串:

string connString = ConfigurationSettings.AppSettings["SqlConnString"];

您可以使用 Web.config 或 Machine.config 来存储加密的连接字符串。 Machine.config 是首选项,因为它位于虚拟目录外的系统目录中。这将在下一节“使用 Web.config 和 Machine.config”中进一步介绍。

4.

该应用程序调用服务组件上的方法来解密连接字符串。

5.

服务组件与使用 P/Invoke 的 DPAPI 交互,以调用 Win32 DPAPI 函数。

6.

该解密字符串被返回到 Web 应用程序。

要在 Web.config 文件中存储加密的连接字符串,请首先编写一个接受该连接字符串的实用工具应用程序,并调用服务组件的 EncryptData 方法来获得加密字符串。当您用运行 Enterprise Services 服务器应用程序所使用的帐户来登录时,必须运行该实用工具。

直接从 ASP.NET 使用 DPAPI

如果您使用机器存储,并调用带有 CRYPTPROTECT_LOCAL_MACHINE 标记的 DPAPI 函数时,您可以直接从 ASP.NET Web 应用程序调用DPAPI。

但是,由于您使用机器存储,因此可以登录到计算机的任何 Windows 帐户都能够访问机密信息。一种减小风险的方法是增加熵,但这需要额外的密钥管理。

若不想在使用机器存储时使用熵,请考虑以下替代方法:

使用 ACL 限制对加密数据的访问(不论数据是存储在文件系统中还是存储在注册表中)。

考虑将熵参数硬编码到应用程序中,从而省去密钥管理麻烦。

更多信息

有关创建与 .NET Web 应用程序一起使用的 DPAPI 库的详细信息,请参见“How To Create a DPAPI Library”。

有关介绍如何直接从 ASP.NET 使用 DPAPI 的详细实施步骤,请参见“How To Use DPAPI (Machine Store) from ASP.NET”。

有关介绍如何从 Enterprise Services 使用 DPAPI 的详细实现步骤,请参见“How To Use DPAPI (User Store) from ASP.NET with Enterprise Services”。

有关用 DPAPI进行 Windows 数据保护的详细信息,请参见 MSDN 文章:“Windows Data Protection”。

使用 Web.config 和 Machine.config

建议不要以明文形式在 Web.config 中存储密码。默认情况下,HttpForbiddenHandler 可防止文件被怀有恶意的用户下载和查看。但是,直接对包含配置文件的文件夹拥有访问权限的用户仍然可以看到用户名和密码。

Machine.config 被视为一个比 Web.config 更安全的存储位置,因为它位于 Web 应用程序的虚拟目录之外的系统目录(带有 ACL)中。有关保护 Machine.config 的信息,请参见第 8 章“ASP.NET 安全性”。

使用 UDL 文件

OLE DB .NET 数据提供程序支持其连接字符串中的 UDL 文件名。要引用 UDL 文件,请在连接字符串中使用“File Name=name.udl”。

重要该选项仅在您使用 OLE DB .NET 数据提供程序连接到数据库时才可用。SQL Server .NET 数据提供程序不使用 UDL 文件。

建议不要在虚拟目录中将 UDL 文件和其他应用程序文件存储在一起。您应当将它们保存在 Web 应用程序的虚拟目录层次结构之外,然后利用 Windows ACL 保护该文件或包含该文件的文件夹。您还应当考虑将 UDL 文件存储在操作系统所在的逻辑卷之外的逻辑卷上,以防止可能发生的文件规范化和目录遍历错误。

ACL 粒度

如果在您在使用 UDL 文件(或任何文本文件)时应用 ACL,则比 Machine.config 提供更高的粒度。与 Machine.config 关联的默认 ACL 向各种各样的本地和远程用户授予访问权限。例如,Machine.config 包含以下默认的 ACLs:

MachineName/ASPNET:R
BUILTIN/Users:R
BUILTIN/Power Users:C
BUILTIN/Administrators:F
NT AUTHORITY/SYSTEM:F
  

比较起来,您可以进一步锁定自己的应用程序的 UDL 文件。例如,您可以将访问权限限制到管理员、系统帐户和 ASP.NET 进程帐户(该帐户需要读权限),如下所示。

BUILTIN/Administrators:F
MachineName/ASPNET:R
NT AUTHORITY/SYSTEM:F  

因为 UDL 文件可以从外部修改为任意 ADO.NET 客户端应用程序,所以每次打开连接时都会分析包含对 UDL 文件的引用的连接字符串。这会给性能带来影响,因此为获得最佳性能,建议您使用不包含 UDL 文件的静态连接字符串。

创建新的 UDL 文件

1.

使用 Windows 资源管理器并导航到您要在其中创建 UDL 文件的文件夹。

2.

右键单击文件夹,指向“New”,然后单击“Text Document”。

3.

提供一个带有 .udl 文件扩展名的文件名。

4.

双击新文件以显示“UDL Properties”对话框。

更多信息

有关从 Microsoft C# 开发工具程序使用 UDL 文件的详细信息,请参见“Microsoft 知识库”中的文章 Q308426:“HOW TO:Use Data Link Files with OleDbConnection Object in Visual C# .NET”。

使用自定义的文本文件

许多应用程序使用自定义的文本文件来存储连接字符串。如果您采用这种方式,请考虑以下建议:

将自定义文件存储在应用程序的虚拟目录层次结构之外。

考虑将文件存储在操作系统逻辑卷之外的逻辑卷上,以防止可能发生的文件规范化和目录遍历错误。

用受限制的 ACL 保护文件,该 ACL 只向您的应用程序的进程帐户授予读权限。

避免在文件中以明文形式存储连接字符串。应考虑使用 DPAPI 来存储加密的字符串。

使用注册表

您可以在 Windows 注册表中使用自定义项来存储连接字符串。存储的这些信息可以保存在 HKEY_LOCAL_MACHINE (HKLM) 或 HKEY_CURRENT_USER (HKCU) 注册表配置单元中。对于没有用户配置文件的进程标识,比如 ASPNET 帐户,这些信息必须存储在 HKLM 中,以便允许 ASP.NET 代码对其进行检索。

如果您使用此方法,则应当:

使用 ACL 来保护使用 Regedt32.exe 的注册表项。

在存储前对数据进行加密。

更多信息

有关在注册表中加密数据存储的详细信息,请参见 “How To Store an Encrypted Connection String in the Registry”。

使用 COM+ 目录

如果 Web 应用程序包含服务组件,您可以将连接字符串作为构造函数字符串存储在 COM+ 目录中。这样,这些字符串就很容易管理(使用“组件服务”工具),并且也很容易由组件代码检索。Enterprise Services 在实例化对象后就会调用该对象的 Construct 方法,并传递已配置的构造字符串。

COM+ 目录无法提供高度的安全性,因为信息没有加密;但是,与配置文件比较,由于增加了进程跳跃,因此它提高了安全门槛。

要禁止通过“组件服务”工具访问目录,应只将那些您想授权其权限的一组用户添加到 System 应用程序的 AdministratorReader 角色中。

以下示例说明如何从服务组件检索对象构造程序字符串。

 [ConstructionEnabled(Default="Default Connection String")]
public class YourClass : ServicedComponent 
{
  private string _ConnectionString;
  override protected void Construct(string s) 
  {
    _ConnectionString = s; 
  }
}
  

为增加安全性,您可以在存储之前通过添加代码对构造字符串进行加密,然后在服务组件中予以解密。

更多信息

有关使用连接字符串的详细信息,请参见“Microsoft 知识库”中的文章 Q271284:“HOWTO:Access COM+ Object Constructor String in a Visual Basic Component”。

有关 .NET Framework SDK 提供的完整代码示例,请参见位于以下目录中的对象构造函数示例: /Program Files/Microsoft Visual Studio .NET/FrameworkSDK/Samples/Technologies/ComponentServices/ObjectConstruction。

为数据库验证用户身份

如果您要构建需要某个数据库存储验证用户凭据的应用程序,请考虑以下几点:

存储单向密码哈希(使用随机的 salt 值)。

在验证用户凭据时避免 SQL 注入。

存储单向密码哈希(使用 Salt 值)

使用表单身份验证的 Web 应用程序通常需要在数据库中存储用户凭据(包括密码)。为安全起见,您不应当在数据库中存储密码(不管是明文的还是加密的)。

您应当避免存储加密的密码,因为这会带来密钥管理问题 –您可以用加密来保护密码安全,但是这样需要考虑如何存储加密密钥。如果密钥被破坏,则攻击者可以对数据存储中的所有密码解密。

首选的方式是:

存储密码的单向哈希。在需要验证密码时重新计算哈希。

联合使用密码哈希与 salt 值 (一个加密能力很强的随机数字)。通过联合使用 salt 值与密码哈希,您可以减轻字典攻击带来的威胁。

创建 Salt 值

下面的代码说明如何使用 System.Security.Cryptography 名字空间中的 RNGCryptoServiceProvider 类提供的随机数字生成功能来生成 salt 值。

public static string CreateSalt(int size)
{
  RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
  byte[] buff = new byte[size];
  rng.GetBytes(buff);
  return Convert.ToBase64String(buff);
}

创建哈希值(使用 Salt 值)

以下代码片段说明如何从提供的密码和 salt 值生成哈希值。

public static string CreatePasswordHash(string pwd£¬string salt)
{
  string saltAndPwd = string.Concat(pwd£¬salt);
  string hashedPwd = 
        FormsAuthentication.HashPasswordForStoringInConfigFile(
                                             saltAndPwd£¬"SHA1");
  return hashedPwd;
}

更多信息

有关此方法的完整实施细节,请参见“How To Use Forms Authentication with SQL Server 2000”。

SQL 注入式攻击

如果您要对 SQL 数据库使用表单身份验证,您应当采取本节介绍的预防措施来防范 SQL 注入式攻击。SQL 注入式攻击是将其他的(恶意的)SQL 代码传递到某个应用程序中的行为,该代码通常被附加到该应用程序包含的合法 SQL 代码中。所有 SQL 数据库都在不同程度上容易遭受 SQL 注入式攻击,但本章介绍的重点是在 SQL Server 数据库上。

当您处理作为 SQL 命令组成部分的用户输入时,应当特别注意可能发生的 SQL 注入式攻击。如果您的身份验证方案用于为 SQL 数据库验证用户,比如,您对 SQL Server 使用表单身份验证,则必须防止 SQL 注入式攻击。

如果您使用未筛选的输入构建 SQL 字符串,则应用程序可能会遭受恶意用户输入(注意,千万不要信任用户输入)的攻击。危险在于,当您将用户输入插入一个将要成为可执行语句的字符串中时,恶意用户可以利用转义符将 SQL 命令附加到您想用的 SQL 语句中。

下面几节中的代码片段使用 SQL Server 附带的 Pubs 数据库来说明 SQL 注入式攻击的示例。

问题

当您将用户输入或其他未知数据添加到数据库查询中时,您的应用程序会很容易受到 SQL 注入式攻击。例如,下面的两个代码片段都容易遭受攻击。

您用未筛选的用户输入构建的 SQL 语句。

SqlDataAdapter myCommand = new SqlDataAdapter(
          "SELECT au_lname£¬au_fname FROM authors WHERE au_id = '" + 
          Login.Text + "'", myConnection);

您通过构建单个字符串来调用存储过程,该字符串包含未筛选的用户输入。

SqlDataAdapter myCommand = new SqlDataAdapter("LoginStoredProcedure '" + 
                               Login.Text + "'", myConnection);

SQL 脚本注入式攻击的剖析

当您在应用程序中接受未筛选的用户输入值(见上文)时,恶意用户可以使用转义符来添加他们自己的命令。

试考虑这样一个 SQL 查询,该查询希望用户的输入是一个社会保障号码,比如 172-32-xxxx,最后查询形式如下所示:

SELECT au_lname, au_fname FROM authors WHERE au_id = '172-32-xxxx'  

恶意用户可以将以下文字输入您的应用程序输入字段(例如,文本框控件)中。

' ; INSERT INTO jobs (job_desc, min_lvl, max_lvl) VALUES ('Important Job', 25, 100)  -  

此示例中注入了一个 INSERT 语句(但是用于连接到 SQL Server 的帐户所允许的任何语句都可以执行)。如果该帐户是 sysadmin 角色的成员(它允许执行使用 xp_cmdshell的 shell 命令),并且 SQL Server 使用的域帐户拥有其他网络资源的访问权限,则该代码的破坏性尤其大。

上述命令生成以下组合 SQL 字符串:

SELECT au_lname, au_fname FROM authors WHERE au_id = '' ; INSERT INTO jobs (job_desc, min_lvl, max_lvl) VALUES ('Important Job', 25, 100) --  

在这种情况下,恶意输入开头的 '(单引号)字符中止了您的 SQL 语句中的当前字符串。它仅在以下情况下才关闭当前语句:下面分析的标记的意思不是当前语句的继续标记,而是一个新语句的开始标记。

SELECT au_lname, au_fname FROM authors WHERE au_id = ' '  

;(分号)字符告诉 SQL 您正在开始一个新语句,而紧跟在后面的就是恶意 SQL 代码:

; INSERT INTO jobs (job_desc, min_lvl, max_lvl) VALUES ('Important Job', 25, 100)  

分开 SQL 语句不一定需要分号。这与供应商/实施方法有关,但 SQL Server 不需要。例如,SQL Server 将以下语句分析为两个独立的语句:

SELECT * FROM MyTable DELETE FROM MyTable

最后,--(双短划线)字符序列是一个 SQL 注释符号,它告诉 SQL 忽略文本其余部分,在此例中就是忽略 '(单引号)这个结束字符(否则它会导致 SQL 分析器错误)。

作为上述语句的结果,SQL 执行的全部文本是:

SELECT au_lname, au_fname FROM authors WHERE au_id = '' ; INSERT INTO jobs (job_desc,min_lvl,max_lvl) VALUES ('Important Job',25,100) --'

解决方案

以下几种方法可用来从应用程序安全调用 SQL。

在构建 SQL 语句时使用 Parameters 集合。

SqlDataAdapter myCommand = new SqlDataAdapter(
        "SELECT au_lname, au_fname FROM Authors WHERE au_id= @au_id", 
        myConnection);

SqlParameter parm = myCommand.SelectCommand.Parameters.Add(
                                             "@au_id",
                                             SqlDbType.VarChar, 11);
parm.Value= Login.Text;  

在调用存储过程时使用 Parameters 集合。

// AuthorLogin is a stored procedure that accepts a parameter named Login
SqlDataAdapter myCommand = new SqlDataAdapter("AuthorLogin", myConnection);
myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parm = myCommand.SelectCommand.Parameters.Add(
                                "@LoginId", SqlDbType.VarChar,11);
parm.Value=Login.Text;  

如果您使用 Parameters 集合,不管恶意用户在输入中包含什么内容,该输入都会被按文本处理。使用 Parameters 集合的另一好处是您可以执行类型和长度检查。超出范围的值触发异常。这是一个安全的纵深防御示例。

从用户输入中筛选 SQL 字符。下面的方法说明如何确保在简单 SQL 比较(等于、小于、大于)语句中使用的任何字符串都是安全的。它是通过确保在字符串中使用的任何撇号再附加一个撇号进行转义来做到这一点的。在 SQL 字符串中,两个连续的撇号被视为字符串内的撇号字符实例,而不是分隔符。

private string SafeSqlLiteral(string inputSQL)
{
  return inputSQL.Replace("'", "''");
}
…
string safeSQL = SafeSqlLiteral(Login.Text);
SqlDataAdapter myCommand = new SqlDataAdapter(
       "SELECT au_lname, au_fname FROM authors WHERE au_id = '" + 
       safeSQL + "'", myConnection);

其他最佳做法

下面是用于减少安全漏洞以及用于将可能造成的损失限制在一定范围内的其他一些措施:

通过限制输入的大小和类型在关口(前端应用程序)防止输入非法内容。通过限制输入的大小和类型,可大大减小危害的可能性。例如,如果数据库查看字段是十一个字符长并且全部由数字组成,则强制实现这一规则。

用具有最少权限的帐户运行 SQL 代码。这可大大减小可能造成的损失。

例如,如果用户要注入 SQL 以删除数据库的表,但是 SQL 连接所使用的帐户没有相应的权限,则 SQL 代码将失败。这是不应将 sa 帐户、 sysadmindb_owner 的成员用于应用程序的 SQL 连接的又一原因。

在 SQL 代码中出现异常错误时,不要向最终用户披露数据库引起的 SQL 错误。记录错误信息,并只显示用户友好信息。这可避免泄露可能对攻击者有所帮助的不必要的详细信息。

保护模式匹配语句

如果输入将在 LIKE子句的字符串中使用,则字符(撇号除外)还具有特殊的模式匹配含义。

例如,在 LIKE 子句中,% 字符表示“匹配零或多个字符”为将输入中的此类字符作为没有特殊含义的文字字符,它们也需要进行转义。如果不对它们进行特殊处理,则查询可能返回错误结果,在字符串开头或开头附近的非转义模式匹配字符可能也会破坏索引。

对于 SQL Server,应当使用以下方法来确保输入的内容有效:

private string SafeSqlLikeClauseLiteral(string inputSQL)
{
  // Make the following replacements:
  // '  becomes  ''
  // [  becomes  [[]
  // %  becomes  [%]
  // _  becomes  [_]

  string s = inputSQL;
  s = inputSQL.Replace("'", "''");
  s = s.Replace("[", "[[]");
  s = s.Replace("%", "[%]");
  s = s.Replace("_", "[_]");
  return s;
}    

审核

在默认情况下,SQL Server 中的登录审核功能并不打开。您可以使用 SQL Server 企业管理器或使用注册表配置审核功能。图 6 中的对话框显示为用户的登录成功或失败启用审核。

日志条目写入 SQL 日志文件,在默认情况下该文件位于以下位置:C:/Program Files/Microsoft SQL Server/MSSQL/LOG。您可以使用任何文本阅读器(比如 Notepad)来查看它们。


6. 包含审核级设置的“SQL Server 属性”对话框

您还可以在注册表中启用 SQL Server 审核功能。要启用 SQL Server 审核功能,在注册表中创建以下 AuditLevel 项,将其值设置为下面指定的 REG_DWORD 值之一。

HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/MSSQLServer/AuditLevel

您可以从以下值中选择一个,从而使您能够捕获您想要的细节级别:

3—既捕获成功的登录尝试也捕获失败的登录尝试

2—只捕获失败的登录尝试

1—只捕获成功的登录尝试

0—不捕获任何登录尝试

建议您打开失败登录审核功能,因为这是一个判断是否有人试图恶意攻击 SQL Server 的途径。对失败的审核尝试进行记录对性能的影响很小,除非您正在遭受攻击,而在这种情况下您必须要知道这些尝试情况。

您还可以对 SQL 数据库管理对象 (DMO) 编写脚本。下面的代码片段显示一些 VBScript 代码示例。

Sub SetAuditLevel(Server As String£¬NewAuditLevel As SQLDMO_AUDIT_TYPE)
    Dim objServer As New SQLServer2  
    objServer.LoginSecure = True     'Use integrated security
    objServer.Connect Server        'Connect to the target SQL Server
    'Set the audit level
    objServer.IntegratedSecurity.AuditLevel = NewAuditLevel       
    Set objServer = Nothing
End Sub

查阅 SQL Server 联机图书得知,枚举类型 SQLDMO_AUDIT_TYPE 的成员包括:

SQLDMOAudit_All      3  Log all authentication attempts regardless of success
                        or failure 
SQLDMOAudit_Failure  2    Log failed authentication 
SQLDMOAudit_Success  1    Log successful authentication
SQLDMOAudit_None     0    Do not log authentication attempts

SQL Server 的进程标识

使用拥有最少权限的本地帐户运行 SQL Server。安装 SQL Server 时,您可以选择使用本地 SYSTEM 帐户或指定的帐户运行 SQL Server 服务。

不要使用 SYSTEM 帐户或管理员帐户。而应使用拥有最少权限的本地帐户。您无须向此帐户授予任何特定的权限,因为安装进程(或 SQL Server 企业管理器,如果您在安装后重新配置了 SQL 服务)向指定的帐户授予了必要的权限。

如果 SQL Server 需要访问远程计算机,比如进行网络备份和恢复或者复制,则您需要使用一个最少权限的域帐户或者用相同的用户名和密码在远程服务器上创建一个重复的本地帐户。密码同步可用脚本实现。如果要访问处于其它没有信任关系的域中的服务器,则需要使用创建重复帐户这一方法。

小结

下面是一个总结,主要为 .NET Web 应用程序中的数据访问提供一些建议:

尽可能为 SQL Server 使用 Windows 身份验证。

在数据库中使用具有最少权限的帐户。

在连接到 SQL Server 时使用最少权限的本地帐户来运行 ASP.NET/Enterprise Services。

授权时使用数据库中用户自定义的数据库角色。

如果您使用的是 SQL 身份验证,则采取以下步骤来提高安全性:

使用拥有强密码的自定义帐户。

使用数据库角色来限制 SQL Server 中每个帐户的权限。

向任何用于存储连接字符串的文件添加 ACL。

对连接字符串进行加密。

考虑在凭据存储中使用 DPAPI。

当您对 SQL 使用表单身份验证时,采取预防措施来防范 SQL 注入式攻击。

不要将用户密码存储在数据库中供用户验证。应使用 salt 值存储密码哈希,而不要采用明文密码或加密密码。

保护通过网络发送给 SQL Server 或从 SQL Server 中发出的机密数据。

Windows 身份验证可保护凭据,但不能保护应用程序数据。

使用 IPSec 或 SSL。

转到原英文界面


 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值