56

I am trying to create an SQL statement using user-supplied data. I use code similar to this in C#:

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

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

and this in VB.NET:

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

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

However,

  • this fails when the user input contains single quotes (e.g. O'Brien),
  • I cannot seem to get the format right when inserting DateTime values and
  • people keep telling me that I should not do this because of "SQL injection".

How do I do it "the right way"?

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • 5
    Note: This question is meant as a canonical question for people who cannot get their string-concatenated SQLs to work. [If you want to discuss it, here is the corresponding meta question.](http://meta.stackoverflow.com/q/315913/87698) – Heinzi Feb 02 '16 at 20:39
  • 3
    If you would like a more indepth look at what "SQL Injection" is and why it is dangerous see the question: "[How can I explain SQL injection without technical jargon?](http://security.stackexchange.com/questions/25684/how-can-i-explain-sql-injection-without-technical-jargon)" from our Information Security sister site. – Scott Chamberlain Feb 02 '16 at 21:11
  • 1
    You should wiki this, btw. –  Feb 02 '16 at 21:12
  • 2
    @Will: Won't CW'ing the question also CW all future answers, and, thus, discourage others from contributing better answers than mine? – Heinzi Feb 02 '16 at 21:19
  • Possible duplicate of [What are good ways to prevent SQL injection?](http://stackoverflow.com/questions/14376473/what-are-good-ways-to-prevent-sql-injection) – TT. Feb 02 '16 at 22:21
  • You might want to include things that are are hard to paramterize. e.g. [table names](http://stackoverflow.com/q/26134483/119477) or [sort order](http://stackoverflow.com/q/6442325/119477) – Conrad Frix Feb 03 '16 at 20:52
  • @Heinzi - Thank you for this, I finally found a good question to use as `duplicate of`! I have a couple of suggestions. 1) What about replacing the [tag:sqlcommand] with [tag:ado.net]? 2) How about adding [tag:vb.net]? I could edit your question and answer and add a duplicate code block translated into [tag:vb.net]. The reason being is that I see almost just as many (if not more) questions that could be marked as duplicate written in vb.net and due to their inexperience it might be easier for them to read/understand an answer in vb. Let me know what you think, I will be happy to do both/either – Igor Mar 26 '18 at 18:16
  • @Igor: Both sound like great ideas! If you want, you can make the edit and I will look over it (VB.NET is actually (still) my main language at the moment). Alternatively, you could add the VB.NET code examples as a second answer (and link to it from the main answer), so that we keep the main answer short. – Heinzi Mar 26 '18 at 19:24
  • @Heinzi - Based on your suggestion I added a second answer which links back to the first example. I can add a "vb.net code link" in the primary answer if you would like. Please edit it as you see fit, based on your comment you vb.net skills are far better than mine (*I only ever use vb.net on [so]*). If you decide it would be better to copy/paste just the VB.NET code directly into the existing answer let me know and I will delete this one. – Igor Mar 26 '18 at 19:55
  • @Heinzi - thank you for the review and edit! One final question, do you have any objection to me replacing the [tag:user-input] with [tag:vb.net] tag? – Igor Mar 26 '18 at 20:32
  • 1
    @Igor: Good idea, done. I have also moved the VB version of the question code directly to the question, to make it obvious that this is about VB as well. – Heinzi Mar 27 '18 at 07:33
  • Related: https://stackoverflow.com/questions/28473476/avoid-string-concatenation-to-create-queries – Hans Kesting Apr 26 '23 at 09:40

2 Answers2

66

Use parameterized SQL.

Examples

(These examples are in C#, see below for the VB.NET version.)

Replace your string concatenations with @... placeholders and, afterwards, add the values to your SqlCommand. You can choose the name of the placeholders freely, just make sure that they start with the @ sign. Your example would look like this:

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

The same pattern is used for other kinds of SQL statements:

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

// see above, same as INSERT

or

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.
}

A word of caution: AddWithValue is a good starting point and works fine in most cases. However, the value you pass in needs to exactly match the data type of the corresponding database field. Otherwise, you might end up in a situation where the conversion prevents your query from using an index. Note that some SQL Server data types, such as char/varchar (without preceding "n") or date do not have a corresponding .NET data type. In those cases, Add with the correct data type should be used instead.

Why should I do that?

Other database access libraries

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • Careful with the "other kind" statement -- SELECT won't work with `cmd.ExecuteNonQuery()` – Hogan Feb 02 '16 at 20:56
  • @Hogan: True. I thought about giving a more complete example, but since this answer is mainly about transitioning from string-concatenated SQL to parameterized SQL, I did not want to bloat the answer by adding too much code that "won't change" (if it was ExecuteScalar before, it's ExecuteScalar afterwards). – Heinzi Feb 02 '16 at 21:04
  • @Heinze - I think I'd like a short bullet point under special cases saying something like "using parameters will work even if you are doing 'ExecuteQuery(), ExecuteReader(), ExecuteScalar()` or others. – Hogan Feb 02 '16 at 22:06
  • @Hogan: I've expanded it a bit. I'm still trying to [keep it short](http://meta.stackoverflow.com/q/315643/87698). There is no SqlCommand.ExecuteQuery(). – Heinzi Feb 04 '16 at 07:28
  • 1
    Also worth reading [can we stop using AddWithValue](https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/). – Richardissimo Oct 13 '19 at 08:27
2

VB.NET Example Code

This is the example code for the wiki answer in , assuming Option Strict On and Option Infer On.

INSERT

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

UPDATE

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

' see above, same as INSERT

SELECT

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
Heinzi
  • 167,459
  • 57
  • 363
  • 519
Igor
  • 60,821
  • 10
  • 100
  • 175
  • Also worth reading [can we stop using AddWithValue](https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/). – Richardissimo Oct 13 '19 at 08:28