大多数 ASP.NET Web 应用程序都涉及数据访问。许多应用程序都会收集数据并将其存储在数据库或文件中,要存储的数据通常基于来自用户的信息。由于原始数据可能来自不受信任的来源,信息是以持久格式存储的,并且您希望确保未经授权的用户不能直接访问您的数据源,因而您需要特别注意与数据访问有关的安全问题。本主题中所介绍的最佳操作将帮助您提高 ASP.NET Web 应用程序中的数据访问的安全性。
尽管遵循编码和配置最佳做法可以提高应用程序的安全性。但还有一点也很重要,那就是应经常执行 Microsoft Windows 和 Internet 信息服务 (IIS) 的最新安全更新以及 Microsoft SQL Server 或其他数据源软件的所有安全更新,以使您的 Web 服务器始终保持在最新状态。
有关编写安全代码和保护应用程序的最佳做法的更详细信息,请参见 Michael Howard 和 David LeBlanc 合著的“Writing Secure Code”(《编写安全代码》)一书,也可以参见“Microsoft Patterns and Practices”(Microsoft 模式与实践)网站中提供的指南。
保护数据源访问的安全
以下各节提供了有关帮助保护数据访问的不同方面的信息。
连接字符串
连接到数据库需要连接字符串。由于连接字符串可能包含敏感数据,因此您应当遵循以下准则:
-
不要将连接字符串存储在页面中。例如,应避免通过声明
-
不要以纯文本形式存储连接字符串。为了确保与数据库服务器之间的连接的安全性,建议您使用受保护的配置来对配置文件中的连接字符串信息进行加密。有关更多信息,请参见
使用集成安全性连接到 SQL Server
如果可能,请使用集成安全性,而不要使用显式的用户名和密码连接到 SQL Server 实例。这有助于避免危及连接字符串的安全以及泄漏用户 ID 和密码。
建议您确保运行 ASP.NET 的进程(例如应用程序池)的标识是默认进程帐户或受限用户帐户。有关更多信息,请参见
如果不同的网站连接到不同的 SQL Server 数据库,那么使用集成安全性可能并不实际。例如,在 Web 宿主网站中,通常会为每个客户分配一个不同的 SQL Server 数据库,但所有用户均以匿名用户的身份使用 Web 服务器。在这种情况下,您需要使用显式凭据来连接到 SQL Server 实例。请确保以安全的方式存储凭据,如本主题的连接字符串中所述。
SQL Server 数据库权限
建议您为用来连接到应用程序所使用的 SQL Server 数据库的用户 ID 分配最低特权。
限制 SQL 操作
数据绑定控件可以支持各种数据操作,包括在数据表中选择、插入、删除和更新记录等。建议您将数据控件配置为仅执行页上或应用程序中所需的最低功能。例如,如果控件不应该允许用户删除数据,则不要在数据源控件中包括删除查询,也不要在控件中启用删除功能。
SQL Server Express Edition
在将某个进程附加到 SQL Server Express Edition 数据库(.mdf 文件)时,该进程必须具备管理权限。通常情况下,这种做法使得 SQL Server Express Edition 数据库不适合用在成品网站上,因为 ASP.NET 进程不会(也不应当)使用管理特权运行。因此,SQL Server Express Edition 数据库只能用于下面的情况中:
-
在开发 Web 应用程序时用作测试数据库。在准备好部署应用程序时,可以将数据库从 SQL Server Express Edition 转移到 SQL Server 的成品实例中。
-
如果您正在运行可以使用模拟功能的网站并且可以控制所模拟的用户的特权,那么可以使用该版本。实际上,此策略仅当应用程序运行于局域网(而非公共网站)上时才可行。
-
将 .mdf 文件存储在站点的 App_Data 文件夹中,因为该文件夹的内容不会返回给直接的 HTTP 请求。还应在 IIS 中将 .mdf 扩展名映射到 ASP.NET,并在站点的 Web.config 文件中使用以下元素将该扩展名映射到 ASP.NET 中的 HttpForbiddenHandler 处理程序:
<httpHandlers> <add verb="*" path="*.mdf" type="System.Web.HttpForbiddenHandler" /></httpHandlers>
有关如何在 IIS 中将文件扩展名映射到 ASP.NET 的信息,请参见如何:注册 HTTP 处理程序。
Microsoft Access 数据库
Microsoft Access 数据库(.mdb 文件)所包括的安全功能比 SQL Server 数据库少。对于成品网站,建议您不要使用 Access 数据库。但是,如果您确实需要在 Web 应用程序中使用 .mdb 文件,请遵循以下准则:
-
将 .mdb 文件存储在站点的 App_Data 文件夹中,因为该文件夹的内容不会返回给直接的 HTTP 请求。还应在 IIS 中将 .mdb 扩展名映射到 ASP.NET,并在站点的 Web.config 文件中使用以下元素将该扩展名映射到 ASP.NET 中的 HttpForbiddenHandler 处理程序:
<httpHandlers> <add verb="*" path="*.mdb" type="System.Web.HttpForbiddenHandler" /></httpHandlers>
有关如何在 IIS 中将文件扩展名映射到 ASP.NET 的信息,请参见如何:注册 HTTP 处理程序。
-
为读写 .mdb 文件的用户帐户添加适当的权限。如果网站支持匿名访问,这通常是本地 ASPNET 用户帐户或 NETWORK SERVICE 帐户。由于 Access 必须创建一个 .ldb 文件以支持锁定,因此用户帐户必须对包含 .mdb 文件的文件夹具备写权限。
-
如果数据库采用密码保护,那么不要使用 AccessDataSource 控件来建立与数据库的连接,因为 AccessDataSource 控件不支持凭据的传递。在这种情况下,应使用 ODBC 提供程序和 SqlDataSource 控件,并在连接字符串中传递凭据。请务必按照本主题的连接字符串中的说明来保护连接字符串的安全。
XML 文件
如果将数据存储在 XML 文件中,则应将 XML 文件放在网站的 App_Data 文件夹中,因为该文件夹的内容不会返回给直接的 HTTP 请求。
防止恶意用户输入
如果应用程序要接受用户输入,则需要确保输入中不包含可能危及应用程序的恶意内容。恶意用户输入可用于发动下面的攻击:
-
脚本注入 脚本注入攻击试图向应用程序发送可执行的脚本,意欲使其他用户运行该脚本。典型的脚本插入攻击是向数据库中存储脚本的页发送脚本,以使查看数据的其他用户在不经意间运行该代码。
-
SQL 注入 SQL 注入攻击试图创建 SQL 命令以取代或扩充应用程序内置的命令,从而危及数据库(可能还有运行数据库的计算机)的安全。
通用准则
对于所有用户输入,请遵循以下准则:
-
尽可能使用验证控件,以限定用户输入可接受的值。
-
在运行服务器代码之前,请始终确保 IsValid 属性的值为 true。如果值为 false,则意味着一个或多个验证控件未通过验证检查。
-
应始终执行服务器端验证(即使浏览器也执行客户端验证)以防止用户跳过客户端验证环节。对于 CustomValidator 控件尤其应如此;不要使用“仅客户端验证”逻辑。
-
始终在应用程序的业务层再次验证用户输入。不要依赖于调用进程来提供安全的数据。例如,如果正在使用 ObjectDataSource 控件,则可向执行数据更新的对象添加冗余验证和编码。
脚本注入
若要避免脚本注入攻击,请遵循以下准则:
-
采用 HtmlEncode 方法对用户输入进行编码,该方法可将 HTML 转换为文本表示形式(例如,将 <b> 转换为 <b>),这有助于防止在浏览器中执行标记。
-
在使用参数对象将用户输入传递给查询时,可以为数据源控件的预查询事件添加处理程序并在这些事件中进行编码。例如,如果处理 SqlDataSource 控件的 Inserting 事件,可以在该事件中,在执行查询之前对参数值进行编码。
-
如果正在使用带绑定字段的 GridView 控件,则可将 BoundField 对象的 HtmlEncode 属性设置为 true。这会使 GridView 控件在行处于编辑模式下时对用户输入进行编码。
-
对于可以进入编辑模式的控件,建议您使用模板。例如,GridView、DetailsView、FormView、DataList 和 Login 控件可以显示可编辑的文本框。但是,除 GridView 控件之外(请参见上一条),这些控件不会自动验证用户输入或对用户输入进行 HTML 编码。因此,建议您为这些控件创建模板,在模板中包括输入控件(例如 TextBox 控件)并添加验证控件。此外,在提取控件的值时,应对其进行编码。
SQL 注入
若要避免 SQL 注入攻击,请遵循以下准则:
-
不要通过将字符串(尤其是那些包括了用户输入的字符串)串联在一起来创建 SQL 命令,而应当使用参数化查询或存储过程。
-
如果要创建参数化查询,则可使用参数对象来建立参数的值。有关详细信息,请参见对 SqlDataSource 控件使用参数和对数据源控件使用参数。
加密视图状态数据
数据绑定控件(例如 GridView 控件)有时需要保存被视为敏感内容的信息。例如,GridView 控件可能要在 DataKeys 属性中维护一个键的列表,即使该信息并不显示。在往返行程之间,控件会将该信息存储在视图状态中。
视图状态信息进行了编码,并与页的内容一起存储,未经授权的源无法解码和查看视图状态信息。如果必须在视图状态中存储敏感信息,可以要求页对视图状态数据进行加密。若要加密数据,请将页的 ViewStateEncryptionMode 属性设置为 true。有关更多信息,请参见保证视图状态的安全。
缓存
在ASP.NET中防止注入攻击
- 对输入的字串长度,范围,格式和类型进行约束.
- 在开发ASP.NET程序时使用请求验证防止注入攻击.
- 使用ASP.NET验证控件进行输入验证.
- 对不安全的输出编码.
- 使用命令参数集模式防止注入攻击.
- 防止错误的详细信息被返回到客户端.
概述 :
你应该在程序中验证所有的不信任输入.你应该假定所有的用户输入都是非法的.用户可以在应用程序中提供表单字段,查询字串,客户端cookies和浏览器环境值比如用户代理字串和IP地址等.
弱输入校验通常为注入攻击提供了机会.下面是常见的利用弱输入校验或无输入校验进行攻击的手段. - SQL 注入(SQL injection). 如果你使用用户的输入值来动态构造SQL语句,那么数据库可能执行攻击性的有害SQL语句.
- 跨站脚本(Cross-site scripting). 跨站脚本攻击利用网页验证漏洞注入客户端脚本.接下来这些代码被发送到受信任的客户端电脑上并被浏览器解释执行.因为这些代码来自受信任的站点,所以浏览器无法得知这些代码是有害的.
- 未授权的文件访问(Unauthorized file access).如果你的代码从调用者那里接受输入,恶意用户可以看到你对文件的操作过程从而访问那些受保护的文件或者使用你的代码注入非法数据.
注意 : 注入攻击可通过使用HTTP或HTTPS Secure Socket Layer(SSL) 连接. 传输加密技术不能用来防御攻击.
通常的输入验证方法总结如下.你应在所有的需要通过网络输入的地方进行验证,比如文本框和其它表单输入字段, 查询字串参数,cookies,服务器端变量和网络方法参数.注意,过滤策略应该是只允许正确的输入然后拒绝非法输入.这是因为定义正确的输入策略比过滤所有的非法输入要容易,那通常很难包括所有的非法输入.
通入如下几个方面验证输入内容: - 约束.验证是否输入的是正确的类型,字符长度,格式和范围.可以应用ASP.NET验证控件来约束服务器控件输入.约束其它来源的输入可以使用正则表达式和自定义的验证规则.
- 拒绝.检测已知的有害数据输入并拒绝.
- 过滤.有时候你会希望过滤掉用户输入中那些有安全隐患的那些部分.例如,你的程序允许自由格式的输入,比如备注字段,你会允许特定的安全HTML标记象<b>,<i>及其它的HTML标记.
步骤提要
通过以下步骤保护你的ASP.NET程序不受注入式攻击危害 : - 第一步.使用ASP.NET请求验证.
- 第二步.约束输入.
- 第三步.对不安全的输出进行编码.
- 第四步.对SQL查询语句使用命令参数.
- 第五步.验证ASP.NET的出错信息没有泄漏至客户端.
下面的章节将对这些步骤进行详细讨论.
第一步.使用ASP.NET请求验证.
默认地,ASP.NET 1.1和2.0请求验证会对送至服务器的数据检测是否含有HTML标记元素和保留字符.这可以防止用户向程序中输入脚本.请求验证会对照一个有潜在威胁的字符串列表进行匹配,如果发现异常它会抛出一个HttpRequestValidationException类型的异常.
你可以在你的web.config文件中的<pages>元素中加入validateRequest="false" 或在单独的页面的@Pages元素里面设置ValidateRequest = "false"来禁用此项功能.
如果你想禁用请求验证功能,你可以仅在需要的页面禁用它.比如你在程序页面上包含一个可接受HTML格式输入的字段.
确定在Machine.config文件中请求验证功能被打开.
请求验证功能在ASP.NET中被默认启用.你可以在Machine.config.comments文件中看到如下的默认设置.
<pages validateRequest = "true" ... />
确认你没有修改你的服务器的Machine.config和应用程序的Web.config文件里的默认设置.
测试ASP.NET请求验证
你可以测试请求验证的作用.创建一个ASP.NET页面通过设置ValidateRequest = "fasle"禁用请求验证,代码如下 :<%@ Language="C#" ValidateRequest="false" %>
<html>
<script runat="server">
void btnSubmit_Click(Object sender, EventArgs e)
{
//
If ValidateRequest is false, then 'hello' is displayed
//
If ValidateRequest is true, then ASP.NET returns an exceptionResponse.Write(txtString.Text);
}
</script>
<body>
<form id="form1" runat="server">
<asp:TextBox id="txtString" runat="server"
Text="<script>alert('hello');</script>" />
<asp:Button id="btnSubmit" runat="server" OnClick="btnSubmit_Click"
Text="Submit" />
</form>
</body>
</html>
当你运行页面的时候,"Hello"被显示在一个消息框中,因为在txtString中的脚本被执行并被客户端的浏览器处理.
如果你设置ValidateRequest = "true" 或者移除ValidateRequest页面属性,ASP.NET请求验证会拒绝脚本输入并抛出一个象下面这样的错误信息.
A potentially dangerous Request.Form value was detected from the client (txtString="<script>alert('hello").