I'm trying to replace parameters in a string to execute in an Npgsql query.
The problem is, when it replaces the parameter by its value in the string it adds unnecessary parentheses and so the query returns an error.
NAME_SCHEMA_DB and NAME_ADMIN_DB are string constants and
ExecuteCommand just takes an NpgsqlCommand and executes it.
This is my code:
String qdropSchema = #"DROP SCHEMA IF EXISTS #name_schem CASCADE";
String qCreateSchema = #"CREATE SCHEMA #name_schem AUTHORIZATION #name_admin";
DbCommand commandeDrop = new NpgsqlCommand(qdropSchema);
commandDrop.Parameters.Add(new NpgsqlParameter("#name_schem", NAME_SCHEMA_DB));
DbCommand commandCreate = new NpgsqlCommand(qCreateSchema);
commandCreate.Parameters.Add(new NpgsqlParameter("#name_schem", NAME_SCHEMA_DB));
commandCreate.Parameters.Add(new NpgsqlParameter("#name_admin", NAME_ADMIN_DB));
ExecuteCommand(commandDrop);
ExecuteCommand(commandCreate);
This is what the SQL query it tries to run when it reaches ExecuteCommand(commandDrop)
DROP SCHEMA IF EXISTS (('test_schemaName')) CASCADE;
I 'm not sure why it adds the extra parentheses and single quotes. Normally, I'd want the query it runs to be
DROP SCHEMA IF EXISTS test_schemaName CASCADE;
SQL parameters are generally only valid for values (e.g. the values of fields) - not field names and table names etc. While it's annoying, you'll probably need to embed these names directly into the SQL.
You should be very careful doing that, of course - anywhere that it might be from user input, you should use a whitelist of some form.
Related
I need to update a record in a table but it is possible that the record does not exist, so I would need to insert the record.
Below is a SQL statement that accomplished my goal by trying to update the record first and if it doesn't exist it performs an insert. What I am wondering if it can be executed directly via ADO.NET command object or does it need to go into a stored procedure.
UPDATE MyTable SET ReservationDate = #ReservationDate WHERE CustomerNumber = XXXX;
IF ##ROWCOUNT=0
INSERT INTO MyTable (CustomerNumber , ReservationDate)
VALUES (#CustomerNumber , #ReservationDate)
If I could execute it via the command object without a stored procedure it would mean one less dependency for deployment time (i.e. deploying the stored procedure).
The MERGE command in T-SQL works for just this scenario
string cmdText = #"MERGE MyTable T
USING (SELECT #CustomerNumber As CustomerNumber) as S
ON T.CustomerNumber = S.CustomerNumber
WHEN MATCHED UPDATE SET ReservationDate = #ReservationDate
WHEN NOT MATCHED INSERT INTO (CustomerNumber , ReservationDate)
VALUES (#CustomerNumber , #ReservationDate)";
It is just one string of text wrapped for readability in more lines thanks to the verbatim character #
With MERGE you start defining your TARGET table (T), then you build a pseudotable called SOURCE (S) with the parameter that contains the value for the primary key field in TARGET. Now the two tables are JOINED ON the field CustomerNumber. The product of this join could be MATCHED or NOT MATCHED depending of the previous presence of the record in the TARGET table. The remainder of the query is probably self explanatory, just notice that in the two actions (UPDATE and INSERT) there is no need to repeat the MyTable name (it is the TARGET)
By the way, yes you could pass multiple commands to the SqlCommand separated by a semicolon
I have the following code:
string strTruncateTable = "TRUNCATE TABLE #TableNameTruncate";
SqlCommand truncateTable = new SqlCommand(strTruncateTable, myConnection);
truncateTable.Parameters.AddWithValue("TableNameTruncate", tbTableName.Text);
truncateTable.ExecuteNonQuery();
Whenever I run the application, I get the following error:
Incorrect syntax near '#TableNameTruncate'
How can I fix the issue?
How can I fix the issue?
By specifying the table name as part of the SQL. Table and column names can't be parameterized in most database SQL dialects, including SQL Server.
You should either perform very stringent validation on the table name before putting it into the SQL, or have a whitelisted set of valid table names, in order to avoid SQL injection attacks in the normal way.
You can only parameterized your values, not your column names or table names no matter you use DML statements or DDL statements.
And by the way, parameters are supported for Data manipulation language operations not Data Manipulation language operations.
Data manipulation language =
SELECT ... FROM ... WHERE ...
INSERT INTO ... VALUES ...
UPDATE ... SET ... WHERE ...
DELETE FROM ... WHERE ...
TRUNCATE TABLE is a Data Definition Language statement. That's why you can't use TRUNCATE TABLE with parameters even only if you try to parameter a value. You need to specify it as a part of SQL query.
You might need to take a look at the term called Dynamic SQL
As mentioned by Jon Skeet, table name cannot be parametrized for truncate operation.
To fix this issue, fully qualified query needed to be written.
So you can put a conditional check by the parameter value #TableNameTruncate and using if or switch case statement create fully qualified query then execute it.
or simply
string strTruncateTable = "TRUNCATE TABLE " + TableNameTruncate.Value;
SqlCommand truncateTable = new SqlCommand(strTruncateTable, myConnection);
truncateTable.Parameters.AddWithValue("TableNameTruncate", tbTableName.Text);
truncateTable.ExecuteNonQuery();
Does it make sense to prevent sql injection for a create statement? How could I do this?
I wanted to use command parameters, but it doesn't seam to work:
Example:
var createSql = "CREATE TABLE #TableName (#Column1 ...)";
var command = new SqlCommand();
command.CommandText = createSql;
command.Parameters.AddWithValue("#TableName", "XYZ");
command.Parameters.AddWithValue("#Column1", "Col");
// somewhere else
command.Connection = connection;
command.ExecuteNonReader(); // --> exception: invalid syntax at #TableName
Edit: The Column and TableNames are generated depending on other data. Indirectly also on userinput, yes. The given create statement is incomplete. It is just an example.
My problem is, that it seems that the command parameters are not replaced.
You cannot use bind variables for table or column names.
So you'll have to construct that SQL statement using string concatenation and if necessary, manual quoting/escaping, and be very careful how you go about it.
Direct user input would be very dangerous, but if it is only indirectly, for example just choosing options for auto-generated names, you should be okay.
in this specific code there can't be sql injection because the user doesn't have a say in this.
sql injection are caused when user input is either incorrectly filtered for string literal escape characters embedded in SQL statements or user input is not strongly typed and unexpectedly executed
in your case the user doesn't input anything so there is no worry.
however, your create table query is invalid. you can read here on create statement.
you can't use the "..." in a create statement, and every column must have a type
In both queries 1 and 2, the text from the textbox is inserted into the database. What's the significance of the parameterized query here?
Passing txtTagNumber as a query parameter
SqlCommand cmd = new SqlCommand("INSERT INTO dbo.Cars " +"VALUES(#TagNbr);" , conn);
cmd.Parameters.Add("#TagNbr", SqlDbType.Int);
cmd.Parameters["#TagNbr"].Value = txtTagNumber.Text;
Converting txtTagNumber to an integer before constructing the query
int tagnumber = txtTagNumber.Text.ToInt16(); /* EDITED */
INSERT into Cars values(tagnumber.Text); /* then is it the same? */
Also, here I would use Regular Expression validation to stop insertion of illegal characters.
Parameterized queries do proper substitution of arguments prior to running the SQL query. It completely removes the possibility of "dirty" input changing the meaning of your query. That is, if the input contains SQL, it can't become part of what is executed because the SQL is never injected into the resulting statement.
Imagine a dynamic SQL query
sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND
Pass=' + password
so a simple sql injection would be just to put the Username in as ' OR
1=1-- This would effectively make the sql query:
sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS='
+ password
This says select all customers where they're username is blank ('') or
1=1, which is a boolean, equating to true. Then it uses -- to comment
out the rest of the query. So this will just print out all the
customer table, or do whatever you want with it, if logging in, it
will log in with the first user's privileges, which can often be the
administrator.
Now parameterized queries do it differently, with code like:
sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'
parameters.add("User", username) parameters.add("Pass", password)
where username and password are variables pointing to the associated
inputted username and password
Now at this point, you may be thinking, this doesn't change anything
at all. Surely you could still just put into the username field
something like Nobody OR 1=1'--, effectively making the query:
sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND
Pass=?'
And this would seem like a valid argument. But, you would be wrong.
The way parameterized queries work, is that the sqlQuery is sent as a
query, and the database knows exactly what this query will do, and
only then will it insert the username and passwords merely as values.
This means they cannot effect the query, because the database already
knows what the query will do. So in this case it would look for a
username of "Nobody OR 1=1'--" and a blank password, which should come
up false.
Source: lavamunky.com; Nov 2011
SQL injection happens when a possible parameter has SQL within it and the strings are not handled as it should be
eg:
var sqlquerywithoutcommand = "select * from mytable where rowname = '" + condition+''";
and the condition is a string coming from the user in the request. If condition is malicious
say eg:
var sqlquerywithoutcommand = "select * from mytable where rowname = '" + "a' ;drop table mytable where '1=1"+"'";
you could end up running malicious scripts.
However, using parameters the input will be cleaned of any characters which might escape string characters, which means you can be ensured no matter what comes in it will not be able to run inject scripts.
Using the command object with parameters the SQL actually executed would look like this:
select * from mytable where rowname = 'a'';drop table mytable where 1=1'''
in essence it will be looking for a row with rowname = a';drop table mytable where 1=1'
and not running the remaining script.
Parameterized queries handles everything - why go to the trouble?
With parametrized queries, in addition to general injection, you get all the data types handled, numbers (int and float), strings (with embedded quotes), dates and times (no formatting problems or localization issues when .ToString() is not called with the invariant culture and your client moves to a machine with and unexpected date format).
Parameterized queries allow the client to pass the data separately form the query text.
Where on most free from text you would do validation + escaping.
Of course Parameterization don't help against other kind of injection, but as the parameter are passed separately, they are not use as execution text query.
A good analogy would be the "recent" execution bit used with most of the modern processor and Operating system to protect from buffer overflow. It still allows the buffer overflow but prevent the execution of the injected data.
It is quite understandable why one would feel so.
sqlQuery = "select * from users where username='+username+';"
vs
sqlQuery = "select * from users where username=#username;"
Both the above queries seem to do the same thing.But they actually don't.
The former uses input to for a query, the latter decides on the query but only substitutes the inputs as it is during the execution of the query.
To be more clear, the parameters' values are located some where on the stack where the variables' memory is stored and is used for search when needed.
So if we were to give ' OR '1'='1 as the input in username, the former would dynamically construct a new queries or queries as part of the sql query string sqlQuery which is then executed.
While on the same input, latter would search for ' OR '1'=' in the username field of the users table with the statically specified query in the query string sqlQuery
Just to consolidate it, this is how you use parameters to make query:
SqlCommand command = new SqlCommand(sqlQuery,yourSqlConnection);
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "#username";
parameter.Value = "xyz";
command.Parameters.Add(parameter);
public static bool TruncateTable(string dbAlias, string tableName)
{
string sqlStatement = string.Format("TRUNCATE TABLE {0}", tableName);
return ExecuteNonQuery(dbAlias, sqlStatement) > 0;
}
The most common recommendation to fight SQL injection is to use an SQL query parameter (several people on this thread have suggested it).
This is the wrong answer in this case. You can't use an SQL query parameter for a table name in a DDL statement.
SQL query parameters can be used only in place of a literal value in an SQL expression. This is standard in every implementation of SQL.
My recommendation for protecting against SQL injection when you have a table name is to validate the input string against a list of known table names.
You can get a list of valid table names from the INFORMATION_SCHEMA:
SELECT table_name
FROM INFORMATION_SCHEMA.Tables
WHERE table_type = 'BASE TABLE'
AND table_name = #tableName
Now you can pass your input variable to this query as an SQL parameter. If the query returns no rows, you know that the input is not valid to use as a table. If the query returns a row, it matched, so you have more assurance you can use it safely.
You could also validate the table name against a list of specific tables you define as okay for your app to truncate, as #John Buchanan suggests.
Even after validating that tableName exists as a table name in your RDBMS, I would also suggest delimiting the table name, just in case you use table names with spaces or special characters. In Microsoft SQL Server, the default identifier delimiters are square brackets:
string sqlStatement = string.Format("TRUNCATE TABLE [{0}]", tableName);
Now you're only at risk for SQL injection if tableName matches a real table, and you actually use square brackets in the names of your tables!
As far as I know, you can't use parameterized queries to perform DDL statements/ specify table names, at least not in Oracle or Sql Server. What I would do, if I had to have a crazy TruncateTable function, that had to be safe from sql injection would be to make a stored procedure that checks that the input is a table that is safe to truncate.
-- Sql Server specific!
CREATE TABLE TruncableTables (TableName varchar(50))
Insert into TruncableTables values ('MyTable')
go
CREATE PROCEDURE MyTrunc #tableName varchar(50)
AS
BEGIN
declare #IsValidTable int
declare #SqlString nvarchar(50)
select #IsValidTable = Count(*) from TruncableTables where TableName = #tableName
if #IsValidTable > 0
begin
select #SqlString = 'truncate table ' + #tableName
EXECUTE sp_executesql #SqlString
end
END
If you're allowing user-defined input to creep into this function via the tablename variable, I don't think SQL Injection is your only problem.
A better option would be to run this command via its own secure connection and give it no SELECT rights at all. All TRUNCATE needs to run is the ALTER TABLE permission. If you're on SQL 2005 upwards, you could also try using a stored procedure with EXECUTE AS inside.
CREATE OR REPLACE PROCEDURE truncate(ptbl_name IN VARCHAR2) IS
stmt VARCHAR2(100);
BEGIN
stmt := 'TRUNCATE TABLE '||DBMS_ASSERT.SIMPLE_SQL_NAME(ptbl_name);
dbms_output.put_line('<'||stmt||'>');
EXECUTE IMMEDIATE stmt;
END;
Use a stored procedure. Any decent db library (MS Enterprise Library is what I use) will handle escaping string parameters correctly.
Also, re:parameterized queries: I prefer to NOT have to redeploy my app to fix a db issue. Storing queries as literal strings in your source increases maintenance complexity.
Have a look at this link
Does this code prevent SQL injection?
Remove the unwanted from the tableName string.
I do not think you can use param query for a table name.
There are some other posts which will help with the SQL injection, so I'll upvote those, but another thing to consider is how you will be handling permissions for this. If you're granting users db+owner or db_ddladmin roles so that they can truncate tables then simply avoiding standard SQL injection attacks isn't sufficient. A hacker can send in other table names which might be valid, but which you wouldn't want truncated.
If you're giving ALTER TABLE permissions to the users on the specific tables that you will allow to be truncated then you're in a bit better shape, but it's still more than I like to allow in a normal environment.
Usually TRUNCATE TABLE isn't used in normal day-to-day application use. It's used for ETL scenarios or during database maintenance. The only situation where I might imagine it would be used in a front-facing application would be if you allowed users to load a table which is specific for that user for loading purposes, but even then I would probably use a different solution.
Of course, without knowing the specifics around why you're using it, I can't categorically say that you should redesign, but if I got a request for this as a DBA I'd be asking the developer a lot of questions.
Use parameterized queries.
In this concrete example you need protection from SQL injection only if table name comes from external source.
Why would you ever allow this to happen?
If you are allowing some external entity (end user, other system, what?)
to name a table to be dropped, why won't you just give them admin rights.
If you are creating and removing tables to provide some functionality for end user,
don't let them provide names for database objects directly.
Apart from SQL injection, you'll have problems with name clashes etc.
Instead generate real table names yourself (e.g DYNTABLE_00001, DYNTABLE_00002, ...) and keep a table that connects them to the names provided by user.
Some notes on generating dynamic SQL for DDL operations:
In most RDBMS-s you'll have to use dynamic SQL and insert table names as text.
Be extra careful.
Use quoted identifiers ([] in MS SQL Server, "" in all ANSI compliant RDBMS).
This will make avoiding errors caused by invalid names easier.
Do it in stored procedures and check if all referenced objects are valid.
Do not do anything irreversible. E.g. don't drop tables automatically.
You can flag them to be dropped and e-mail your DBA.
She'll drop them after the backup.
Avoid it if you can. If you can't, do what you can to minimize rights to other
(non-dynamic) tables that normal users will have.
You could use SQLParameter to pass in tableName value. As far as I know and tested, SQLParameter takes care of all parameter checking and thus disables possibility of injection.
If you can't use parameterized queries (and you should) ... a simple replace of all instances of ' with '' should work.
string sqlStatement = string.Format("TRUNCATE TABLE {0}", tableName.Replace("'", "''"));