I used parameters in the whereclause, but what about the variables for this {0}. Do I need to create a parameter for it to prevent sql injection?
("...inner join db1.dbo.table1.id on db2.dbo.table2.id = {0}.dbo.table3.id where name=#name",abc)
var abc = ddl2.SelectedItem.Text;
cmd.Parameters.AddWithValue("#name", ddl1.selectedvalue);
To the best of my knowledge, you can't actually 'paramaterize' database names/table names.
String.Format does not solve SQL injection in this case since it is possible for the user to change ddl2.SelectedItem.Text to whatever they want.
If you need a dynamic value for the database name, I suggest you either keep that value as a const or store it somewhere that you have complete control over/ is never sent or interpreted client side.
I would suggest you to use any ORM (object relational mapping) i.e Entity framework or n-hibernate etc. and use linq to write queries, that will prevent you application from SQL Injection.
Unfortunately, as Abbath already mentioned, this type of construct is not parameterizable. As Abbath mentioned, the best solution is to keep such arguments under your absolute control, but there are times, where such constructs are needed, and it may not be possible to have complete control over them.
For such scenarios, the best recommendation in this case is to escape the arguments. In this case, the DB name represented by {0} on your sample code.
There are two potential mechanisms to achieve this:
a) Create a mechanism that allows you to parameterize the query
Advantage: You can reuse the same solution from any driver (.Net, ODBC, etc.)
Disadvantage: A bit more work. You will not use select directly anymore on this case.
For example (I am including a simple example that has an inner join, just like your code):
CREATE PROC sp_MyQuery( #target_db_name sysname, #name nvarchar(100))
AS
BEGIN
DECLARE #cmd nvarchar(max)
DECLARE #parameters nvarchar(max)
SELECT #cmd = N'SELECT * FROM msdb.sys.objects inner join '
+ quotename(#target_db_name) + N'.sys.sql_modules
on msdb.sys.objects.object_id = '
+ quotename(#target_db_name) + N'.sys.sql_modules.object_id WHERE name = #name'
print #cmd -- See the command before it is executed.
set #parameters = N'#name nvarchar(100)'
EXEC sp_executesql #cmd, #parameters, #name = #name
END
go
-- Example of usage
DECLARE #target_db_name sysname = 'msdb'
DECLARE #name nvarchar(100) = 'sp_help_operator'
EXEC sp_MyQuery #target_db_name, #name
go
At this point, you can use SqlParameter objects as you would normally do. For example:
sqlcmd.CommandText = #"[dbo].[sp_MyQuery]";
sqlcmd.CommandType = System.Data.CommandType.StoredProcedure;
sqlcmd.Parameters.AddWithValue("#target_db_name", ddl0.selectedvalue);
sqlcmd.Parameters.AddWithValue("#name", ddl1.selectedvalue);
SqlDataReader reader = sqlcmd.ExecuteReader();
b) Escape the DB name within your CLR code
Advantage: Easier to implement
Disadvantage: App-specific solution, you need to be careful with potential Unicode–DB collation translation issues.
For example (same query as above):
sqlcmd.CommandText = String.Format(#"
SELECT * FROM msdb.sys.objects inner join [{0}].sys.sql_modules on msdb.sys.objects.object_id = [{0}].sys.sql_modules.object_id WHERE name = #name;",
ddl0.selectedvalue.Replace("]", "]]"));
sqlcmd.CommandType = System.Data.CommandType.Text;
sqlcmd.Parameters.AddWithValue("#name", ddl1.selectedvalue);
SqlDataReader reader2 = sqlcmd.ExecuteReader();
I typically recommend using solution (a) whenever it is possible, but both solutions should help you protect against SQL injection.
BTW. The following link may also be quite useful: https://blogs.msdn.microsoft.com/raulga/2007/01/04/dynamic-sql-sql-injection/
I hope information helps.
you dont need create parameter for abbc if you use string.format like this
var abc = ddl2.SelectedItem.Text;
string.format("...inner join db1.dbo.table1.id on db2.dbo.table2.id = {0}.dbo.table3.id where name=#name",abc)
cmd.Parameters.AddWithValue("#name", ddl1.selectedvalue);
Related
I need to add the schema value dynamically to the query. I was trying to construct it the way we usually use values but realised it does not work with the schema names the same way.
This was what I was trying to do
sql = "SELECT Name FROM [#dbo].[Members]";
...
command.Parameters.Add("#dbo", SqlDbType.VarChar).Value = "dbo";
I know I can construct the query by directly adding the variable in the query like this:
sql = $#"SELECT Name FROM [{parameter}].[Members]";
But I wanted to disallow any kind of SQL Injection so want to go ahead using the parameterized query as above.
Can anyone help with a possible idea to implement this?
TIA
The schema is not a parameter. Query parameters are equivalent to function parameters in eg C#. They're used to pass values. In SQL, the table and columns are equivalent to C# types and properties. You can't specify them by name. The schema in a SQL query is similar to the Namespace in C#. The table is equivalent to a Type. In C#, just because Sales.Record and Diagnostics.Record have the same type name doesn't mean the two types can be used the same way.
The question doesn't explain why the schema name is passed dynamically. It's almost certain there are easier, more efficient and safer ways to query similar tables in multiple schemas, but the solution would depend on the actual problem.
There are some techniques that can be used to make such a dynamic query safe if not efficient. I'd really, really try to avoid treating the schema as a value though.
Using QUOTENAME
One option, is to use QUOTENAME in a T-SQL script to construct a dynamic query. At least this way a syntax error will be thrown if the schema and table names are wrong:
sql = #"declare #sql nvarchar(max)='SELECT Name FROM ' + QUOTENAME(#dbo) + '.[Members]';
select #sql;
exec sp_executesql #sql;";
...
command.Parameters.Add("#dbo", SqlDbType.NVarChar,100).Value = "dbo";
QUOTENAME will convert something like sys].schemas; PRINT ''x''; -- to [[sys]].schemas; PRINT 'x'; --]. This will result in an error :
declare #sql nvarchar(max)= 'select * from [' +quotename('sys].schemas; PRINT ''x''; --')
select #sql
exec sp_executesql #sql;
--------
select * from [[sys]].schemas; PRINT 'x'; --]
Invalid object name '[sys].schemas; PRINT 'x'; --'.
It's too easy to make quoting mistakes with such scripts. This could be extracted into a stored procedure :
CREATE PROCEDURE GetMemberNameBySchema
#dbo nvarchar(100)
as
declare #sql nvarchar(max)='SELECT Name FROM ' + QUOTENAME(#dbo) + '.[Members]';
exec sp_executesql #sql;
Verify the Schema name
Query sys.schema to ensue the schema is correct before constructing the query. Let's say you're using Dapper (so I don't have to write all the ADO.NET code) :
var schema="dbo";
var isValid=connection.ExecuteScalar<bool?>(
"select 1 from sys.schema where name=#name",
new {name=schema});
//isValid will be null if nothing is found
if(isValid ==true)
{
var names=connection.Query($"SELECT Name FROM [{schema}].[Members]");
...
}
This is safe to do because the first query ensured the schema name is valid.
You can only pass parameters to dynamic query not object names as far as I know. You can do something like below:
Update after seeing Panagiotis' solution. we can take out the exists part outside. I am no expert in C# but something similar we want to do:
DECLARE #Table NVARCHAR(100);
DECLARE #Schema NVARCHAR(50);
SET #Schema = 'dbo'; --This part will come from C#
SET #Table = 'Tablename'; --This part will come from C#
DECLARE #sql NVARCHAR(MAX);
IF EXISTS(SELECT * FROM sys.tables WHERE name = #Table and schema_id = SCHEMA_ID(#Schema))
BEGIN
SET #sql = N'SELECT TOP 1 * FROM '+ QUOTENAME(#Schema) + '.' + QUOTENAME(#Table) +';';
PRINT #sql;
EXEC sp_executesql #sql;
END
ELSE
RAISERROR('Table or Schema doesn''t exist.',16,1);
In the IF EXISTS you need to pass the table name and schema name as parameter to dynamic query.
In the actual query you need to make a concatenation of table name and schema name.
This will prevent sql injection as well and if the table is not there then raise an error.
Is the following c# snippet susceptible to a SQL Injection Attack?
string sql = #"
declare #sql navarchar(200)
set #sql = 'select * from customers where customerId = ' + convert(navarchar, #custId)
exec sp_sqlexec #sql
"
oSQL.Parameters.Add("custId", CustomerId);
DataTable dt = oSQL.ExecuteDataTable(sql);
I understand this is a trivial sql statement but I'm more interested in the approach of using exec sp_sqlexec. The sql statement is more dynamic than the one stated but don't think it's important for my question.
A slightly more secure solution would be to make the dynaimc query parameterized as well:
(Note that you should be using sp_executesql as well:
string sql = #"
declare #sql navarchar(200)
set #sql = 'select * from customers where customerId = #customerId'
exec sp_sqlexecuseSQL #sql, N'#customerId INT`, #customerId = #custId
"
oSQL.Parameters.Add("custId", CustomerId);
DataTable dt = oSQL.ExecuteDataTable(sql);
Updating answer based on comments from SO. Dynamic SQL (or really any SQL statement, this is a good rule to follow) in general usually is open to SQL Injection if their is a potential for user input. If all your parameters that make the SQL statment say come from another database or say a dropdown, etc then No it is not succeptible to SQL Injection.
The general rule to remember: Don't ever allow unvalidated data to get into a SQL statement. Everything should be validated and add them to the database as a parameter.
I've encountered quite a wierd problem:
I form SQL server command dynamically and one of its part is a group of LIKE tests, that are used as textual search on several columns and tables. It looks like:
SET #text = '%' + REPLACE(REPLACE(REPLACE(#text, '!', '!!'), '%', '!%'), '_', '!_') + '%'
INSERT INTO
#textFiltered
FROM
#documents d
LEFT JOIN docData t1 WITH (NOLOCK)
ON t1.ID = d.ID AND
(t1.Topic like #text escape '!'
OR t1.Subject like #text escape '!')
LEFT JOIN docData2 t2 WITH (NOLOCK)
ON t2.ID = d.ID AND
t2.Name like #text escape '!'
WHERE
t1.ID IS NOT NULL
OR t2.ID IS NOT NULL
(Surely, that's not the best way to do textual search, still that's not the point)
Now, when I create SQLCommand in C#, like this:
using (var cmd = new SqlCommand())
{
cmd.CommandText = cmdText;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
cmd.Parameters.Add("text", NVarChar, 4000).Value = searchText;
var reader = cmd.ExecuteReader();
....
}
performce of executing is very poor (say, 8 seconds), while executing same query in SQL Management Studio much faster (say, 500 ms).
However, if instead of passing text as parameter, I embed it into text of SQL with:
DECLARE #text nvarchar(max)
SET #text = '<embedded-text>'
then SqlCommand also runs fast.
What's even more strange this behavior seems to be correlated with set of columns that are used in LIKE clauses (didn't figure how). Types of those columns can be nvarchar(size), nvarchar(max), ntext.
I suspect that problem is with parameter - maybe it's type is incorrect or smth else.
P.S. Tried to create parameter with size = length of searched text + 1 - didn't help.
My best guess will be that it is related to the query plan that SQL Server is choosing for you.
there is big difference between using constant varchar which the SQL server can evaluate against existing statistics, and using arbitrary variable which the server know nothing about.
you can try OPTION (RECOMPILE) hint. although this hint will cause the Stored procedure to be compiled on every call, it'll also allow the SQL Server to search for the best plan for the given values, and maybe in your case it will be good trade off.
you could also add the query plan for both option of the stored procedure, and maybe someone will be able to see the difference, and pin point the exact problem.
I have a WPF Application in which I am getting
string someone = TextBox.text;
I would like to use this in the following query
query = " Select * From Table Where Title = someone "
How should I go about using the variable someone in the query?
You can just do this
query = "Select * From Table Where Title = " + someone;
But that is bad and opens you to SQL Injection
You should just use a parameterized query
Something like this should get you started
using (var cn = new SqlClient.SqlConnection(yourConnectionString))
using (var cmd = new SqlClient.SqlCommand())
{
cn.Open();
cmd.Connection = cn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "Select * From Table Where Title = #Title";
cmd.Parameters.Add("#Title", someone);
}
From Jon Skeet's answer since his was more complete than mine
See the docs for SqlCommand.Parameters for more information.
Basically you shouldn't embed your values within the SQL itself for various reasons:
It's inelegant to mix code and data
It opens you up to SQL injection
attacks unless you're very careful about escaping
You have to worry about formatting and i18n details for things like numbers, dates and
times etc
When the query remains the same with only the values
changing, the optimizer has less work to do - it can look up the
previous optimized query directly as it'll be a perfect match in
terms of the SQL.
You should use a parameterized SQL query:
query = "SELECT * From TableName WHERE Title = #Title";
command.Parameters.Add("#Title", SqlDbType.VarChar).Value = someone;
See the docs for SqlCommand.Parameters for more information.
Basically you shouldn't embed your values within the SQL itself for various reasons:
It's inelegant to mix code and data
It opens you up to SQL injection attacks unless you're very careful about escaping
You have to worry about formatting and i18n details for things like numbers, dates and times etc
When the query remains the same with only the values changing, the optimizer has less work to do - it can look up the previous optimized query directly as it'll be a perfect match in terms of the SQL.
Easiest is to use a C# Prepared sql. Example on this post. You don't have to worry about escaping the characters in your sql string or anything
declare #SqlQuery varchar(2000), #Fromdate varchar(20), #Todate varchar(20)
set #Fromdate='01 jan 2017'
set #Todate='30 mar 2017'
set #SqlQuery='select * from tblEmployee where tblEmployee.JDate between '''+ #Fromdate + ''' and '''+ #Todate+ ''''
print #SqlQuery
Is it somehow possible to intercept the query that a given SqlCommand is going to execute on the database?
I'd like to track for debugging purposes all queries that my Data class invokes, and can't find a clever way to do this.
I tried to use some weird "replace" of the sql command string, or appending the parameters with a funky
sb.AppendLine("#" + p.ParameterName + " = " + p.ToDebugString());
(with "ToDebugString()" being an extension method that does a "ToString()" with or without single quotes depending if it's a string or not)
But that seems kinda unprofessional, and it fails horribly when it encounters an
SqlDbType.Structured
parameter.
More or less, I'd like to intercept the DB call inside the application in the same way the SqlServer Profiler does inside the DB itself.
Thank you in advance.
BIG EDIT:
I know that given a simple query like this:
SELECT * FROM MyTable WHERE ID=#ID
Rather than running it like this:
SELECT * FROM MyTable WHERE ID=1234
The database actually runs the procedure like this:
DECLARE #ID int
SET #ID = 1234
SELECT * FROM MyTable WHERE ID=#ID
Can I intercept at application level this last block?
It sounds like you're wanting to see the parameters substituted directly in the query string "as it's done on the server". This is not possible, because the server never substitutes the parameters into the string. That's the beauty of parameterized queries: data is data, code is code, and never that twain shall meet.
Given a simple query like this:
SELECT * FROM MyTable WHERE ID=#ID
Rather than running it like this:
SELECT * FROM MyTable WHERE ID=1234
You can think of it as if the database actually runs a procedure like this:
DECLARE #ID int
SET #ID = 1234
SELECT * FROM MyTable WHERE ID=#ID