编写安全的ADO.NET代码

来源:互联网 发布:sqlserver收缩日志文件 编辑:程序博客网 时间:2024/05/14 05:56

安全的 ADO.NET 连接
在处理应用程序的安全性时,最重要的目标之一是保护对数据源的访问。为了限制对数据源的访问,请务必保持用户标识、密码和数据源名称等连接信息的私密性。以下是确保关键连接信息保持私密性的指南。

避免以纯文本形式存储用户标识和密码
以纯文本形式存储用户标识和密码将造成严重的安全漏洞。如果用户标识和密码是源代码的一部分,那么一旦源代码的安全受到威胁,用户标识和密码比任何时候都容易遭受攻击。即使向外部源提供的是代码的编译版本,编译的代码也可能被反汇编,从而用户标识和密码也被公开。因此,关键信息如用户标识和密码决不能以纯文本形式存在于代码中。

要保持用户标识和密码信息的私有性,您可以选择使用密码系统(请参见加密服务),但是在存储关键信息,以及与应用程序分离、而通过 NTFS 权限得到了严密保护的秘密信息时,您仍然要小心。

连接到 Microsoft SQL Server 时,也可以选择使用“集成安全性”,它使用当前活动用户的标识,而不是传递用户标识和密码。. 强烈建议使用“集成安全性”。

注意   ASP.NET 开发人员在使用“集成安全性”时需要特别注意。有关在 ASP.NET 应用程序中控制当前活动用户标识的信息,请参见 ASP.NET 模拟。
保护通用数据链接文件
可以使用“通用数据链接”(UDL) 文件为 OleDbConnection 提供连接信息。由于 UDL 文件对于您的应用程序来说是外部资源,因此应使用“新技术文件系统”(NTFS) 文件权限保护 UDL 文件,防止连接信息被公开或修改。而且,请务必提供 UDL 文件的完全限定路径,以确保对连接使用正确的 UDL 文件。

UDL 文件没有加密。如果您想使用密码进一步保护连接信息的安全性,您不能使用 UDL 文件来提供连接字符串信息。

保持 Persist Security Info 为 False
将 Persist Security Info 设置为 true 或 yes 将导致在打开连接后,从连接中获得涉及安全性的信息(包括用户标识和密码)。如果在连接时要提供用户标识和密码,则最安全的做法是,使用这些信息打开连接后立即丢弃它们。因此,有助于获得更好的安全性的选择就是将 Persist Security Info 设置为 false 或 no。

当您向不可信的源提供打开的连接,或将连接信息保存到磁盘时,这点尤其重要。如果将 Persist Security Info 保持为 false,可帮助确保不可信的源无法访问连接中涉及安全性的信息,并帮助确保任何涉及安全性的信息都不会随连接字符串信息保存到磁盘中。

Persist Security Info 的默认设置为 false。

在从用户输入构造连接字符串时使用警告
当采用来自外部源(如提供用户标识和密码的用户)的连接字符串信息时,您必须注意确保用于构造连接字符串的值不含有会更改连接行为的附加连接字符串参数。为了保证连接字符串的安全,请验证来自外部源的所有输入,以确保它们遵循了正确的格式。

验证输入
可以使用正则表达式验证输入与特定的格式是否匹配。.NET Framework 提供了 Regex 对象,以根据正则表达式来验证值。例如,以下代码用于确保用户标识值是一个 8 字符的字母字符串。

