SQL Injection attack with stored procedures [duplicate] - sql

This question already has answers here:
How to cleanse (prevent SQL injection) dynamic SQL in SQL Server?
(8 answers)
Closed 3 years ago.
I have a stored procedure as below:
CREATE PROCEDURE sp_GetName(#Name NVARCHAR(50))
AS
BEGIN
DECLARE #sqlcmd NVARCHAR(MAX);
DECLARE #params NVARCHAR(MAX);
SET #sqlcmd = N'SELECT * FROM [dbo].[Employee] WHERE Name like ''%' + #Name + '%''';
PRINT #sqlcmd;
SET #params = N'#Name NVARCHAR(50)';
EXECUTE sp_executesql #sqlcmd, #params, #Name;
END
EXEC sp_GetName '';WAITFOR DELAY '00:00:10'
Whenever I execute above statement, it always delays the response.
How can I write my procedure so that it will handle this SQL Injection attack.

Don't use dynamic SQL if you want to avoid SQL injection attacks. It doesn't matter whether you wrap the string concatenation code in a stored procedure or not.
In this case there's no reason to use dynamic SQL. You can simply write :
SELECT * FROM [dbo].[Employee] WHERE Name like '%' + #Name + '%'
to search for employees with a specific string in their name.
This query is immune to SQL injection whether you execute it as a parameterized query from a client or as a stored procedure. Clients would call both of them the same way.
The stored procedure should be just :
CREATE PROCEDURE getName(#Name nvarchar(50)
AS
SELECT * FROM [dbo].[Employee] WHERE Name like '%' + #Name + '%'
I removed the sp_ prefix because it's used for system stored procedures.
The problem with this code is that %something% will have to search the entire table. It can't use any index on Name. Only prefix searches can use indexes, ie #Name + '%' :
SELECT * FROM [dbo].[Employee] WHERE Name like #Name + '%'

You just need to parametrize the dynamic code.
CREATE PROCEDURE sp_GetName(#Name NVARCHAR(50))
AS
BEGIN
DECLARE #sqlcmd NVARCHAR(MAX);
DECLARE #params NVARCHAR(MAX);
SET #sqlcmd = N'SELECT * FROM [dbo].[Employee] WHERE Name like ''%'' + #Name + ''%''';
print #sqlcmd;
SET #params = N'#Name NVARCHAR(50)';
EXECUTE sp_executesql #sqlcmd, #params, #Name;
End
exec sp_GetName '';
However, the code has nothing that would justify the need of making it dynamic. It could be simply rewritten as this.
CREATE PROCEDURE sp_GetName(#Name NVARCHAR(50))
AS
SELECT * FROM [dbo].[Employee] WHERE Name like '%' + #Name + '%';
Why are you making your application slow by design?

You can just embed the SQL in you're stored procedure without using sp_executesql
CREATE PROCEDURE sp_GetName(#Name NVARCHAR(50))
AS
BEGIN
SELECT * FROM [dbo].[Employee] WHERE Name like '%' + #Name + '%'
End
Your example doesn't actually demonstrate a SQL injection attack; you've just got 2 sql statements seperated by a semi-colon, the second of which is a "wait"
-- Also demonstrates wait behaviour
exec sp_who;WAITFOR DELAY '00:00:10'

Related

Single Quote Handling in Dynamic SQL Stored Procedure

I'm using a query like this:
ALTER procedure [dbo].[procedure]
#Search nvarchar(100) = ''
declare #LargeComplexQuery nvarchar(max) =
'select * from People where Name like ''%' + #Search + '% order by Name'
exec sys.sp_executesql #LargeComplexQuery
#Search is populated by a program.
If the program returns a string containing a single quote the stored procedure errors, how can I handle this?
If possible, I'd like this to be handled by the stored procedure, rather than the program passing in the string.
Either escape the quote in the application before passing the parameter, or do it in the proc:
declare #Search nvarchar(100) = ''
declare #Search2 nvarchar(100)
declare #LargeComplexQuery nvarchar(max)
set #Search2 = replace(#Search, '''', '''''')
set #LargeComplexQuery = 'select * from People where Name like ''%' + #Search2 + '%''' -- I escaped the quote here too
exec sys.sp_executesql #LargeComplexQuery
You should escape the quotes after recovering the value.
And also COALESCE the parameter in case a NULL is passed to avoid the following error
EXEC sp_executesql NULL
Which would give
Procedure expects parameter '#statement' of type
'ntext/nchar/nvarchar'.
Which gives the following statements
DECLARE #Search nvarchar(100) = '';
SET #Search = REPLACE(COALESCE(#Search, ''), '''', '''''');
SET #LargeComplexQuery = 'SELECT * FROM People WHERE Name LIKE ''%' + #Search + '%'''
EXEC sys.sp_executesql #LargeComplexQuery
Here as a version that uses sp_executesql parameters and so is not vulnerable to SQL injection - it should also provide better performance, to quote MSDN:
Because the Transact-SQL statement itself remains constant and only
the parameter values change, the SQL Server query optimizer is likely
to reuse the execution plan it generates for the first execution.
CREATE PROCEDURE [dbo].[Yourproc]
(
#Search NVARCHAR(100) = N''
)
AS
DECLARE #LargeComplexQuery NVARCHAR(MAX) = 'SELECT * from People WHERE Name LIKE ''%'' + COALESCE(#Search, '''') + ''%'' ORDER BY Name'
EXEC sys.sp_executesql #LargeComplexQuery, N'#Search NVARCHAR(100)', #Search = #Search
I've made some assumptions, such as if you pass empty string or NULL as a search condition then you get all people returned.
Testing it out - dummy schema & data:
CREATE TABLE People(Name NVARCHAR(MAX))
INSERT INTO People(Name)
VALUES ('Mr Smith'), ('Mrs Jones'), ('Miss O'' Jones')
Testing stored proc execution:
DECLARE #search NVARCHAR(100) = N'Jones';
EXEC YourProc #Search; --Should get back both Mrs Jones and Miss O'Jones
SET #search = N'O'' Jones';
EXEC YourProc #Search; --Should get back just Miss O'Jones
SET #search = N'';
EXEC YourProc #Search; --Should get everyone, same as if you passed no search value at all
SET #search = NULL
EXEC YourProc #Search; --Should get everyone
MSDN Documentation on sp_executesql

Is it possible to set a part of a select statement in a variable

I have a query of which the select-part is really long. I'd like to split this in several pieces, especially because some parts are in there twice or even more often.
What I'd like is something like the following:
Declare #SQLPart as varchar(1000)
Set #SQLPart = 'Field1,
case ... as Field2,'
Select ..., #SQLPart, ... From .....
Unfortunately this results error messages. I tried something like EXEC(#SQLPart) as well but of course this also didn't work. How would I solve this?
Yes, dynamic sql and sp_executesql:
CREATE TABLE ##Temp (Field1 int, Field2 int)
Declare #SQLPart nvarchar(1000)
Set #SQLPart = N'Field1, Field2 '
DECLARE #SQL nvarchar(1000) = N'SELECT ' + #SQLPart + 'FROM ##Temp'
PRINT #SQL
EXEC sp_executesql #SQL
DROP TABLE ##Temp
Your SQL code must be nvarchar type.
Alse sp_executesql is better than EXECUTE function, when you have many similar queries, sp_executesql caches executaion plans, and it can be better in perfomance.
You can use dynamic sql here,and use a EXECUTE keyword to execute this dynamic query
Declare #SQLPart as varchar(1000)
Set #SQLPart = 'Field1,
case ... as Field2,'
EXECUTE ('SELECT ....,'+#SQLPart+',... FROM ...')
SQL Server does not support Macro-Substitution, so you would have to use Dynamic SQL.
Declare #SQL varchar(max) ='Select ... ' + #SQLPart + '... from ...'
Exec(#SQL)

Dynamic SQL in SQL Server

I have a SQL CLR stored procedure dbo.Parallel_AddSql that takes a SQL query to execute as a string parameter.
It looks something like this :
exec dbo.Parallel_AddSql 'sql1',
'insert into test.dbo.table_1 values (1, ''test1'')'
I need to pass my SQL query as dynamic SQL statement to Parallel_AddSql procedure.
SQL statement that I need to pass is procedure execution statement with parameters i.e
Exec dbo.MatchLastName #LastNameFromUser = #LastName,
#checkbool = #checkbool
How do I pass this? As #lastName and #checkbool will be out of scope if I pass them as such in the string.
I tried using this :
set #SQL = 'Exec dbo.MatchFirstName #FirstNameFromUser =' + #firstname + ',
#checkbool = ' + cast(#checkbool as nvarchar(10))
exec dbo.Parallel_AddSql 'sql1', #SQL
However, I get this error :
Could not find stored procedure 'dbo.MatchFirstName'
dbo.MatchFirstName is there but dbo.Parallel_AddSql is not able to see it at all.
dbo.Parallel_AddSql is coming the library code given here
Edit : Unclosed quotation mark error :
set #SQL = 'Exec dbo.MatchFirstName #FirstNameFromUser =''' + #firstname
+ ''', #checkbool = ''' + cast(#checkbool as nvarchar(10))
set #SQL = 'Exec dbo.MatchFirstName #FirstNameFromUser =''' + #firstname + ''', ...'
If you were to call the stored procure in a script, you'd write Exec dbo.MatchFirstName #FirstNameFromUser = 'John', not Exec dbo.MatchFirstName #FirstNameFromUser = John, right? Same thing with your dynamic SQL. You have to add in the quotes.
If you want to keep the #FirstName = #FirstName syntax, you'll have to declare and set your variables in the string itself. So,
'DECLARE #FirstName Varchar(Max); SET #FirstName = ''John''; EXEC ...'
EDIT
If you look at the example they gave for Parallel_AddSql, they used the fully qualified name of the procedure.
The example from www.codeproject.com:
exec ClrLibDb.dbo.Parallel_AddSql 'sql1', 'insert into test.dbo.table_1 values (1, ''test1'')'
I think it requires the database to be part of that. So, YourDbName.dbo.MatchFirstName. It may be looking at master for your stored procedure, which is why it's not finding it.

Pass a variable into a stored procedures SELECT command [duplicate]

This question already has answers here:
SQL: Select dynamic column name based on variable
(3 answers)
Closed 8 years ago.
I have a web page with a link to a stored procedure and I want to pass a variable to the stored procedures select statement. The code so far is -
ALTER procedure [dbo].[RTO]
#Weeknumber int,
#asset nvarchar(50)
AS
Begin
SELECT #asset
FROM RTO_weeklyanalysis
Where weekNumber = #weeknumber
END
basically the #asset will be the name of the column but this will change depending on what the user selects on the page.
You will need to use Dynamic sql for this. Also use QuoteName() function when concatenating object names to your sql query. and use system stored procedure sp_executesql to execute the dynamic query the most safe and secure way of executing dynamic sql. Something as follows :
ALTER procedure [dbo].[RTO]
#Weeknumber int,
#asset sysname
AS
Begin
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' SELECT '+ QUOTENAME(#asset) + '
FROM RTO_weeklyanalysis
Where weekNumber = #weeknumber'
EXECUTE sp_executesql #Sql
,N'#Weeknumber int'
,#Weeknumber
END
You cannot use a column name dynamicly in this way. The simplest way to achiewe what you want will be to exec whole query from temporary variable, i mean:
declare #query
set #query = 'SELECT ' + #asset ' FROM RTO_weeklyanalysis Where weekNumber = #weeknumber'
exec sp_executesql #query

stored procedure input parameter

i am creating a stored procedure in sql server 2008 such as this
-- the code
create procedure proce
#database varchar(50)
as
begin
select * from [#database].[dbo].[sometable]
end
the procedure is compiled
but when i execute the procedure using
-- here i execute it
execute proce 'somedatabase'
it throws an error
-- me gets error :(
Invalid object name '#database.dbo.sometable'
where am i going wrong???????
You cannot directly parameterized the tableName. The only way you can do that is to make a dynamic SQL Statement.
eg,
CREATE PROCEDURE proce #database VARCHAR(50)
AS
BEGIN
DECLARE #SQLQuery AS NVARCHAR(500)
SET #SQLQuery = 'SELECT * FROM [' + #database + '].[dbo].[sometable]'
EXECUTE(#SQLQuery)
END
GO
Building Dynamic SQL In a Stored Procedure
You can go with this:
DECLARE #string AS NVARCHAR(500)
SET #string = 'select * from [' + #database + '].[dbo].[sometable]'
EXEC (#string)
More more info refer: Dynamic SQL
I don't believe variables are allowed in that context, use the following which I've just confirmed works with your procedure:
exec('select * from [' + #database + '].[dbo].[sometable]')