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

我听说“每个人”都在使用参数化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

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

更新:这篇伟大的文章与格罗托克的一个问题相关联。[http://www.sommarskog.se/dynamic_sql.html]

Jim Counts asked 2019-09-09T16:40:39Z
6个解决方案
73 votes

您的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之外的所有内容),并以此方式获得一些保护。

Joel Coehoorn answered 2019-09-09T16:41:21Z
15 votes

绝对是最后一个,即

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

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

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

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

[编辑]这是一个例子:

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);
Andrew Hare answered 2019-09-09T16:42:51Z
2 votes

大多数人会通过服务器端编程语言库(如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*/}

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

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

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

JAL answered 2019-09-09T16:43:47Z
1 votes

您的命令文本必须如下:

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

cmdText = "EXEC foo_from_baz ?"

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

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

你能看出会发生什么吗?

Tony Andrews answered 2019-09-09T16:44:24Z
0 votes

这是一个从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
Chillzy answered 2019-09-09T16:45:00Z
translate from https://stackoverflow.com:/questions/542510/how-do-i-create-a-parameterized-sql-query-why-should-i