Why table params aren't allowed in SQL Server? Is there any solution to this?
Example:
using (SqlCommand myCommand = new SqlCommand("SELECT * FROM #table WHERE USERNAME=#username AND PASSWORD=HASHBYTES('SHA1',
#password)", myConnection))
{
myCommand.Parameters.AddWithValue("#table", table);
myCommand.Parameters.AddWithValue("#username", user);
myCommand.Parameters.AddWithValue("#password", pass);
myConnection.Open();
SqlDataReader myReader = myCommand.ExecuteReader())
...................
}
Thanks.
You can't paramaterise that part of the SQL. The server needs to know the name of the table to be able to 'prepare' the query, which is done before the parameters are processed.
You might dynamically generate the query, but that may open you up to SQL injection attacks and run-time SQL syntax errors. Also, there is a saving to be had if an SQL statement can be cached by the server - you'll loose that if every query is dynamically generated.
Why? Because the benefit of flexibility is minor compared to the nightmare it would create in query optimization and validation.
As a sidenote, even if it was recognised you'd be getting a quoted string in the SQL, not just the table name. Dynamic SQL with heavy validation is the only real way of doing this.
If you have to pass a table of values...
XML parameter
CSV (String) parameter
Parse in SQL. See "Arrays and Lists in SQL Server 2005"
Otherwise, what are you trying to do?
Edit: I've got it now. As others mentioned, SQL does not work like that.
No, you cannot pass the table name as a param.
The best way would be to try using String.Format for the table name.
I would try to ilustrate my point of view about this with an example:
If you go to buy a car, you can "parametrize" some thinks: You can change the colour, may be some variations of the engine, you can put an MP3 or not, ... but you cant change the car model. If you change the car model, this is not a parameter, this is another car.
It is the same with sql query, the table is not a parameter is part of the sentence itself, same way that the command is (select, update) .. so you can't do #command from #table. If you change the table, this is another sentence, like the car.
(this is not a technical "because" answer for you question, but a conceptual point of view for better understanding of the techical part that others are posting)
My two cents.
Related
This question already has answers here:
How does SQLParameter prevent SQL Injection?
(4 answers)
Closed 8 years ago.
I hope this is the right place to ask, how does parameters.addwithvalue work? I am thinking in a way to protect against SQL injection? I have been looking quite a lot on Stackoverflow, and a lot of people say "//Against sql injection". I have been using it blindly, but now that I have to hand in a paper about my assignment I need to explain why it is protection. I have been trying to find something on MSDN, found this one:
SQL injection But it uses the parameters.add. I then read that they replace .Add with .AddWithValue, is that true? Any official on this then?
So basically, anyone better in searching for some official paperwork that it protect against SQL injection? Or can tell me how it works?
I am not trying to make you do my work, I just can't find it my self.
I am using it like this:
using (SqlConnection conn = new SqlConnection(connectionString))
using (var cmd = conn.CreateCommand())
{
conn.Open();
String queryString = "DELETE FROM dbo.SecurityAccess WHERE Username = ' #Username ";
cmd.CommandText = queryString;
cmd.Parameters.AddWithValue("#Username", Username);
cmd.ExecuteNonQuery();
}
From SQL Injection point of view using parameters is usually safe (subject to what you do with those parameters in the SQL...). Your example is safe. How one adds the parameters makes no difference from the SQL Ibjection point of view, but makes a lot of difference from ADO.Net and SQL performance point of view. AddWithValue is an anti-pattern because of performance problems related to parameter type and size. In your example the #UserName will be a parameter of type NVARCHAR, which will likely make the WHERE Username=#UserName predicate unsarg-able (will not use an index on Username). The execution result would be dreadful.
A potential solution to the datatype conversion is to use the explicit Add method instead of AddWithValue, which takes the datatype as second parameter. More details on this here.
For more details I urge you to read How Data Access Code Affects Database Performance.
In Short parameters allow for type safe and length checks on the data. Enabling a defense against SQL injection, they do not prohibit SQL injection completely you still need to check your inputs.
SO Answer on similar topic.
Good article explaining how parameters do not prevent SQL injection 100%
SQL Injection Example (Taken from MSDN:)
Consider what happens when a user types the following string in the SSN text box, which is expecting a Social Security number of the form nnn-nn-nnnn.
' ; DROP DATABASE pubs --
Using the input, the application executes the following dynamic SQL statement or stored procedure, which internally executes a similar SQL statement.
// Use dynamic SQL
SqlDataAdapter myCommand = new SqlDataAdapter(
"SELECT au_lname, au_fname FROM authors WHERE au_id = '" +
SSN.Text + "'", myConnection);
// Use stored procedures
SqlDataAdapter myCommand = new SqlDataAdapter(
"LoginStoredProcedure '" +
SSN.Text + "'", myConnection);
The developer's intention was that when the code runs, it inserts the user's input and generates a SQL the following statement.
SELECT au_lname, au_fname FROM authors WHERE au_id = '172-32-9999'
However, the code inserts the user's malicious input and generates the following query.
SELECT au_lname, au_fname FROM authors WHERE au_id = ''; DROP DATABASE pubs --'
Common ways to avoid Injection attacks.
•Constrain and sanitize input data. Check for known good data by validating for type, length, format, and range.
•Use type-safe SQL parameters for data access. You can use these parameters with stored procedures or dynamically constructed SQL command strings. Parameter collections such as SqlParameterCollection provide type checking and length validation. If you use a parameters collection, input is treated as a literal value, and SQL Server does not treat it as executable code. An additional benefit of using a parameters collection is that you can enforce type and length checks. Values outside of the range trigger an exception. This is a good example of defense in depth.
•Use an account that has restricted permissions in the database. Ideally, you should only grant execute permissions to selected stored procedures in the database and provide no direct table access.
•Avoid disclosing database error information. In the event of database errors, make sure you do not disclose detailed error messages to the user.
if you do not use Parameterised queries with above command then it looks like :
string queryString="DELETE FROM dbo.SecurityAccess WHERE Username = '"+txtUserName.Text+"'";
in the above command username would be assigned whatever user enters in TextBox( ex: txtUserName).
if user wants to inject some behaviour( adding delete/update or whatever he wants to do) he can enter following in TextBox (txtUserName)
=> "'';delete * from users"
then the above command with given username value looks like this:
string queryString="DELETE FROM dbo.SecurityAccess WHERE Username = '';delete * from users";
finally the above command would delete all the records from the users table.
if i allow users to type in a textbox and then perform a search against my db, there is the potential for sql injection. i could use regex, thats my first thought. but i had a better idea. why not see if what they typed has any SQL keywords in it. Im using an SQL Server database, in an ASP.NET program with c#, i thought microsoft would have offered an easy solution to what i am talking about. the best i can find is in this article:
Is it a programmatic way to get SQL keywords (reserved words)
which is probably what ill end up doing, but my problem is i still have to type out the entire list of keywords, there is around a hundred. sure i could be done by now instead of searching and asking this question. but isnt there an easier way? right now im going to:
1 Create a Hashset
2 add all the keywords to the hashset (cmon)
3 validate user input against the hashset
would love to see step 2 be made much easier, any other suggestions about sql injections are also appreciated
If you are passing the search text into a stored procedure and doing something like
WHERE search LIKE #inputParam
SQL will not allow injection to incur in the above fragment.
However, if you are building a string variable and then using EXEC #sql or sp_execute #SQL, you are vulnerable to SQL injection.
In my opinion, you would be better off avoiding the problem of checking for SQL keywords altogether by using parameterized SQL. Here's an example in C# (assuming you're using MS SQL Server):
using (SqlCommand sqlcmd = new SqlCommand("SELECT * FROM [MyDB].[dbo].[MyTable] WHERE [SomeColumn] = #SomeValue", sqlconnection))
{
sqlcmd.Parameters.AddWithValue("#SomeValue", strUsersSearchString);
// use sqlcmd.ExecuteReader() here
// or whatever you normally would
}
Here's another example on MSDN. This one is using parameterized SQL to call a stored procedure: http://msdn.microsoft.com/en-us/library/ff648339.aspx#paght000002_step3
Use named parameters. Dapper-dot-net makes it really easy to do this:
IEnumerable<Row> results = connection.Query<Row>("SELECT column FROM table WHERE title LIKE #query", new { query = "SEARCHTERM" });
I was debugging a database operation code and I found that proper UPDATE was never happening though the code never failed as such. This is the code:
condb.Open();
OleDbCommand dbcom = new OleDbCommand("UPDATE Word SET word=?,sentence=?,mp3=? WHERE id=? AND exercise_id=?", condb);
dbcom.Parameters.AddWithValue("id", wd.ID);
dbcom.Parameters.AddWithValue("exercise_id", wd.ExID);
dbcom.Parameters.AddWithValue("word", wd.Name);
dbcom.Parameters.AddWithValue("sentence", wd.Sentence);
dbcom.Parameters.AddWithValue("mp3", wd.Mp3);
But after some tweaking this worked:
condb.Open();
OleDbCommand dbcom = new OleDbCommand("UPDATE Word SET word=?,sentence=?,mp3=? WHERE id=? AND exercise_id=?", condb);
dbcom.Parameters.AddWithValue("word", wd.Name);
dbcom.Parameters.AddWithValue("sentence", wd.Sentence);
dbcom.Parameters.AddWithValue("mp3", wd.Mp3);
dbcom.Parameters.AddWithValue("id", wd.ID);
dbcom.Parameters.AddWithValue("exercise_id", wd.ExID);
Why is it so important that the parameters in WHERE clause has to be given the last in case of OleDb connection? Having worked with MySQL previously, I could (and usually do) write parameters of WHERE clause first because that's more logical to me.
Is parameter order important when querying database in general? Some performance concern or something?
Is there a specific order to be maintained in case of other databases like DB2, Sqlite etc?
Update: I got rid of ? and included proper names with and without #. The order is really important. In both cases only when WHERE clause parameters was mentioned last, actual update happened. To make matter worse, in complex queries, its hard to know ourselves which order is Access expecting, and in all situations where order is changed, the query doesnt do its intended duty with no warning/error!!
Within Access, an ADODB.Command object ignores parameter names. In fact I can refer to a parameter using a bogus name (which doesn't even exist in the SQL statement) and ADO doesn't care. All it seems to care about is that you supply parameter values in the exact same order as those parameters appear in the SQL statement. BTW, that is also what happens if I build the SQL statement with ? place-holders instead of named parameters.
While I realize that your question is about c# and OleDbCommand, it looks to me like Dot.Net's OleDbCommand may be operating the same as Access' ADODB.Command. Unfortunately, I don't know Dot.Net ... but that is my hunch. :-)
The order is important because of the use of ? placeholders in the command string.
If you want to list the parameters in any order, it's best to use named parameters, such as #word, #sentence, etc.
condb.Open();
OleDbCommand dbcom = new OleDbCommand("UPDATE Word SET word=#word,sentence=#sentence,mp3=#mp3 WHERE id=#id AND exercise_id=#exercise_id", condb);
dbcom.Parameters.AddWithValue("#id", wd.ID);
dbcom.Parameters.AddWithValue("#exercise_id", wd.ExID);
dbcom.Parameters.AddWithValue("#word", wd.Name);
dbcom.Parameters.AddWithValue("#sentence", wd.Sentence);
dbcom.Parameters.AddWithValue("#mp3", wd.Mp3);
I have been doing some tests with using OleDbCommand and its parameters collection against an Access DB. The ordering of parameters is of course necessary, since this is a limitation of the OLE DB .NET provider. But there is a problem that you can encounter when using question marks as place holders.
Say you have a query ("stored procedure") in your Access DB that looks like this, very simplified here:
parameters
prmFirstNumber Long,
prmSecondNumber Long;
select
fullName
from
tblPersons
where
numberOfCars < prmFirstNumber And
numberOfPets < prmSecondNumber And
numberOfBooks beteween prmFirstNumber And prmSecondNumber
Here you see that simply changing to question marks would break the query.
I have found though, as a solution to this, that you can actually use names for parameters. So you can let the query above remain as it is. You just have to use the same order when you run the query. Like in this case, you first add the parameter prmFirstNumber and then prmSecondNumber, and then you run the query.
When reusing parameters, i.e. executing a query more than once and setting new values for the parameters each time, one must call the prepare method of the command object right after having defined the parameters. There are some details there that need to be fulfilled too, look at the documentation for "prepare". Not calling prepare causes strange behaviour without error messages which can corrupt your database or cause wrong information to be presented to users.
I can add also that when queries are stored in the Access DB with parameters specified, like in my example above, then the ordering of the parameters is unambiguously defined by the parameters-section.
I also made a routine, "retrieveDeclaredJetParametersInOrder", which automatically populates an OleDbCommand object with those named parameters, in the correct order. So my code can look like this:
Dim cmd As New OleDbCommand("qryInAccessDB", Conn)
cmd.CommandType = CommandType.StoredProcedure
Conn.Open()
retrieveDeclaredJetParametersInOrder(cmd)
cmd.Parameters("prmOneOfTheParametersPerhapsTheLastOneDeclared").Value = 1
cmd.Parameters("prmAnotherone").Value = 20
cmd.Parameters("prmYetAnotherPerhapsTheFirstOneDeclared").Value = 300
cmd.ExecuteNonQuery()
Conn.Close()
So, as you see, I can handle it as if parameters are named, and never have to bother with their ordering.
The retrieveDeclaredJetParametersInOrder of course adds extra time to execution, since it involves an extra call to the DB, where it retrieves the SQL-text and then parses out the parameter names and types.
string sqlQuery = "unknown";
I need to write a function which receives a sql query as parameter e.g. sqlQuery. I would like to execute it only if it is select statement and return data. In other case, if parameter sqlQuery contains delete, update or truncate, the function should return null.
I wonder if there is way to achieve this without parsing contents of parameter sqlQuery.
I would like to do this using c sharp for oracle queries.
Any tips. Thanks.
Update:
This should work for all kinds of users with all privileges.
Run the query in the context of a user who only has select privileges. Any other type of query will error out.
SET TRANSACTION READ ONLY, then execute the string. If it attempts to modify data, it will generate an ORA-01456 error. You can trap this and return whatever you want.
If you really have to work with a constructed string that will operate on the database, you should use the DBMS_ASSERT database package to make sure you have a pure query that's not subject to SQL injection. There's a nice paper on the Oracle site about that here.
The basics are:
only give the minimum privileges necessary, for example only giving the user "select" as described in an earlier reply. And then only on the minimum necessary set of tables. Views are really helpful here in limiting access.
Use bind variables where that's possible.
If you can't use bind variables then check the purity of your statement using DBMS_ASSERT
You can probably search the string for keywords like "update", "delete", "truncate" and all the other ways you can do ddl or dml on the table, but it is very error-prone. You have to eliminate strings in the query which might have these keywords and there are a lot of keywords that you have to take into account.
If your requirement is to return null, Why not give just the select privilege on the necessary objects and return null if you encounter the Insufficient Privileges error?
http://download.oracle.com/docs/cd/E11882_01/server.112/e17069/strms_trapply.htm#STRMS1065
I would not allow the client to specify a SQL select string. Too many possible attack vectors.
Have you considered using Linq? The caller could pass a Func<T, bool> that could be passed to a Where clause. Since Linq will generate the select statement for you, there's no possibility of a non-select statement occuring.
Bear in mind a SELECT column FROM table FOR UPDATE will still take an exclusive lock on every row on that table. And it only needs SELECT privileges (none of INSERT, UPDATE or DELETE are required).
You can use ADO.NET SqlCommand http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.aspx. It has methods ExecuteReader for a select type query and ExecuteNonQuery for other sql expression, you jest set the CommandText string attribute. If I'm right it throws exception if the query is not a select in ExecuteReader but you must check it.
string sqlQuery = "("+evil_sql+")";
Only a subquery can start with a parentheses. This will stop DML, DDL, and the FOR UPDATE issue that Gary mentioned. You still have to execute everything, just catch all the errors. I've done this on a public-facing website without any issues.
Even if your user is not directly granted anything you'll need to check for unnecessary PUBLIC grants. And of course keep your system patched. There have been exploits in functions that can be called in a SELECT.
I have a C# program that I want to dynamically create databases with. Although only privileged users will be using this application, I want to follow best practices security wise. How do I go about doing this? I don't think I can use a parameterized query in this case since I'm not wanting to pass in a string, I want to pass in an identifier.
Here's the insecure way I've got in there now as a placeholder:
using (MySqlConnection connection = new MySqlConnection(connectionString))
using (MySqlCommand command = connection.CreateCommand())
{
connection.Open();
command.CommandText = "DROP SCHEMA IF EXISTS " + schema;
command.ExecuteNonQuery();
command.CommandText = "CREATE SCHEMA " + schema;
command.ExecuteNonQuery();
}
Any ideas on this?
You can use the backtick delimiter to ensure the strings are correctly quoted and ensure that whatever is entered by the user is used as a literal identifier.
See: http://dev.mysql.com/doc/refman/5.0/en/identifiers.html and Using backticks around field names
That way the command that is passed to the server would look like: "DROP SCHEMA IF EXISTS foo" which will tolerate using reserved words and other incorrect string values as identifiers.
simply run a regular expression against schema to verify that it is a valid name? (contains no spaces, colons etc). I'm not sure if you can or can't use parameterised queries in this case, but if you do, it will fail creating a database called TRANCATE TABLE users; anyway :)
I don't know why do you want to create databases dynamically, but I think the correct way of doing this is not to generate databases dynamically.
The only exception I can think of is if you were creating your own database management system. If that's the case, maybe you could look at the source code of some open source MySQL database manager, like phpMyAdmin. (I don't know any for .Net.)