96

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

4

6 回答 6

80

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

';DROP TABLE 栏;--

尝试将其放入您的fuz变量中(或者不要,如果您重视bar表格)。更微妙和破坏性的查询也是可能的。

下面是如何使用 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 之外的所有内容)的权限,并通过这种方式获得一些保护。

于 2009-02-12T17:55:22.827 回答
15

绝对是最后一个,即

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

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

  • 安全性:这是避免SQL 注入漏洞的好方法
  • 性能:如果您定期使用不同的参数调用相同的查询,则参数化查询可能允许数据库缓存您的查询,这是性能提升的重要来源。
  • 额外:您不必担心数据库代码中的日期和时间格式问题。同样,如果您的代码将在非英语语言环境的机器上运行,您将不会遇到小数点/小数点逗号的问题。
于 2009-02-12T17:58:10.527 回答
5

您想使用最后一个示例,因为这是唯一真正参数化的示例。Type除了安全问题(这比您想象的要普遍得多)之外,最好让 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);
于 2009-02-12T17:53:02.473 回答
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*/}

这负责转义您的数据以进行数据库插入。

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

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

于 2009-02-12T17:57:01.947 回答
1

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

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

cmdText = "EXEC foo_from_baz ?"

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

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

你能看到会发生什么吗?

于 2009-02-12T17:56:05.803 回答
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

微软 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
于 2018-05-27T03:28:57.967 回答