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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/542510/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-01 01:06:46  来源:igfitidea点击:

How do I create a parameterized SQL query? Why Should I?

sqlvb.netsql-parametrized-query

提问by Jim Counts

I've heard that "everyone" is using parameterized SQL queries to protect against SQL injection attacks without having to vailidate every piece of user input.

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

How do you do this? Do you get this automatically when using stored procedures?

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

So my understanding this is non-parameterized:

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

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

Would this be parameterized?

这会被参数化吗?

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

Or do I need to do somethng more extensive like this in order to protect myself from SQL injection?

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

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

Are there other advantages to using parameterized queries besides the security considerations?

除了安全考虑之外,使用参数化查询还有其他优势吗?

Update: This great article was linked in one of the questions references by Grotok. http://www.sommarskog.se/dynamic_sql.html

更新:这篇很棒的文章链接在 Grotok 的一个问题参考文献中。 http://www.sommarskog.se/dynamic_sql.html

采纳答案by Joel Coehoorn

Your EXEC example would NOT be parameterized. You need parameterized queries (prepared statements in some circles) to prevent input like this from causing damage:

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

';DROP TABLE bar;--

';删除表格栏;--

Try putting that in your fuz variable (or don't, if you value your bar table). More subtle and damaging queries are possible as well.

试着把它放在你的 fuz 变量中(或者不要,如果你重视你的条形表)。更微妙和破坏性的查询也是可能的。

Here's an example of how you do parameters with Sql Server:

以下是如何使用 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

Stored procedures are sometimes credited with preventing SQL injection. However, most of the time you still have to call them using query parameters or they don't help. If you use stored procedures exclusively, then you can turn off permissions for SELECT, UPDATE, ALTER, CREATE, DELETE, etc (just about everything but EXEC) for the application user account and get some protection that way.

存储过程有时被认为可以防止 SQL 注入。但是,大多数时候您仍然必须使用查询参数调用它们,否则它们无济于事。如果你使用存储过程完全,那么你可以为应用程序的用户帐户关闭权限SELECT,UPDATE,ALTER,创建,删除等(刚才的一切,但EXEC),并得到了一些保护的方式。

回答by Tamas Czinege

Definitely the last one, i.e.

绝对是最后一个,即

Or do I need to do somethng more extensive ...? (Yes, cmd.Parameters.Add())

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

Parametrized queries have two main advantages:

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

  • Security: It is a good way to avoid SQL Injectionvulnerabilities
  • Performance: If you regularly invoke the same query just with different parameters a parametrized query might allow the database to cache your queries which is a considerable source of performance gain.
  • Extra: You won't have to worry about date and time formatting issues in your database code. Similarly, if your code will ever run on machines with a non-English locale, you will not have problems with decimal points / decimal commas.
  • 安全性:是避免SQL注入漏洞的好方法
  • 性能:如果您定期使用不同的参数调用相同的查询,则参数化查询可能允许数据库缓存您的查询,这是性能提升的重要来源。
  • 额外:您不必担心数据库代码中的日期和时间格式问题。同样,如果您的代码将在非英语语言环境的机器上运行,您将不会遇到小数点/小数点逗号的问题。

回答by Andrew Hare

You want to go with your last example as this is the only one that is truly parametrized. Besides security concerns (which are much more prevalent then you might think) it is best to let ADO.NET handle the parametrization as you cannot be sure if the value you are passing in requires single quotes around it or not without inspecting the Typeof each parameter.

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

[Edit] Here is an example:

[编辑] 这是一个例子:

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);

回答by JAL

Most people would do this through a server side programming language library, like PHP's PDO or Perl DBI.

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

For instance, in PDO:

例如,在 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*/}

This takes care of escaping your data for database insertion.

这负责为数据库插入转义您的数据。

One advantage is that you can repeat an insert many times with one prepared statement, gaining a speed advantage.

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

For instance, in the above query I could prepare the statement once, and then loop over creating the data array from a bunch of data and repeat the ->execute as many times as needed.

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

回答by Tony Andrews

Your command text need to be like:

你的命令文本需要像:

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

cmdText = "EXEC foo_from_baz ?"

Then add parameter values. This way ensures that the value con only end up being used as a value, whereas with the other method if variable fuz is set to

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

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

can you see what might happen?

你能看到会发生什么吗?

回答by Chillzy

Here's a short class to start with SQL and you can build from there and add to the class.

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

MySQL

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

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