[Visual Basic]
Public Static Function ValidateUserid(inString As String) As Boolean
  Dim r As Regex = New Regex("^[A-Za-z0-9]{8}___FCKpd___0quot;)
  Return r.IsMatch(inString)
End Function
[C#]
public static bool ValidateUserid(string inString)
{
  Regex r = new Regex("^[A-Za-z0-9]{8}___FCKpd___0quot;);
  return r.IsMatch(inString)
}安全的 ADO.NET 编码指南
保证应用程序的安全包括编写安全的代码。代码必须只公开客户端代码所需要的信息和功能。与 ADO.NET 相关的常见攻击是 SQL Insertion 攻击,它从应用程序返回的异常中来确定私有数据库信息。

避免 SQL Insertion 攻击
在 SQL Insertion 攻击中,攻击者在您的命令中插入在数据源位置执行处理的其他 SQL 语句。这些命令不仅可以修改或破坏数据源位置的信息,还可以检索您的私有信息。将命令字符串与外部输入串联在一起的代码容易受到 SQL Insertion 攻击。例如,以下代码容易受到 SQL Insertion 攻击。

[Visual Basic]
' Retrieve CustomerID to search for from external source.
Dim custID As String = GetCustomerID()

' The following line of code allows for SQL Insertion attack.
Dim selectString As String = "SELECT * FROM Customers WHERE CustomerID = " & custID

Dim cmd As SqlCommand = New SqlCommand(selectString, conn)
conn.Open()
Dim myReader As SqlDataReader = cmd.ExecuteReader()
' Process results.
myReader.Close()
conn.Close()
[C#]
// Retrieve CustomerID to search for from external source.
string custID = GetCustomerID();

// The following line of code allows for SQL Insertion attack.
string selectString = "SELECT * FROM Customers WHERE CustomerID = " + custID;

SqlCommand cmd = new SqlCommand(selectString, conn);
conn.Open();
SqlDataReader myReader = cmd.ExecuteReader();
' Process results.
myReader.Close();
conn.Close();攻击者可为要查询的 CustomerID 输入一个值“1;DROP TABLE Customers”。这会导致为此查询执行以下命令。

SELECT * FROM Customers WHERE CustomerID = 1;DROP TABLE Customers为了防止 SQL Insertion 攻击,请验证来自外部源的输入,并传递列值作为参数,而不是串联这些值来创建 SQL 语句。

验证输入
可以使用正则表达式验证输入与特定的格式是否匹配。.NET Framework 提供了 Regex 对象,以根据正则表达式来验证值。例如,以下代码用于确保值为 5 个字符的字母字符串。

[Visual Basic]
Public Static Function Validate(inString As String) As Boolean
  Dim r As Regex = New Regex("^[A-Za-z0-9]{5}___FCKpd___3quot;)
  Return r.IsMatch(inString)
End Function
[C#]
public static bool Validate(string inString)
{
  Regex r = new Regex("^[A-Za-z0-9]{5}___FCKpd___3quot;);
  return r.IsMatch(inString)
}使用参数
参数提供了一种有效的方法来组织随 SQL 语句传递的值,以及向存储过程传递的值。另外,通过确保从外部源接收的值仅作为值传递,而不是作为 SQL 语句的一部分传递,可以防止参数受到 SQL Insertion 攻击。因此,在数据源处不会执行插入到值中的 SQL 命令。相反,所传递的这些值仅仅被视为参数值。以下代码显示了使用参数传递值的一个示例。

[Visual Basic]
' Retrieve CustomerID to search for from external source.
Dim custID As String = GetCustomerID()

Dim selectString As String = "SELECT * FROM Customers WHERE CustomerID = @CustomerID"

Dim cmd As SqlCommand = New SqlCommand(selectString, conn)
cmd.Parameters.Add("@CustomerID", SqlDbType.VarChar, 5).Value = custID

conn.Open()
Dim myReader As SqlDataReader = cmd.ExecuteReader()
' Process results.
myReader.Close()
conn.Close()
[C#]
// Retrieve CustomerID to search for from external source.
string custID = GetCustomerID();

string selectString = "SELECT * FROM Customers WHERE CustomerID = @CustomerID";

SqlCommand cmd = new SqlCommand(selectString, conn);
cmd.Parameters.Add("@CustomerID", SqlDbType.VarChar, 5).Value = custID;

conn.Open();
SqlDataReader myReader = cmd.ExecuteReader();
' Process results.
myReader.Close();
conn.Close();保持异常信息的私有性
攻击者经常利用来自某次异常的信息,例如服务器、数据库或表的名称来发起对系统的特定攻击。因为异常可能会包含关于应用程序或数据源的特定信息,所以,如果您只向客户端公开所需要的信息,则可使应用程序和数据源更为安全。

为了避免通过异常公开私有信息,请不要将系统异常的内容返回用户。相反,要在内部处理异常。如果必须向用户发送消息,则返回您包含最少信息的自定义消息(例如“连接失败。请与系统管理员联系。”),并记录特定信息以便于管理员使用。

例如,以下代码捕获打开连接时的异常,并将异常写入事件日志。

[Visual Basic]
Dim conn As SqlConnection = New SqlConnection("Data Source=localhost;Initial Catalog=Northwind;")

Try
  conn.Open()

Catch e As SqlException
  Dim log As System.Diagnostics.EventLog = New System.Diagnostics.EventLog()
  log.Source = "My Application"
  log.WriteEntry(e.ToString())

  If conn.State <> ConnectionState.Open Then _
    Console.WriteLine("Connection was not opened.")

Finally
  conn.Close()
End Try
[C#]
SqlConnection conn = new SqlConnection("Data Source=localhost;Initial Catalog=Northwind;");

try
{
  conn.Open();
}
catch (SqlException e)
{
  System.Diagnostics.EventLog log = new System.Diagnostics.EventLog();
  log.Source = "My Application";
  log.WriteEntry(e.ToString());

  if (conn.State != ConnectionState.Open)
    Console.WriteLine("Connection was not opened.");
}
finally
{
  conn.Close();

代码访问安全性和 ADO.NET
Windows 安全性使您能够根据用户的角色来保证资源的安全。例如,管理员可以具有不受限制的权限,而来宾的权限则受到限制。.NET Framework 包含一个称为代码访问安全性的安全机制,它能够帮助您更好地保护应用程序的安全。使用“代码访问安全性”时,将根据代码的来源应用不同的权限。代码的来源由程序集的 Zone 属性和“代码访问安全性”策略中为该区域标识的权限来标识。例如,某台计算机上存在的代码被标识为在 MyComputer 区域中。而从本地 Intranet 上的另一台计算机下载的代码则被标识为在 LocalIntranet 区域中。可以为各个区域应用不同的权限集,更严密地保证 LocalIntranet 区域中外部代码的安全,而使 MyComputer 区域中的安全性具有较少的限制。

可以使用权限集标识针对特定区域的限制。.NET Framework 提供了若干默认权限集,您也可以创建自定义权限集,并将其标识为某特定区域的权限集。

权限集通常分为三类:完全信任、部分信任和不可信。完全信任表明,该代码没有限制,且默认情况下可用于本地计算机和具有强名称的源。标识为不具有任何权限集的区域被认为不可信,而且只能使用不需要任何权限的功能。部分信任权限集包含适用于特定区域的权限和限制的组合。

OLE DB .NET Framework 数据提供程序、ODBC .NET Framework 数据提供程序和 Oracle .NET Framework 数据提供程序只在具有 FullTrust 权限的区域中运行时才被认为是安全的。在权限级别低于 FullTrust 的区域中使用 OLE DB 或 ODBC 提供程序的任何尝试都会导致 SecurityException。用于 OLE DB、ODBC 和 Oracle 的数据提供程序在链接时和运行代码时都要求 FullTrust 权限。而在 .NET Framework 1.0 版中,用于 OLE DB、ODBC 和 Oracle 的数据提供程序只在链接时要求 FullTrust 权限。

注意   随 .NET Framework 1.0 版提供的 SQL Server .NET Framework 数据提供程序要求在 FullTrust 权限下运行。在权限级别低于 FullTrust 的区域中使用 SQL Server 提供程序的任何尝试都会产生 SecurityException。
SQL Server .NET Framework 数据提供程序在具有 FullTrust 权限和具有部分受信任权限集的区域中都可以运行。部分受信任的应用程序必须至少具有“执行”和“SQL 客户端”权限。对于部分受信任的区域,可以使用 SqlClient 权限属性来进一步限制 SQL Server 提供程序可用的功能。

注意   SQL Server .NET Framework 数据提供程序需要具有启用了“允许调用非托管程序集”的“安全”权限(具有 UnmanagedCode SecurityPermissionFlag 的 SecurityPermission),才可以打开启用了 SQL 调试的 SqlConnection。
为了对特定区域使用 SqlClient 权限,管理员需要创建自定义权限集并将其设置为特定区域的权限集。不能修改默认权限集(如 LocalIntranet)。例如,要包括 Zone 为 LocalIntranet 的代码的 SqlClient 权限,管理员可以复制 LocalIntranet 的权限集,将其重命名为 MyLocalIntranet,添加 SqlClient 权限,使用代码访问安全策略工具 (Caspol.exe) 导入 MyLocalIntranet 权限集,并将 LocalIntranet_Zone 的权限集设置为 MyLocalIntranet。

下表列出了可用的权限特性属性及其应用。

SqlClientPermissionAttribute
属性 说明
AllowBlankPassword 用于启用或禁用连接字符串中空白密码。有效值为 true 则启用空白密码,为 false 则禁用。
ConnectionString 标识允许的连接字符串。可标识多个连接字符串。使用 KeyRestrictions 可以提供附加的连接字符串限制。
建议您的连接字符串中不要包括用户标识和密码。

此版本中,不能使用 .NET Framework 配置工具更改连接字符串限制。
 
KeyRestrictions 标识允许或不允许的连接字符串参数。连接字符串参数以格式 <parameter name>= 进行标识。可以指定多个参数,每个参数用分号 (;) 隔开。使用 KeyRestrictionBehavior 可以将列出的连接字符串参数仅标识为允许的附加参数,或者标识为不允许的附加参数。
如果未指定 KeyRestrictions,且 KeyRestrictionBehavior 属性设置为 AllowOnly,则不允许任何附加的连接字符串参数。

如果未指定 KeyRestrictions,且 KeyRestrictionBehavior 属性设置为 PreventUsage,则不允许任何附加的连接字符串参数。
 
KeyRestrictionBehavior 确定 KeyRestrictions 属性所标识的连接字符串参数的列表是唯一允许的附加连接字符串参数 (AllowOnly),还是唯一不允许的附加连接字符串参数 (PreventUsage)。默认为 AllowOnly。

例如,以下属性设置只允许使用连接字符串 Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;。

<add ConnectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;" />要启用以上连接字符串,还要启用 Encrypt 和 Packet Size 连接字符串选项,但同时限制其他任何连接字符串选项的使用,请使用以下设置。

<add ConnectionString=" Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;"
KeyRestrictions="Encrypt=;Packet Size=;"
KeyRestrictionBehavior="AllowOnly" />要启用以上连接字符串并允许除 User Id、Password 和 Persist Security Info 外的其他任何连接参数,请使用以下设置。

<add ConnectionString=" Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;"
KeyRestrictions="User Id=;Password=;Persist Security Info=;"
KeyRestrictionBehavior="PreventUsage" />以下权限使用“集成安全性”允许同时连接到 localhost 和 MySqlServer。连接字符串也可以包含 Initial Catalog、Connection Timeout、Encrypt 和 Packet Size 等参数。而所有其他的连接字符串参数都被限制。

<add ConnectionString="Data Source=localhost;Integrated Security=SSPI;"
     KeyRestrictions="Initial Catalog;Connection Timeout=;Encrypt=;Packet Size=;"
     KeyRestrictionBehavior="AllowOnly" />
<add ConnectionString="Data Source=MySqlServer;Integrated Security=SSPI;"
     KeyRestrictions="Initial Catalog;Connection Timeout=;Encrypt=;Packet Size=;"
     KeyRestrictionBehavior="AllowOnly" />示例权限集
下面是在部分受信任方案中,SQL Server .NET Framework 数据提供程序的示例权限集。有关创建自定义权限集的信息,请参见使用 Caspol.exe 配置权限集。

<PermissionSet class="System.Security.NamedPermissionSet"
               version="1"
               Name="MyLocalIntranet"
               Description="Custom permission set given to applications on the local intranet">

   <IPermission class="System.Data.SqlClient.SqlClientPermission, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
                version="1"
                AllowBlankPassword="False">
      <add ConnectionString="Data Source=localhost;Integrated Security=SSPI;"
           KeyRestrictions="Initial Catalog;Connection Timeout=;Encrypt=;Packet Size=;"
           KeyRestrictionBehavior="AllowOnly" />
   </IPermission>

</PermissionSet>使用安全权限验证 ADO.NET 代码访问对于部分受信任方案,可以通过指定所需的 SqlClientPermissionAttribute 属性,将您代码中的特定方法标识为需要特定的代码访问安全特权。如果不允许您的代码使用该特权,那么在运行代码之前将引发异常。注意   由于对部分受信任方案没有启用 OLE DB .NET Framework 数据提供程序和 ODBC .NET Framework 数据提供程序,因此,虽然在测试是否有特定的特权时可能会成功,但代码在执行时将会失败,并引发 SecurityException。例如,以下代码显示一个标识为需要特定连接字符串的方法。如果不允许该连接字符串,将引发异常,方法也得不到执行。[Visual Basic]
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Security
Imports System.Security.Permissions

Public Class Sample

  <SqlClientPermissionAttribute(SecurityAction.Demand, ConnectionString := " Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;")> _
  Private Shared Sub OpenConn()
    Dim testConn As SqlConnection = New SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;")
    testConn.Open()
    Console.WriteLine("The calling method has been granted sufficient permission to access the database.")
    testConn.Close()
  End Sub

  Public Shared Sub Main()
    Try
      OpenConn()
    Catch e As SecurityException
      Console.WriteLine("The calling method has not been granted sufficient permission to access the database.")
    End Try
  End Sub
End Class
[C#]
using System;
using System.Data;
using System.Data.SqlClient;
using System.Security;
using System.Security.Permissions;

public class Sample
{

  [SqlClientPermissionAttribute(SecurityAction.Demand, ConnectionString = " Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;")]
  private static void OpenConn()
  {
    SqlConnection testConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;");
    testConn.Open();
    Console.WriteLine("The calling method has been granted sufficient permission to access the database.");
    testConn.Close();
  }

  public static void Main()
  {
    try
    {
      OpenConn();
    }
    catch (SecurityException)
    {
      Console.WriteLine("The calling method has not been granted sufficient permission to access the database.");
    }
  }
}

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/my1688/archive/2008/04/22/2315359.aspx

原创粉丝点击