Is there any way how to do that? This does not work:
SqlCommand command = new SqlCommand("SELECT #slot FROM Users WHERE name=#name; ");
prikaz.Parameters.AddWithValue("name", name);
prikaz.Parameters.AddWithValue("slot", slot);
The only thing I can think of is to use SP and declare and set the variable for the column. Seems to me a bit ackward.
As has been mentioned, you cannot parameterise the fundamental query, so you will have to build the query itself at runtime. You should white-list the input of this, to prevent injection attacks, but fundamentally:
// TODO: verify that "slot" is an approved/expected value
SqlCommand command = new SqlCommand("SELECT [" + slot +
"] FROM Users WHERE name=#name; ")
prikaz.Parameters.AddWithValue("name", name);
This way #name is still parameterised etc.
You cannot do this in regular SQL - if you must have configurable column names (or table name, for that matter), you must use dynamic SQL - there is no other way to achieve this.
string sqlCommandStatement =
string.Format("SELECT {0} FROM dbo.Users WHERE name=#name", "slot");
and then use the sp_executesql stored proc in SQL Server to execute that SQL command (and specify the other parameters as needed).
Dynamic SQL has its pros and cons - read the ultimate article on The Curse and Blessings of Dynamic SQL expertly written by SQL Server MVP Erland Sommarskog.
Related
Is it possible to add the alias of a column as an SqlParameter to an SQL command? If so, how is it specified?
As a simplified example, say I have an SQL command that is constructed like this:
SqlCommand GetCommand(string columnName)
{
string filter = String.Format("SELECT MyColumn1 AS '{0}' FROM MyTable", columnName);
return new SqlCommand(filter);
}
This command does nothing to prevent an SQL Injection attack, so I want to follow the standard procedure and parameterize the command.
I'm used to converting statements in the WHERE clause to use parameters. The statements look similar to the above, for example:
SqlCommand command("SELECT * FROM MyTable WHERE name = '{0}'", name);
When I convert this, it becomes:
SqlCommand command("SELECT * FROM MyTable WHERE name = #name");
command.Parameters.Add(new SqlParameter("name", SqlDbType.NVarChar) { Value = name });
That works well. Taking the same approach here with the GetCommand() method gives:
SqlCommand GetCommand(string columnName)
{
string filter = "SELECT MyColumn1 AS #columnName FROM MyTable";
SqlCommand command = new SqlCommand(filter);
command.Parameters.Add(new SqlParameter("columnName", SqlDbType.NVarChar)
{ Value = columnName });
return command;
}
But, when the command is executed, it results in:
An exception of type 'System.Data.SqlClient.SqlException' occurred in MyApplication.exe but was not handled in user code
Additional information: Incorrect syntax near '#columnName'.
I see there are plenty of questions on SO and the web in general about use of SqlParameter, but seemingly none that touch on their use with column aliases.
There's no indication in Microsoft's SqlParameter documention either. From this, I noticed that the SqlParameter.Direction property defaults to ParameterDirection.Input; the column alias is used in output, so I tried InputOutput and Output, but it made no difference to the results.
Short answer: you can't.
Column Aliases are not parameterizable. They are identifiers in the SQL language, not values - just like the column name itself, or the table name.
"Get me column X from table Y and name it Z in the result set." None of X, Y or Z are parameterizable.
Note that this is not a limitation of SqlParameter but of the SQL language as implemented by Sql Server.
Parameters are not designed for aliasing TSQL columns. If you need an alias, just give it one in the TSQL. Additionally, the In/Out aspect of the parameter is for cases where the query modifies the parameter during running. Such as an output parameter of a stored procedure.
In truth, what it appears you're trying to do is get a dataset where the returned column name is based upon an inputted value.
I would use a data adapter to fill a data table, and then just rename the column to the desired value.
dataTable.Columns["MyColumn1"].ColumnName = columnName;
Strange thing to do, but you can build some sql with the parameter and then exec it.
Declare #sql VarChar(255)
Set #sql = 'SELECT ClientNumber AS ' + #columnName + ' FROM Ib01'
Exec(#sql)
You can't parameterize schema names and aliases. If you must have dynamic aliases, you'll need to protect yourself from SQL injection in the application layer.
Consider creating a whitelist of valid aliases, and only select from the whitelist.
If the alias comes from user input, you'll have to validate/sanitize the input.
Edit: It's a bad practice to rely on the application layer to prevent SQL injection attacks. But sometimes business drivers or other reasons force this to happen. My suggestions here are ways to mitigate the risk of SQL injection if you are forced to do this.
INSERTing values without parameters is fully understandable why it shouldn't be allowed, where you e.g. want to prevent sql-injection. However I do not understand why it's still a big no doing the following as well:
cmd.CommandText = "SELECT * FROM [Students]
WHERE StudentID = " + studentID + ";";
int getID = (int)cmd.ExecuteScalar();
What's the harm in it when just SELECTing? I don't really understand the point with parameters below. I'm not questioning it, I just want to know the reason why parameters is necessary and what consequences I could get from the code above instead using the option below.
var pStudentID = new SqlParameter("#studentID", SqlDbType.Int);
pStudentID.Value = studentID;
cmd.Parameters.Add(pStudentID);
There are two reasons it's better to use parameters.
Sql Injection - Your first example would be susceptible to a sql injection attack. What this means is if the studentID was being input from a web form, some one could use a '-- to comment out the select string and issue other commands against the database.
Prepare - If you use parameters you can prepare the sql statement, which is sort of a precompile of the syntax. This can be slightly more performant in high volume situations.
Edit: I came across this video on reddit the other day, which is a great example of how sql injection works.sql injection
Assume this input:
var studentID = "''; drop table users;--"
cmd.CommandText = "SELECT * FROM [Students]
WHERE StudentID = " + studentID + ";";
This would if calling this select delete the table users completely.
Parameters would help by approving only legitimate input to be added to the query.
I am writing a generic sqldump utility that takes a DSN and a table name and dumps the contents to a file. It's an internal app so SQL Injection is not a serious threat, but I don't want to have to worry about it. The thing is, the variable part of the query is actually the tablename, so the query is going to look like:
select * from [tablename];
...which I don't imagine will work well with the OdbcCommand's parameterized query support. I am also trying to support all types of DSN's as generically as I can, regardless of the driver on the other side of the DSN.
Is there some universal way to sanitize my tablename input to protect against all SQL Injection using the OdbcCommand object?
I'd check the user input against the list of tables you know are there, using code roughly like what's posted here to retrieve the table list (code from the link included for posterity):
class Program
{
static void Main(string[] args)
{
string connectionString = GetConnectionString();
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Connect to the database then retrieve the schema information.
connection.Open();
DataTable table = connection.GetSchema("Tables");
// Display the contents of the table.
DisplayData(table);
Console.WriteLine("Press any key to continue.");
Console.ReadKey();
}
}
That said, I agree with #KeithS above. This is probably a Bad Idea.
The only special character in a [] quoted identifier for SQL Server is ], and it can be escaped by passing ]]. So for that, "select * from [" + tableName.Replace("]", "]]") + "];" should be safe. Other systems, however, may use other escape mechanisms, so this is not a full solution if you want to connect to a different type of database.
Alternatively, consider each character, and see if it is a valid character for table names you wish to support. If you say table names only contain letters, digits, and/or whitespace, then SQL injection is not possible, because you'll never be able to unquote the [quoted table name].
You could first query the information_schema to find out if the table exists:
select *
from information_schema.tables
where table_schema = #your_database_name and table_name = #table_name
This query can be parameterized and is NOT prone to SQL injections.
Following that, you can issue your select * from #table_name query.
If the table name is enclosed in [ ] then just do not allow table names to contain "]". ] could be used by malicious people to terminated the sql command and to introduce dangerous code.
If you are constructing the sql like this
string sql = "SELECT * FROM [" + tablename + "]";
and the tablename is defined like this
string tablename = "tablename]; DELETE FROM [tablename";
The resulting sql becomes
SELECT * FROM [tablename]; DELETE FROM [tablename];
However, this is only possible if the table name contains a ].
Note:
If you are replacing string values like this, then replacing a single quote by two single quotes makes it safe too.
string sql = "SELECT * FROM tbl WHERE Name = '" + input.Replace("'","''") + "'";
For example, this is the code that I am using:
String commandString = "UPDATE Members SET UserName = #newName , AdminLevel = #userLevel WHERE UserID = #userid";
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["sqlconnectionstring"].ConnectionString))
{
SqlCommand cmd = new SqlCommand(commandString, conn);
cmd.Parameters.Add("#newName", newName);
cmd.Parameters.Add("#userLevel", userLevel);
cmd.Parameters.Add("#userid", userid);
conn.Open();
cmd.ExecuteReader();
Reader.Close();
}
That code looks fine. Parameterisation is the way to go, as opposed to concatenating user-supplied values in an adhoc SQL statement which can open you up to sql injection attacks. This can also help with execution plan reuse.
The only thing I'd add, is I prefer to explicitly define the datatype and sizes of the parameters. For example, if you don't then, as an example, all string values will get passed in to the database as NVARCHAR instead of VARCHAR. Hence I like to be explicit.
It's safe against SQL injection because it's parameterized. Other security concerns, such as ensuring that #userid is not spoofed, are separate security concerns that should be dealt with in other layers of your application.
That's still a static query string. It's not really "dynamic" sql until you also build parts of the string on the fly — something like this:
var sql = "SELECT columns FROM Table WHERE 1=1";
if (!string.IsNullOrEmpty(txtName.Text)) sql += " AND Name LIKE '%' + #Name + '%'";
if (!string.IsNullOrEmpty(txtDesc.Text)) sql += " AND CONTAINS(DESCRIPTION, #description)";
But even so, this is still "safe" in the sql injection sense as long as you continue to use parameters for every part of the query that originates with user input.
.NET, Java and other high level database API's in various language often provide techniques known as prepared statements and parameter binding as opposed to sending plain text commands to the Database server. What I would like to know is what happens when you execute a statement like this:
SqlCommand cmd = new SqlCommand("GetMemberByID");
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter param = new SqlParameter("#ID", memberID);
para.DbType = DbType.Integer;
cmd.Parameters.Add(param);
I know this is a best practice. SQL injection attacks are minimized this way. But what exactly happens under the hood when you execute these statements? Is the end result still a SQL safe string? If not, what is the end result? And is this enough to prevent SQL injection attacks?
The MySQL manual page on prepared statements provides lots of information (which should apply to any other RDBMS).
Basically, your statement is parsed and processed ahead of time, and the parameters are sent separately instead of being handled along with the SQL code. This eliminates SQL-injection attacks because the SQL is parsed before the parameters are even set.
in layman terms: if a prepared statement is sent then the DB will use a plan if it is available, it doesn't not have to recreate a plan every time this query is sent over but only the values of the params have changed. this is very similar to how procs work, the additional benefit with procs is that you can give permission through procs only and not to the underlying tables at all
If you're using MS SQL, load up the profiler and you'll see what SQL statements are generated when you use parameterised queries. Here's an example (I'm using Enterprise Libary 3.1, but the results are the same using SqlParameters directly) against SQL Server 2005:
string sql = "SELECT * FROM tblDomains WHERE DomainName = #DomName AND DomainID = #Did";
Database db = DatabaseFactory.CreateDatabase();
using(DbCommand cmd = db.GetSqlStringCommand(sql))
{
db.AddInParameter(cmd, "DomName", DbType.String, "xxxxx.net");
db.AddInParameter(cmd, "Did", DbType.Int32, 500204);
DataSet ds = db.ExecuteDataSet(cmd);
}
This generates:
exec sp[underscore]executesql N'SELECT * FROM tblDomains WHERE DomainName = #DomName AND DomainID = #Did',
N'#DomName nvarchar(9),
#Did int',
#DomName=N'xxxxx.net',
#Did=500204
You can also see here, if quotation characters were passed as parameters, they are escaped accordingly:
db.AddInParameter(cmd, "DomName", DbType.String, "'xxxxx.net");
exec sp[underscore]executesql N'SELECT * FROM tblDomains WHERE DomainName = #DomName AND DomainID = #Did',
N'#DomName nvarchar(10),
#Did int',
#DomName=N'''xxxxx.net',
#Did=500204