I've been preaching both to my colleagues and here on SO about the goodness of using parameters in SQL queries, especially in .NET applications. I've even gone so far as to promise them as giving immunity against SQL injection attacks.
But I'm starting to wonder if this really is true. Are there any known SQL injection attacks that will be successfull against a parameterized query? Can you for example send a string that causes a buffer overflow on the server?
There are of course other considerations to make to ensure that a web application is safe (like sanitizing user input and all that stuff) but now I am thinking of SQL injections. I'm especially interested in attacks against MsSQL 2005 and 2008 since they are my primary databases, but all databases are interesting.
Edit: To clarify what I mean by parameters and parameterized queries. By using parameters I mean using "variables" instead of building the sql query in a string.
So instead of doing this:
SELECT * FROM Table WHERE Name = 'a name'
We do this:
SELECT * FROM Table WHERE Name = #Name
and then set the value of the #Name parameter on the query / command object.
Placeholders are enough to prevent injections. You might still be open to buffer overflows, but that is a completely different flavor of attack from an SQL injection (the attack vector would not be SQL syntax but binary). Since the parameters passed will all be escaped properly, there isn't any way for an attacker to pass data that will be treated like "live" SQL.
You can't use functions inside placeholders, and you can't use placeholders as column or table names, because they are escaped and quoted as string literals.
However, if you use parameters as part of a string concatenation inside your dynamic query, you are still vulnerable to injection, because your strings will not be escaped but will be literal. Using other types for parameters (such as integer) is safe.
That said, if you're using use input to set the value of something like security_level, then someone could just make themselves administrators in your system and have a free-for-all. But that's just basic input validation, and has nothing to do with SQL injection.
No, there is still risk of SQL injection any time you interpolate unvalidated data into an SQL query.
Query parameters help to avoid this risk by separating literal values from the SQL syntax.
'SELECT * FROM mytable WHERE colname = ?'
That's fine, but there are other purposes of interpolating data into a dynamic SQL query that cannot use query parameters, because it's not an SQL value but instead a table name, column name, expression, or some other syntax.
'SELECT * FROM ' + #tablename + ' WHERE colname IN (' + #comma_list + ')'
' ORDER BY ' + #colname'
It doesn't matter whether you're using stored procedures or executing dynamic SQL queries directly from application code. The risk is still there.
The remedy in these cases is to employ FIEO as needed:
Filter Input: validate that the data look like legitimate integers, table names, column names, etc. before you interpolate them.
Escape Output: in this case "output" means putting data into a SQL query. We use functions to transform variables used as string literals in an SQL expression, so that quote marks and other special characters inside the string are escaped. We should also use functions to transform variables that would be used as table names, column names, etc. As for other syntax, like writing whole SQL expressions dynamically, that's a more complex problem.
There seems to be some confusion in this thread about the definition of a "parameterised query".
SQL such as a stored proc that accepts parameters.
SQL that is called using the DBMS Parameters collection.
Given the former definition, many of the links show working attacks.
But the "normal" definition is the latter one. Given that definition, I don't know of any SQL injection attack that will work. That doesn't mean that there isn't one, but I have yet to see it.
From the comments, I'm not expressing myself clearly enough, so here's an example that will hopefully be clearer:
This approach is open to SQL injection
exec dbo.MyStoredProc 'DodgyText'
This approach isn't open to SQL injection
using (SqlCommand cmd = new SqlCommand("dbo.MyStoredProc", testConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter newParam = new SqlParameter(paramName, SqlDbType.Varchar);
newParam.Value = "DodgyText";
.....
cmd.Parameters.Add(newParam);
.....
cmd.ExecuteNonQuery();
}
any sql parameter of string type (varchar, nvarchar, etc) that is used to construct a dynamic query is still vulnerable
otherwise the parameter type conversion (e.g. to int, decimal, date, etc.) should eliminate any attempt to inject sql via the parameter
EDIT: an example, where parameter #p1 is intended to be a table name
create procedure dbo.uspBeAfraidBeVeryAfraid ( #p1 varchar(64) )
AS
SET NOCOUNT ON
declare #sql varchar(512)
set #sql = 'select * from ' + #p1
exec(#sql)
GO
If #p1 is selected from a drop-down list it is a potential sql-injection attack vector;
If #p1 is formulated programmatically w/out the ability of the user to intervene then it is not a potential sql-injection attack vector
A buffer overflow is not SQL injection.
Parametrized queries guarantee you are safe against SQL injection. They don't guarantee there aren't possible exploits in the form of bugs in your SQL server, but nothing will guarantee that.
Your data is not safe if you use dynamic sql in any way shape or form because the permissions must be at the table level. Yes you have limited the type and amount of injection attack from that particular query, but not limited the access a user can get if he or she finds a way into the system and you are completely vunerable to internal users accessing what they shouldn't in order to commit fraud or steal personal information to sell. Dynamic SQL of any type is a dangerous practice. If you use non-dynamic stored procs, you can set permissions at the procesdure level and no user can do anything except what is defined by the procs (except system admins of course).
It is possible for a stored proc to be vulnerable to special types of SQL injection via overflow/truncation, see: Injection Enabled by Data Truncation here:
http://msdn.microsoft.com/en-us/library/ms161953.aspx
Just remember that with parameters you can easily store the string, or say username if you don't have any policies, "); drop table users; --"
This in itself won't cause any harm, but you better know where and how that date is used further on in your application (e.g. stored in a cookie, retrieved later on to do other stuff.
You can run dynamic sql as example
DECLARE #SQL NVARCHAR(4000);
DECLARE #ParameterDefinition NVARCHAR(4000);
SELECT #ParameterDefinition = '#date varchar(10)'
SET #SQL='Select CAST(#date AS DATETIME) Date'
EXEC sp_executeSQL #SQL,#ParameterDefinition,#date='04/15/2011'
I have a very basic test stored procedure, shown below. This proc is reading data in a different database, in a different server. To do this I am using a linked server. From what I have read, I need to change the FROM clause to this:
[linked server name].[database name].[schema name].[table name]
However, I would like to pass in the linked server name and database name as parameters and use them in my FROM clause. I am not concerned with injection attacks, etc. I will be passing this in from a config file.
create PROC [dbo].[SelectTEST]
#GU UNIQUEIDENTIFIER,
#LINKED_SERVER_NAME nvarchar(max),
#DATABASE_NAME nvarchar(max)
AS
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRAN
SELECT [GU]
FROM '[' + #LINKED_SERVER_NAME +'].['+ #DATABASE_NAME + '].[Test Table] '
WHERE ([GU] = #GU OR #GU IS NULL)
COMMIT
This is a big mess of syntax errors. Is it possible to pass in these parameters and use in my stored procedure? I would have to make this change to a bunch of different procs, so sorta trying to find the a succinct solution...
Can i do something like below, let me know
IF #parameter=1 BEGIN ...query... END IF #parameter=2
Need the correct syntax if it is possible.
It's OLE DB connection.
Not a Stored Proc. just a sql query
DECLARE #param AS INT = ?;
IF #param = 1
BEGIN
SELECT 1 AS Y;
END
ELSE IF #param = 2
BEGIN
SELECT 2 AS Y;
END
There are two question marks in your query and probably you were passing only one variable. I have seen code where developers pass the same value twice (or multiple) times. This is inefficient. A better way is to receive the passed parameters in SSIS variables. Advantages:
1. You need to pass one value only once.
2. More importantly, if you change the order in which the passed parameters are used in the sql, you do not need to change their order on the user-interface of Execute SQL Task Editor//Parameters. This is what Andy Leonard has suggested later in his response.
You can. Assuming you are referring to an Execute SQL Task, the parameters in an Execute SQL Task using an OLE DB connection utilize question marks (?) as parameter placeholders. You map the placeholders to SSIS variables on the Parameter Mapping page of the Execute SQL Task. In the SQLStatement property you would enter:
If (?=1)
begin
... {some T-SQL here} ...
end
If (?=2)
begin
... {some T-SQL here} ...
end
That's one way to accomplish what I think you are asking.
Another way is to create an Execute SQL Task to read the value of #parameter from the database into an SSIS variable. Then you can build two Execute SQL Tasks - one with each option for T-SQL as the SQLStatement property - and use expressions on precedent constraints to determine which Execute SQL Task to execute.
Hope this helps,
:{>
You cannot use Execute SQL Task to run Transact-SQL statements.
For setting conditional SQL Statement based on what you are trying to achieve.
In Execute SQL Task editor
In general tab, leave the SQLStatement blank.
In parameter mapping tab, add parameter and map User::Parameter variable to Parameter Name 0.
In Expression tab, set the SQLStatementSource to
(DT_NUMERIC, 18, 0) #[User::Parameter]==1 ? ...query 1... : ...query 2...
I have written a T-SQL script that migrates some data from one database to another.
At the moment I am doing that by use of dynamic sql.
For example see the following code:
Declare #sqlquery nvarchar(4000)
SET #sqlquery = N'SELECT * from ' + #LinkServerName + #SourceDatabaseName + '.dbo.Table'
EXEC #sqlquery
In this example #LinkServerName is a nvarchar variable that stores the name of the linked server for the SQL Server that contains the source database. #SourceDatabaseName is a nvarchar variable that stores the name of the source database.
I donĀ“t like that way. I would prefer the following code:
SELECT * from #SourceDatabase.dbo.Table
Is that possible?
Thank you in advance.
Second approach is incorrect, first one is the correct one. For more information check this other question here at stackoverflow how-to-use-variable-for-database-name-in-t-sql
Here's my configuration:
I have a re-runnable batch script that I use to update my database.
Inside of that batch script, I have code that says the following:
If Table 'A' doesn't exist, then create Table 'A' and insert rows into it.
Later on in that batch script, I create an schemabound indexed view on that table.
And if you didn't already know, indexed views require specific client settings.
Sometimes, when I re-run the script, that is after the table has been created, SQL Server Management Studio evaluates the "insert rows" code, which is protected by the 'If this table doesn't exist' code, and yields the following error:
Msg 1934, Level 16, State 1, Line 15
INSERT failed because the following SET options have incorrect settings: 'CONCAT_NULL_YIELDS_NULL, ANSI_WARNINGS, ANSI_PADDING, ARITHABORT'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
Please note: If someone were to try this INSERT statement in a vacuum, I would fully expect SSMS to generate this error.
But not when it's protected by a conditional block.
My Question:
Does the SSMS compiler evaluate all expressions, regardless of whether they will actually be executed?
Yes, it evaluates all of them,take a look at this
declare #i int
select #i =1
if #i = 1
begin
declare #i2 int
set #i2 = 5
end
else
begin
declare #i2 int
set #i2 = 5
end
Msg 134, Level 15, State 1, Line 12
The variable name '#i2' has already been declared. Variable names must be unique within a query batch or stored procedure.
Another example with temp tables is here: What is deferred name resolution and why do you need to care?
your only way out would be to wrap it inside dynamic SQL
Note that most of the settings you mention are connection-level, i.e. in case you set/change them they stay in effect unless you close the connection or explicitly change their value.
Returning to your question. The error you mention looks like runtime error, i.e. the INSERT is actually being executed. It would be better if you could show your script (omitting details, but keeping batches).
Edit: it is not SSMS compiler that evaluates SQL you try to execute - it is SQL Server. What do you meant by 'evaluate'? Is it 'execute'? When you run a batch (which is what actually is being executed by a server), SQL Server first does syntactic analysis and throws error in case it finds any syntactic error, nothing is being executed at this point of time. In case syntax is ok, the server starts executing you batch.
Again, the error you show seems to be runtime - so I guess you'd carefully watch for the conditions and track what happens (or provide us more details about 'sometimes').