安全的 ADO.NET 编码指南

安全的 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___4quot;);
  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();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值