如何创建参数化SQL查询?我为什么要这样做?

如何创建参数化SQL查询?我为什么要这样做?,sql,vb.net,sql-parametrized-query,Sql,Vb.net,Sql Parametrized Query,我听说“每个人”都在使用参数化SQL查询来防止SQL注入攻击,而不必利用每一条用户输入 你是怎么做到的?在使用存储过程时是否会自动获取此信息 所以我的理解是,这是非参数化的: cmdText=String.Format(“从baz='{0}',fuz的条形图中选择foo) 这是参数化的吗 cmdText=String.Format(“EXEC foo_from_baz'{0}',fuz) 或者我需要做一些更广泛的事情来保护自己不受SQL注入的影响吗 With命令 .Parameters.Co

我听说“每个人”都在使用参数化SQL查询来防止SQL注入攻击,而不必利用每一条用户输入

你是怎么做到的?在使用存储过程时是否会自动获取此信息

所以我的理解是,这是非参数化的:

cmdText=String.Format(“从baz='{0}',fuz的条形图中选择foo)
这是参数化的吗

cmdText=String.Format(“EXEC foo_from_baz'{0}',fuz)
或者我需要做一些更广泛的事情来保护自己不受SQL注入的影响吗

With命令
.Parameters.Count=1
.Parameters.Item(0).ParameterName=“@baz”
.参数.项目(0).值=引信
以
除了安全考虑之外,使用参数化查询还有其他好处吗

更新:这篇伟大的文章链接在Grotok的一个问题参考中。

您想继续上一个示例,因为这是唯一一个真正参数化的示例。除了安全问题(可能比您想象的要普遍得多),最好让ADO.NET处理参数化,因为如果不检查每个参数的
类型
,您无法确定要传递的值是否需要单引号

[编辑]以下是一个示例:

SqlCommand command = new SqlCommand(
    "select foo from bar where baz = @baz",
    yourSqlConnection
);

SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "@baz";
parameter.Value = "xyz";

command.Parameters.Add(parameter);

您的EXEC示例将不会参数化。您需要参数化查询(某些圆圈中的准备语句)来防止这样的输入造成损害:

",;升降台杆--

尝试将其放入fuz变量中(或者,如果您对bar表进行了估值,则不要这样做)。还有可能出现更微妙、更具破坏性的查询

以下是如何使用Sql Server执行参数的示例:

公共函数GetBarFooByBaz(ByVal Baz作为字符串)作为字符串
Dim sql As String=“从baz=@baz的栏中选择foo”
将cn用作新的SqlConnection(“此处为您的连接字符串”)_
cmd作为新的SqlCommand(sql,cn)
cmd.Parameters.Add(“@Baz”,SqlDbType.VarChar,50)。Value=Baz
返回cmd.ExecuteScalar().ToString()
终端使用
端函数

存储过程有时被认为是阻止SQL注入的原因。但是,大多数情况下,您仍然必须使用查询参数调用它们,否则它们就没有帮助了。如果专门使用存储过程,则可以关闭应用程序用户帐户的选择、更新、更改、创建、删除等权限(除EXEC外的所有权限),并通过这种方式获得一些保护。

您的命令文本需要如下所示:

cmdText = "SELECT foo FROM bar WHERE baz = ?"

cmdText = "EXEC foo_from_baz ?"
然后添加参数值。这种方法确保值con仅最终用作值,而如果变量fuz设置为

"x'; delete from foo where 'a' = 'a"

你能看到会发生什么吗?

大多数人都会通过服务器端编程语言库来实现这一点,比如PHP的PDO或Perl DBI

例如,在PDO中:

$dbh=pdo_connect(); //you need a connection function, returns a pdo db connection

$sql='insert into squip values(null,?,?)';

$statement=$dbh->prepare($sql);

$data=array('my user supplied data','more stuff');

$statement->execute($data);

if($statement->rowCount()==1){/*it worked*/}
这负责转义数据以插入数据库

一个优点是,您可以使用一条准备好的语句多次重复插入,从而获得速度优势


例如,在上面的查询中,我可以准备一次语句,然后从一堆数据循环创建数据数组,并根据需要重复->执行多次。

肯定是最后一次,即

