Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does String.Format work but SqlCommand.Parameters.Add not?

Tags:

c#

sql-server

I have a Project table with two columns -- ProjectId and ProjectName -- and am writing a function that constructs and executes a SqlCommand to query the database for the ids of a Project with a given name. This command works, but is vulnerable to SQL Injection:

string sqlCommand = String.Format("SELECT {0} FROM {1} WHERE {2} = {3}",
            attributeParam, tableParam, idParam, surroundWithSingleQuotes(idValue));

SqlCommand command = new SqlCommand(sqlCommand, sqlDbConnection);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
    DataTable attributes = new DataTable();
    adapter.Fill(attributes);
    ...
}

attributeParam, tableParam, idParam, and idValue are all strings. For example, they might be "ProjectId", "Project", "ProjectName", and "MyFirstProject", respectively. surroundWithSingleQuotes surrounds a string with '', so surroundWithSingleQuotes(idValue) == "'MyFirstProject'". I am trying to write this function as general as possible since I might want to get all of a given attribute from a table in the future.

Although the above String.Format works, this doesn't:

string sqlCommand = String.Format("SELECT @attributeparam FROM {0} WHERE " + 
    "@idparam = @idvalue", tableParam);

command.Parameters.Add(new SqlParameter("@attributeparam", attributeParam));
command.Parameters.Add(new SqlParameter("@idparam", idParam));
command.Parameters.Add(new SqlParameter("@idvalue", 
     surroundWithSingleQuotes(idValue)));

SqlCommand command = new SqlCommand(sqlCommand, sqlDbConnection);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
    DataTable attributes = new DataTable();
    adapter.Fill(attributes);
    ...
}

I'm not sure why. I get no error message, but when I fill my DataTable using a SqlDataAdapter, the DataTable contains nothing. Here are various approaches I've taken, to no avail:

  • Following this answer and Microsoft's documentation, using AddWithValue or using Parameters.Add and SqlParameter.Value.
  • Selectively replacing {0}, {1}, {2}, and {3}in String.Format with either the actual value or the parameter string.

In other places in my code, I've used parametrized queries (although with just one parameter) no problem.

like image 862
rln Avatar asked Oct 15 '15 15:10

rln


1 Answers

Basically parameters in SQL only work for values - not identifiers of columns or tables. In your example, only the final parameter represents a value.

If you need to be dynamic in terms of your column and table names, you'll need to build that part of the SQL yourself. Be very careful for all the normal reasons associated with SQL injection attacks. Ideally, only allow a known whitelist of table and column values. If you need to be more general, I'd suggest performing very restrictive validation, and quote the identifiers to avoid conflicts with keywords (or prohibit those entirely, ideally).

Keep using SQL parameters for values, of course.

like image 133
Jon Skeet Avatar answered Oct 24 '22 03:10

Jon Skeet