Prevent SQL injection in stored procedure - sql-server

I want to prevent SQL injection. It's a guild notice smp from a online game. The attacker used "_)$*%RDELETE FROM Character WHERE sid >=1". He deleted all our characters. (Backup ftw) But its really annoying to restore the backups again and again. And don't forget the little roll back with this. The smp seems fine to me. Yeah I'm a beginner so shame on me :*. Thank you for help.
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
ALTER PROCEDURE [dbo].[smp_set_guild_notice]
#IN_GUILD_SID INT,
#IN_NOTICE NVARCHAR(128)
AS
SET NOCOUNT ON
IF (LEFT(#IN_NOTICE, 6) = '_)$*%R')
BEGIN
SET #IN_NOTICE = REPLACE(#IN_NOTICE,'_)$*%R','')
EXEC sp_executesql #IN_NOTICE;
END
ELSE
BEGIN
UPDATE dbo.Guild
SET notice = #IN_NOTICE
WHERE sid = #IN_GUILD_SID
END

I would think the answer is obvious to anyone who understands SQL injection: don't EXEC untrusted input as an SQL statement!
I have no idea what the purpose of your _)$*%R prefix is. It appears to be a kind of "back-door" to allow a verbatim SQL statement to be executed? Why would you do this? Is it some kind of security-by-obscurity measure? The attacker was probably able to find out the prefix by looking at your web page source in their browser.
If this procedure is meant to update the guild notice, then just do the second UPDATE and remove the back-door code.
ALTER PROCEDURE [dbo].[smp_set_guild_notice]
#IN_GUILD_SID INT,
#IN_NOTICE NVARCHAR(128)
AS
SET NOCOUNT ON
UPDATE dbo.Guild SET notice = #IN_NOTICE WHERE sid = #IN_GUILD_SID
I'm not a Microsoft SQL Server developer, so that syntax might not be correct. I'm just showing what I mean by removing the part of the code that does the EXEC.
No SQL injection is possible when you hard-code your SQL, and use inputs only has value parameters.
Don't use EXEC if you can't sanitize input.

Related

What is "dummy" in CREATE PROCEDURE statement

I'm doing investigation of code repo and find one thing that make me confused. SQL Server stored procedures are contained in a repo as a set of queries with following structure:
IF OBJECT_ID(N'[dbo].[sp_ProcTitle]', N'P') IS NULL
BEGIN
EXEC dbo.sp_executeSQL N'CREATE PROCEDURE [dbo].[sp_ProcTitle] AS dummy:;';
END
ALTER PROCEDURE dbo.sp_ProcTitle
#ParamOne int,
#ParamTwo date,
#ParamThree int
AS
SET NOCOUNT ON
-- some procedure body
END
Never before I saw AS dummy:; and now I'm a little confused, I can't find any good explanation what is it and how it works. Could anybody tell me what does it mean this statement? How it works? What is the reason to have it? Any thought would be good to hear. Or, please, advise me some link where I can find good explanation.
This is simply a label, such that could be used in a GOTO statement.
The word "dummy" is unimportant. It's simply trying to create the stored procedure if it doesn't exist, with a minimal amount of text. The content is then filled in with the ALTER.
Conceivably, the dummy text could later be searched for to see if any procedures were created and didn't have their content filled in, to check against failed deployments, etc.
Why do this? Well, it preserve the creation time of the stored procedure in metadata (which can be useful in administration or tracking down problems), and is compatible with versions of SQL Server that lack the CREATE OR ALTER... support.
This might make a little more sense if we add a little formatting to the CREATE:
CREATE PROCEDURE [dbo].[sp_ProcTitle]
AS
dummy:
This is, effectively, an empty procedure with a label called dummy. The user appears to be using this to ensure that the procedure exists first, and the ALTERing it. In older versions of SQL Server, such methods were needed because it didn't support CREATE OR ALTER syntax. As such, if you tried to ALTER a procedure that didn't exist the statement failed, and likewise if you try to CREATE a procedure that already exists it fails.
If you are on a recent version of SQL Server, I'd suggest changing to CREATE OR ALTER and getting rid of the call to sys.sp_executesql.

How can I make a stored procedure commit immediately?

EDIT This questions is no longer valid as the issue was something else. Please see my explanation below in my answer.
I'm not sure of the etiquette so i'l leave this question in its' current state
I have a stored procedure that writes some data to a table.
I'm using Microsoft Practices Enterprise library for making my stored procedure call.
I invoke the stored procedure using a call to ExecuteNonQuery.
After ExecuteNonQuery returns i invoke a 3rd party library. It calls back to me on a separate thread in about 100 ms.
I then invoke another stored procedure to pull the data I had just written.
In about 99% of cases the data is returned. Once in a while it returns no rows( ie it can't find the data). If I put a conditional break point to detect this condition in the debugger and manually rerun the stored procedure it always returns my data.
This makes me believe the writing stored procedure is working just not committing when its called.
I'm fairly novice when it comes to sql, so its entirely possible that I'm doing something wrong. I would have thought that the writing stored procedure would block until its contents were committed to the db.
Writing Stored Procedure
ALTER PROCEDURE [dbo].[spWrite]
#guid varchar(50),
#data varchar(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- see if this guid has already been added to the table
DECLARE #foundGuid varchar(50);
SELECT #foundGuid = [guid] from [dbo].[Details] where [guid] = #guid;
IF #foundGuid IS NULL
-- first time we've seen this guid
INSERT INTO [dbo].[Details] ( [guid], data ) VALUES (#guid, #data)
ELSE
-- updaeting or verifying order
UPDATE [dbo].[Details] SET data =#data WHERE [guid] = #guid
END
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Reading Stored Procedure
ALTER PROCEDURE [dbo].[spRead]
#guid varchar(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT * from [dbo].[Details] where [guid] = #guid;
END
To actually block other transactions and manually commit,
maybe adding
BEGIN TRANSACTION
--place your
--transactions you wish to do here
--if everything was okay
COMMIT TRANSACTION
--or
--ROLLBACK TRANSACTION if something went wrong
could help you?
I’m not familiar with the data access tools you mention, but from your description I would guess that either the process does not wait for the stored procedure to complete execution before proceeding to the next steps, or ye olde “something else” is messing with the data in between your write and read calls.
One way to tell what’s going on is to use SQL Profiler. Fire it up, monitor all possible query execution events on the database (including stored procedure and stored procedures line start/stop events), watch the Text and Started/Ended columns, correlate this with the times you are seeing while tracing the application, and that should help you figure out what’s going on there. (SQL Profiler can be complex to use, but there are many sources on the web that explain it, and it is well worth learning how to use it.)
I'll leave my answer below as there are comments on it...
Ok, I feel shame I had simplified my question too much. What was actually happening is two things:
1) the inserting procedure is actually running on a separate machine( distributed system).
2) the inserting procedure actually inserts data into two tables without a transaction.
This means the query can run at the same time and find the tables in a state where one has been written to and the second table hasn't' yet had its write committed.
A simple transaction fixes this as the reading query can handle either case of no write or full write but couldn't handle the case of one table written to and the other having a pending commit.
Well it turns out that when I created the stored procedure the MSSQLadmin tool added a line to it by default:
SET NOCOUNT ON;
If I turn that to:
SET NOCOUNT OFF;
then my procedure actually commits to the database properly. Strange that this default would actually end up causing problems.
Easy way using try-catch, like it if useful
BEGIN TRAN
BEGIN try
INSERT INTO meals
(
...
)
Values(...)
COMMIT TRAN
END try
BEGIN catch
ROLLBACK TRAN
SET #resp = (convert(varchar,ERROR_LINE()), ERROR_MESSAGE() )
END catch

SQL SERVER 2008 TRIGGER ON CREATE TABLE

Is there a way to run some function like a trigger when a table is created in the database in SQL SERVER 2008?
Yes, it's called a DDL trigger. The documentation for CREATE TRIGGER has a sample for DROP_SYNONYM (a very questionable choice for an example) but you'll want the CREATE_TABLE event instead. A better starting point in understanding how they work is probably here:
http://msdn.microsoft.com/en-us/library/ms190989.aspx
If you have more specific details, e.g. what exactly do you want to pass to this function (I assume you mean procedure), or what does the procedure do, we can provide more useful and specific help.
Yes a DDL Trigger. For example, here is some code I written to prevent some tables from being modified:
PRINT N'Create DDL trigger to prevent changes to various tables'
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER NoEditCertainTables ON DATABASE
FOR DROP_TABLE, ALTER_TABLE, CREATE_TABLE
AS
SET CONCAT_NULL_YIELDS_NULL ON
DECLARE #AffectedTable varchar(255)
SELECT #AffectedTable = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(100)')
--This is the name of the table(s) you dont want messed with
IF (#AffectedTable IN ('Table1','Table2','Table3'))
BEGIN
ROLLBACK;
END
SET CONCAT_NULL_YIELDS_NULL OFF
GO

Why does sql server generate stored procedures using sp_executesql statement with string?

When I generate sql schema creation scripts manually I usually just call 'Create Procedure...', however I notice that when you generate the script using the Tasks/Generate Scripts option it uses 'spexecutesql #statement = ..' e.g.
EXEC dbo.sp_executesql #statement = N'-- =============================================
-- Author: Me
-- Create date: 20/03/2009
-- Description: Does stuff
-- =============================================
CREATE PROCEDURE [dbo].[MyProc]
-- Add the parameters for the stored procedure here
#StartDate datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
...
END
'
Why is this? Is it something about retaining comments?
Thanks
It has nothing to do with comments. It does it that way only when you tell it to "include IF NOT EXISTS". The reason is that it can only programmatically include or exclude objects if they are executed dynamically.
You can disable this is stored procedures by selecting "False" in Options\SQL Server Object Explorer\Scripting - Check for object existence.
I realize this is old, but the fix is buried pretty deep in Sql 2012. Michael Haren is right, the method of rendering sprocs changes when Object Existence Checks are required in Options. To change this, go to Options, Sql Server Object Explorer, Scripting, Object Scripting Options, and set 'Check for object existence' to false. Sprocs now render 'normally', without using sp_executesql.
sql server 2012,set Tools=>Options=>SQL Server Object Explore=>Scripting ,check object existence = false
can solve this problem.
I would guess it has to do with being able to create multiple sprocs in the same script file without GO's? If you do a create sproc ... directly, you have to complete it in a batch (finish with a GO). With the sp_executesql you shouldn't have to have a go in the generated scripts between objects. Although I don't remember, maybe there is one there anyhow.. (don't have a db in front of me).
Using spexecutesql is a best-practice. Has to do with preventing sql injection, etc. by isolating / limiting the scope of variables, etc. More here: http://msdn.microsoft.com/en-us/library/ms188001.aspx
You don't HAVE to use spexecutesql -- EXEC works too -- a lot of folks just use plain old EXEC -- it's just not as safe.

Disable messages in SQL2008 result set

further to these two questions, is there a way to disable the messages that may get sent along with the resultset in SQL2008?
(please note this is nothing to do with the ANSI_WARNINGS setting. Or NOCOUNT.)
Thanks for any help.
Edit: It's not a problem with compatibility settings or table owners. And it's nothing to do with NOCOUNT. Trust me.
No, there's not a way to disable all messages that get sent along with the result sets. Set nocount on/off doesn't have an effect on these types of messages.
You need the NOCOUNT in the body of the Sproc anyway (I appreciate that you've tested it with and without)
In circumstances like this I get the actual call to the Sproc (either from a Debug in my APP, or using SQL Profiler) and then plug that into SSMS or whatever IDE you use, wrapping it in a ROLLBACK transaction (so it can't accidentally make any changes). Note: Log on to SQL Server, with your IDE, using the same credentials as the App will use.
BEGIN TRANSACTION
EXEC StaffEnquirySurnameSearch #searchterm = 'FOOBAR'
ROLLBACK
and see what you get. Use TEXT mode for output, rather than GRID mode which might hide something
Just to show how I think NOCOUNT shoud be added to your SProc:
CREATE PROCEDURE StaffEnquirySurnameSearch
#searchterm varchar(255)
AS
SET NOCOUNT ON
SELECT AD.Name, AD.Company, AD.telephoneNumber, AD.manager, CVS.Position,
CVS.CompanyArea, CVS.Location, CVS.Title, AD.guid AS guid,
AD.firstname, AD.surname
FROM ADCVS AD
LEFT OUTER JOIN CVS ON
AD.Guid=CVS.Guid
WHERE AD.SurName LIKE #searchterm
ORDER BY AD.Surname, AD.Firstname
GO
I note that you are not prefixing the tables with a database owner (most commonly "dbo") which might mean that there are additional copies owned by whomever and that they turn out to be the default from the applications permissions perspective, although I don't think that will change the resultsets [between SQL versions], However, same thing applies to ownership of the Sproc, and there you might be calling some earlier version, created for a different owner.
Ditto where your Sproc name is defined in your ASP.NET code (which I can't seem to find in your linked question) should also have the owner defined, i.e.
EXEC dbo.StaffEnquirySurnameSearch #searchterm = 'FOOBAR'
Did you change the compatibility level when you upgraded from SQL 2000 to 2008? If it is some sort of backward compatibility warning message that might cure it.
Have you tried running the same CONTAINS query without the "OR"?
i.e.:
SELECT * FROM my_table
WHERE CONTAINS(my_column, 'a monkey') -- "a" is a noise word
instead of
SELECT * FROM my_table
WHERE CONTAINS(my_column, 'a OR monkey') -- "a" is a noise word
You can wrap it in a try catch... more info in books online
For example:
CREATE TABLE Test_ShortString(
ShortString varchar(10) NULL
)
begin Try
insert into
Test_ShortString (ShortString)
values ('123456789012345')
End Try
Begin catch
--Select Error_Number() as ErrorNumber
end catch

Resources