还是我需要做一些更广泛的。。。?(是,
cmd.Parameters.Add()

参数化查询有两个主要优点:

  • 安全性:这是避免漏洞的好方法
  • 性能:如果您经常使用不同的参数调用同一查询,那么参数化查询可能会允许数据库缓存您的查询,这是性能提高的一个重要来源
  • 额外:您不必担心数据库代码中的日期和时间格式问题。类似地,如果您的代码将在非英语语言环境的计算机上运行,那么小数点/小数逗号将不会出现问题

    • 这里有一个简短的类,从SQL开始,您可以在此基础上构建并添加到该类中

      MySQL

      MS SQL/Express

      Public Class MSSQLDB
          ' CREATE YOUR DB CONNECTION
          'Change the datasource
          Public SQLSource As String = "Data Source=someserver\sqlexpress;Integrated Security=True"
          Private DBCon As New SqlConnection(SQLSource)
      
          ' PREPARE DB COMMAND
          Private DBCmd As SqlCommand
      
          ' DB DATA
          Public DBDA As SqlDataAdapter
          Public DBDT As DataTable
      
          ' QUERY PARAMETERS
          Public Params As New List(Of SqlParameter)
      
          ' QUERY STATISTICS
          Public RecordCount As Integer
          Public Exception As String
      
          Public Sub ExecQuery(Query As String, Optional ByVal RunScalar As Boolean = False, Optional ByRef NewID As Long = -1)
              ' RESET QUERY STATS
              RecordCount = 0
              Exception = ""
              Dim RunScalar As Boolean = False
      
              Try
                  ' OPEN A CONNECTION
                  DBCon.Open()
      
                  ' CREATE DB COMMAND
                  DBCmd = New SqlCommand(Query, DBCon)
      
                  ' LOAD PARAMS INTO DB COMMAND
                  Params.ForEach(Sub(p) DBCmd.Parameters.Add(p))
      
                  ' CLEAR PARAMS LIST
                  Params.Clear()
      
                  ' EXECUTE COMMAND & FILL DATATABLE
                  If RunScalar = True Then
                      NewID = DBCmd.ExecuteScalar()
                  End If
                  DBDT = New DataTable
                  DBDA = New SqlDataAdapter(DBCmd)
                  RecordCount = DBDA.Fill(DBDT)
              Catch ex As Exception
                  Exception = ex.Message
              End Try
      
      
              ' CLOSE YOUR CONNECTION
              If DBCon.State = ConnectionState.Open Then DBCon.Close()
          End Sub
      
          ' INCLUDE QUERY & COMMAND PARAMETERS
          Public Sub AddParam(Name As String, Value As Object)
              Dim NewParam As New SqlParameter(Name, Value)
              Params.Add(NewParam)
          End Sub
      End Class
      

      我觉得很震惊,很明显这个问题以前没有在Stackoverflow上被问过。非常好!哦,是的。当然,措辞非常不同,但它确实如此。您应该使用参数化查询来防止破坏数据。无法抗拒:)With块有什么不好?是否有人有问题#关于“With块有什么不好”的问题?请注意:。Net字符串是unicode,因此默认情况下参数将采用NVarChar。如果它真的是一个VarChar列,这可能会导致很大的性能问题。你能进一步解释一下这个
      cmd.Parameters.Add(“@Baz”,SqlDbType.VarChar,50)。Value=Baz
      请?@CaryBondoc,你想知道什么?该行创建了一个名为
      @Baz
      的参数,该参数的类型为
      varchar(50)
      ,它被分配了
      Baz
      字符串的值。您也可以说“command.parameters.addithValue(@Baz,50)”@GavinPerkins假设您的意思是
      AddWithValue(@Baz,Baz)
      ,您可以这样做,特别是因为将默认映射为
      nvarchar
      的字符串值转换为实际的
      varchar
      类型是可以触发该链接中提到的效果的最常见位置之一。
      Public Class MSSQLDB
          ' CREATE YOUR DB CONNECTION
          'Change the datasource
          Public SQLSource As String = "Data Source=someserver\sqlexpress;Integrated Security=True"
          Private DBCon As New SqlConnection(SQLSource)
      
          ' PREPARE DB COMMAND
          Private DBCmd As SqlCommand
      
          ' DB DATA
          Public DBDA As SqlDataAdapter
          Public DBDT As DataTable
      
          ' QUERY PARAMETERS
          Public Params As New List(Of SqlParameter)
      
          ' QUERY STATISTICS
          Public RecordCount As Integer
          Public Exception As String
      
          Public Sub ExecQuery(Query As String, Optional ByVal RunScalar As Boolean = False, Optional ByRef NewID As Long = -1)
              ' RESET QUERY STATS
              RecordCount = 0
              Exception = ""
              Dim RunScalar As Boolean = False
      
              Try
                  ' OPEN A CONNECTION
                  DBCon.Open()
      
                  ' CREATE DB COMMAND
                  DBCmd = New SqlCommand(Query, DBCon)
      
                  ' LOAD PARAMS INTO DB COMMAND
                  Params.ForEach(Sub(p) DBCmd.Parameters.Add(p))
      
                  ' CLEAR PARAMS LIST
                  Params.Clear()
      
                  ' EXECUTE COMMAND & FILL DATATABLE
                  If RunScalar = True Then
                      NewID = DBCmd.ExecuteScalar()
                  End If
                  DBDT = New DataTable
                  DBDA = New SqlDataAdapter(DBCmd)
                  RecordCount = DBDA.Fill(DBDT)
              Catch ex As Exception
                  Exception = ex.Message
              End Try
      
      
              ' CLOSE YOUR CONNECTION
              If DBCon.State = ConnectionState.Open Then DBCon.Close()
          End Sub
      
          ' INCLUDE QUERY & COMMAND PARAMETERS
          Public Sub AddParam(Name As String, Value As Object)
              Dim NewParam As New SqlParameter(Name, Value)
              Params.Add(NewParam)
          End Sub
      End Class