Dynamic SQL in SQL Server - sql

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.

Related

SQL Injection attack with stored procedures [duplicate]

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'

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

Pass the Multiple quotes around #variable wherever single quote is there

Below is my procedure. It is working fine.
Create PROCEDURE [dbo].[spCompanyName]
(
#CompanyName VARCHAR(100))
AS
Begin
DECLARE #sql VARCHAR(MAX)
SET #sql = ' Select EmpID,CompanyName FROM Employee' + CHAR(13) + CHAR(10)
IF len(#CompanyName) > 0
BEGIN
SET #sql = #sql + ' Where (RTRIM(LTRIM(CompanyName)) like ''' + #CompanyName + '%'') ' + char(13) + char(10)
END
PRINT #SQL
EXEC(#sql)
End
exec spCompanyName #CompanyName='So Unique, formerly Sofia''''s'
I need Wherever single quote is there in companyname,if I pass 8 quotes in single quotes I need output.above procedure where do I need to change.
Eg:
exec spCompanyName #CompanyName='So Unique, formerly Sofia''''''''s'
exec spCompanyName #CompanyName='Absolute''''''''s,Strategy''''''''s'
Don't think "I'll throw away all of the useful features of using parameters to separate data from code and start manually trying to protect strings" - keep using parameters.
Rather than
EXEC(#sql)
Have:
EXEC sp_executesql #sql,N'#CompanyName varchar(100)',#CompanyName = #CompanyName
And change:
SET #sql = #sql + ' Where (RTRIM(LTRIM(CompanyName)) like ''' + #CompanyName + '%'') ' + char(13) + char(10)
to:
SET #sql = #sql + ' Where (RTRIM(LTRIM(CompanyName)) like #CompanyName + ''%'') ' + char(13) + char(10)
Or, in the alternative, consider just writing a normal query, no dynamic SQL:
Create PROCEDURE [dbo].[spCompanyName]
(
#CompanyName VARCHAR(100))
WITH RECOMPILE
AS
Begin
Select EmpID,CompanyName FROM Employee
Where RTRIM(LTRIM(CompanyName)) like #CompanyName + '%' or
#CompanyName is null
End
Assuming you're using SQL Server 2008 (with particular patch levels) or later, as specified in Erland Sommarskog's Dynamic Search Conditions in T-SQL
If you are going to create a new stored procedure for this feature then you can have this in much better way then dynamic.
You should execute different select queries based on if CompanyName is passed or not.
You can simply the stored procedure as following.
Create PROCEDURE [dbo].[spCompanyName]
(
#CompanyName VARCHAR(100))
AS
Begin
IF LEN(#CompanyName) > 0
BEGIN
Select EmpID,CompanyName FROM Employee WHERE RTRIM(LTRIM(CompanyName)) like '' + #CompanyName + '%'
END
ELSE
BEGIN
Select EmpID,CompanyName FROM Employee
END
END
I am not sure why you need to pass so many single quotes in the parameter value but to me it looks like you can execute the procedure with following simple way.
EXEC spCompanyName #CompanyName='So Unique, formerly Sofia''s'
EXEC spCompanyName #CompanyName='Absolute''s,Strategy''s'
This should help you finding your solution.

No output when stored procedure is executed

No output when stored procedure is executed. The select statement works when I create a new query , but does not return any values when calling the store procedure within my Web app.
You want a dynamic sql.
try this.
Declare #strSQL nvarchar(2000)
set #strSQL = 'SELECT * FROM investor WHERE ' + #search_by + ' = ''' + #col + ''''
execute sp_executesql #strSQL

Error when declaring parameter in stored procedure

I am trying to create a stored procedure that utilizes a variable number of parameters. As I am pretty new to writing stored procedures and TSQL in general, I decided to try and write it with only one parameter. However, I keep getting an error stating "Must declare scalar variable #FirstName" when I try to execute it. Right now, I am trying to store the SQL statement in another variable, #sql. My procedure looks like this:
ALTER PROCEDURE [dbo].[GetEmployeeByParameters]
(#FirstName varchar(50))
AS
BEGIN
DECLARE #sql AS NVARCHAR(4000)
SET #sql = 'SELECT e.* from Employee e
WHERE e.FirstName = #FirstName'
EXEC (#sql)
END
I've looked elsewhere and tried EXEC sp_execute #sql which didn't work. Strangely, what does work is when I don't declare the #sql variable and instead just write my query normally. Since that is the case, I'm assuming there is some error in my usage of the SET and EXEC functions. I'm also not 100% sure that I'm using BEGIN and END properly. The way I understood it is that BEGIN and END separate SQL statements into logical blocks, and thus are more commonly used when IF comes into play. Could anyone tell me what exactly is going on with my parameter here? It's really confusing me as to why SQL Server thinks it's not declared.
The variable parameter needs to be outside the quotes.
SET #sql = N'SELECT e.* from Employee e
WHERE e.FirstName = ''' + #FirstName + ''''
Or, better yet, run it without any dynamic SQL.
SELECT e.*
from Employee e
WHERE e.FirstName = #FirstName
Because
'Select ... #FirstName'
is not the same as
Select ... #FirstName
One is a string and the other is a SQL Query
What you should do instead is
ALTER PROCEDURE [dbo].[GetEmployeeByParameters]
(#FirstName varchar(50))
AS
BEGIN
DECLARE #sql AS NVARCHAR(4000)
SET #sql = 'SELECT e.* from Employee e
WHERE e.FirstName = ''' + #FirstName + ''''
EXEC (#sql)
END
When you execute dynamic sql, you are switching contexts and variables don't move between contexts. Once you declare the SQL statement as a string, everythign must be provided to that string in order for it to recognize it.
Obviously, you don't need dynamic SQL in this case, but once method of doing it is like so:
ALTER PROCEDURE [dbo].[GetEmployeeByParameters]
(#FirstName varchar(50))
AS
BEGIN
DECLARE #sql AS NVARCHAR(4000)
SET #sql = 'SELECT e.* from Employee e
WHERE e.FirstName = #FirstName'
EXEC sp_executeSQL #sql, N'#Firstname varchar(50)', #FirstName
END
sp_executeSQL allows you to declare internal parameters (the second clause), and supply them with values (the last clause).
You need to change your query to the following as the #Firstname variable is not in scope:
ALTER PROCEDURE [dbo].[GetEmployeeByParameters]
(#FirstName varchar(50))
AS
BEGIN
DECLARE #sql AS NVARCHAR(4000)
SET #sql = 'SELECT e.* from Employee e
WHERE e.FirstName = ''' + #FirstName + ''''
EXEC (#sql)
END
As this is a "search" type query your doing with a variable number of params, you need to build the string up bit by bit -- you're on the right lines that it needs to be dynamic, but you also need to avoid SQL injection attacks (google "Little Bobby Tables"). Thus you need to use a parameterised dynamic SQL statement:
ALTER PROCEDURE [dbo].[GetEmployeeByParameters]
#FirstName VARCHAR(50)
AS
BEGIN
DECLARE #sql AS NVARCHAR(4000)
SET #sql = 'SELECT e.* FROM Employee e WHERE 1 = 1'
IF #FirstName IS NOT NULL
BEGIN
SET #sql = #sql + ' AND FirstName = #pFirstName'
END
-- More IF statements, building up the query
EXEC sp_ExecuteSQL #sql, N'#pFirstName VARCHAR(50)', #FirstName
The second and third params map the #FirstName parameter to the query's "internal" parameters (which I normally prefix with a 'p' or 'param' just to differentiate them from the stored procedure's own parameters).
You extend the sp_Exceute as appropriate each time you add a new parameter to search by, so you might end up doing:
EXEC sp_ExecuteSQL #sql,' N'
#pFirstName VARCHAR(50),
#pSurName VARCHAR(50),
#pDateOfBirth DATETIME', #FirstName, #Surname, #DateOfBirth
Use this body, without "dynamic" query
ALTER PROCEDURE [dbo].[GetEmployeeByParameters]
(#FirstName varchar(50))
AS
BEGIN
SELECT e.* from Employee e
WHERE e.FirstName = #FirstName
END
OR
using dynamic query do not include variables itselves into the script - do concatenate them with the script and do not forget to properly quote them.