如何将用户提供的输入添加到 SQL 语句中?

How can I add user-supplied input to an SQL statement?

提问人:Heinzi 提问时间:2/3/2016 最后编辑:Heinzi 更新时间:7/7/2021 访问量:10770

问:

我正在尝试使用用户提供的数据创建 SQL 语句。我在 C# 中使用与此类似的代码:

var sql = "INSERT INTO myTable (myField1, myField2) " +
          "VALUES ('" + someVariable + "', '" + someTextBox.Text + "');";

var cmd = new SqlCommand(sql, myDbConnection);
cmd.ExecuteNonQuery();

这在 VB.NET 中:

Dim sql = "INSERT INTO myTable (myField1, myField2) " &
          "VALUES ('" & someVariable & "', '" & someTextBox.Text & "');"

Dim cmd As New SqlCommand(sql, myDbConnection)
cmd.ExecuteNonQuery()

然而

  • 当用户输入包含单引号时,此操作会失败(例如,O'Brien
  • 在插入 DateTime 值和
  • 人们一直告诉我,我不应该因为“SQL注入”而这样做。

我如何“以正确的方式”做到这一点?

C# vb.net ado.net SQL 注入

评论

5赞 Heinzi 2/3/2016
注意:这个问题是针对那些无法让字符串连接的 SQL 工作的人提出的规范问题。如果你想讨论它,这里有相应的元问题。
3赞 Scott Chamberlain 2/3/2016
如果您想更深入地了解什么是“SQL注入”以及为什么它是危险的,请参阅我们的信息安全姊妹网站中的问题:“如何在没有技术术语的情况下解释SQL注入?
1赞 2/3/2016
顺便说一句,你应该维基这个。
2赞 Heinzi 2/3/2016
@Will:CW的问题不会也会CW所有未来的答案,从而阻止其他人提供比我更好的答案吗?
1赞 Heinzi 3/27/2018
@Igor:好主意,搞定了。我还将问题代码的 VB 版本直接移到了问题中,以表明这也是关于 VB 的。

答:

66赞 8 revs, 2 users 97%Heinzi #1

使用参数化 SQL。

例子

(这些示例是用 C# 编写的,请参阅下面的 VB.NET 版本。

将字符串连接替换为占位符,然后将值添加到 SqlCommand。您可以自由选择占位符的名称,只需确保它们以符号开头即可。您的示例如下所示:@...@

var sql = "INSERT INTO myTable (myField1, myField2) " +
          "VALUES (@someValue, @someOtherValue);";

using (var cmd = new SqlCommand(sql, myDbConnection))
{
    cmd.Parameters.AddWithValue("@someValue", someVariable);
    cmd.Parameters.AddWithValue("@someOtherValue", someTextBox.Text);
    cmd.ExecuteNonQuery();
}

其他类型的 SQL 语句也使用相同的模式:

var sql = "UPDATE myTable SET myField1 = @newValue WHERE myField2 = @someValue;";

// see above, same as INSERT

var sql = "SELECT myField1, myField2 FROM myTable WHERE myField3 = @someValue;";

using (var cmd = new SqlCommand(sql, myDbConnection))
{
    cmd.Parameters.AddWithValue("@someValue", someVariable);
    using (var reader = cmd.ExecuteReader())
    {
        ...
    }
    // Alternatively: object result = cmd.ExecuteScalar();
    // if you are only interested in one value of one row.
}

注意:这是一个很好的起点,在大多数情况下效果很好。但是,传入的值需要与相应数据库字段的数据类型完全匹配。否则,您最终可能会遇到转换阻止查询使用索引的情况。请注意,某些 SQL Server 数据类型(如 char/varchar(前面不带“n”)或 date)没有相应的 .NET 数据类型。在这些情况下,应改用具有正确数据类型的 AddAddWithValue

我为什么要这样做?

其他数据库访问库

  • 如果使用 OleDbCommand 而不是 SqlCommand(例如,如果您使用的是 MS Access 数据库),请在 SQL 中用作占位符。在这种情况下,第一个参数是无关紧要的;相反,您需要按正确的顺序添加参数。OdbcCommand 也是如此?@...AddWithValue

  • 实体框架还支持参数化查询。

评论

0赞 Hogan 2/3/2016
小心使用“其他种类”语句 -- SELECT 不适用于cmd.ExecuteNonQuery()
0赞 Heinzi 2/3/2016
@Hogan:没错。我考虑过给出一个更完整的示例,但由于这个答案主要是关于从字符串连接的 SQL 过渡到参数化的 SQL,我不想通过添加太多“不会改变”的代码来膨胀答案(如果之前是 ExecuteScalar,之后是 ExecuteScalar)。
0赞 Hogan 2/3/2016
@Heinze - 我想我想要一个简短的项目符号,在特殊情况下说“即使你正在执行'ExecuteQuery()、ExecuteReader()、ExecuteScalar()'或其他操作,使用参数也会起作用。
0赞 Heinzi 2/4/2016
@Hogan:我稍微扩展了一下。我仍然试图保持简短。没有 SqlCommand.ExecuteQuery()。
1赞 Richardissimo 10/13/2019
同样值得一读的是,我们可以停止使用 AddWithValue 吗
2赞 7 revs, 2 users 81%Igor #2

VB.NET 示例代码

这是 wiki 答案的示例代码,假设 和 。Option Strict OnOption Infer On

插入

Dim sql = "INSERT INTO myTable (myField1, myField2) " & 
          "VALUES (@someValue, @someOtherValue);"

Using cmd As New SqlCommand(sql, myDbConnection)
    cmd.Parameters.AddWithValue("@someValue", someVariable)
    cmd.Parameters.AddWithValue("@someOtherValue", someTextBox.Text)
    cmd.ExecuteNonQuery()
End Using

更新

Dim sql = "UPDATE myTable SET myField1 = @newValue WHERE myField2 = @someValue;"

' see above, same as INSERT

选择

Dim sql = "SELECT myField1, myField2 FROM myTable WHERE myField3 = @someValue;"

Using cmd As New SqlCommand(sql, myDbConnection)
    cmd.Parameters.AddWithValue("@someValue", someVariable)
    Using reader = cmd.ExecuteReader()
        ' ...
    End Using
    ' Alternatively: Dim result = cmd.ExecuteScalar()
    ' if you are only interested in one value of one row.
End Using

评论

0赞 Richardissimo 10/13/2019
同样值得一读的是,我们可以停止使用 AddWithValue 吗