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

时间:2009-02-12 17:47:28

标签: sql vb.net sql-parametrized-query

我听说“所有人”都在使用参数化SQL查询来防止SQL注入攻击,而不必为每一条用户输入提供信息。

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

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

cmdText = String.Format("SELECT foo FROM bar WHERE baz = '{0}'", fuz)

这会参数化吗?

cmdText = String.Format("EXEC foo_from_baz '{0}'", fuz)

或者,为了保护自己免受SQL注入,我是否需要做更广泛的事情?

With command
    .Parameters.Count = 1
    .Parameters.Item(0).ParameterName = "@baz"
    .Parameters.Item(0).Value = fuz
End With

除安全考虑因素外,使用参数化查询还有其他优点吗?

更新:这篇伟大的文章与Grotok引用的一个问题相关联。 http://www.sommarskog.se/dynamic_sql.html

6 个答案:

答案 0 :(得分:74)

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

  

'; DROP TABLE栏; -

尝试将其放入您的fuz变量中(或者,如果您重视条形表,则不要这样做)。也可能有更微妙和破坏性的查询。

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

Public Function GetBarFooByBaz(ByVal Baz As String) As String
    Dim sql As String = "SELECT foo FROM bar WHERE baz= @Baz"

    Using cn As New SqlConnection("Your connection string here"), _
        cmd As New SqlCommand(sql, cn)

        cmd.Parameters.Add("@Baz", SqlDbType.VarChar, 50).Value = Baz
        Return cmd.ExecuteScalar().ToString()
    End Using
End Function

存储过程有时可以防止SQL注入。但是,大多数情况下,您仍然需要使用查询参数调用它们,否则它们无法帮助您。如果您使用独占存储过程,那么您可以关闭应用程序用户帐户的SELECT,UPDATE,ALTER,CREATE,DELETE等权限(除了EXEC之外的所有内容)并获得一些保护

答案 1 :(得分:15)

绝对是最后一个,即

  

或者我是否需要做更广泛的......? (是的,cmd.Parameters.Add()

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

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

答案 2 :(得分:5)

您想要使用最后一个示例,因为这是唯一真正参数化的示例。除了安全问题(这可能比您想象的要普遍得多)之外,最好让ADO.NET处理参数化,因为您无法确定传入的值是否需要单引号而不检查{{1每个参数。

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

Type

答案 3 :(得分:2)

大多数人都会通过服务器端编程语言库来完成这项工作,例如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*/}

这样可以转义数据以便插入数据库。

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

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

答案 4 :(得分:1)

您的命令文本必须如下:

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

cmdText = "EXEC foo_from_baz ?"

然后添加参数值。这种方式确保值con最终仅用作值,而使用另一种方法,如果变量fuz设置为

"x'; delete from foo where 'a' = 'a"
你能看到会发生什么吗?

答案 5 :(得分:0)

这是一个从SQL开始的简短类,您可以从那里构建并添加到类中。

的MySQL

Public Class mysql

    'Connection string for mysql
    Public SQLSource As String = "Server=123.456.789.123;userid=someuser;password=somesecurepassword;database=somedefaultdatabase;"

    'database connection classes

    Private DBcon As New MySqlConnection
    Private SQLcmd As MySqlCommand
    Public DBDA As New MySqlDataAdapter
    Public DBDT As New DataTable
    Public BindSource As New BindingSource
    ' parameters
    Public Params As New List(Of MySqlParameter)

    ' some stats
    Public RecordCount As Integer
    Public Exception As String

    Function ExecScalar(SQLQuery As String) As Long
        Dim theID As Long
        DBcon.ConnectionString = SQLSource
        Try
            DBcon.Open()
            SQLcmd = New MySqlCommand(SQLQuery, DBcon)
            'loads params into the query
            Params.ForEach(Sub(p) SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value))

            'or like this is also good
            'For Each p As MySqlParameter In Params
            ' SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value)
            ' Next
            ' clears params
            Params.Clear()
            'return the Id of the last insert or result of other query
            theID = Convert.ToInt32(SQLcmd.ExecuteScalar())
            DBcon.Close()

        Catch ex As MySqlException
            Exception = ex.Message
            theID = -1
        Finally
            DBcon.Dispose()
        End Try
        ExecScalar = theID
    End Function

    Sub ExecQuery(SQLQuery As String)

        DBcon.ConnectionString = SQLSource
        Try
            DBcon.Open()
            SQLcmd = New MySqlCommand(SQLQuery, DBcon)
            'loads params into the query
            Params.ForEach(Sub(p) SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value))

            'or like this is also good
            'For Each p As MySqlParameter In Params
            ' SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value)
            ' Next
            ' clears params

            Params.Clear()
            DBDA.SelectCommand = SQLcmd
            DBDA.Update(DBDT)
            DBDA.Fill(DBDT)
            BindSource.DataSource = DBDT  ' DBDT will contain your database table with your records
            DBcon.Close()
        Catch ex As MySqlException
            Exception = ex.Message
        Finally
            DBcon.Dispose()
        End Try
    End Sub
    ' add parameters to the list
    Public Sub AddParam(Name As String, Value As Object)
        Dim NewParam As New MySqlParameter(Name, Value)
        Params.Add(NewParam)
    End Sub
End Class

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