SQL Injection: Stored Procedures & EXEC, SP_EXECUTE

Stored procedures are generally a good way of preventing SQL injection as they encourage parameterisation. A big exception to this rule is if exec\execute\sp_executesql is used within a stored procedure, these may run a string built up from component parts. These component parts can have malicious code injected into them.

The contents of your stored procedure may look something like below.

EXEC(@SQL)

or

EXECUTE [sys].[sp_execute] @SQL

This is major risk. How has @SQL been constructed? If it is by appended strings (and why else would you use this technique) you have a high risk item.

A better solution is sp_executesql with parameters. The example below has a query containing parameters – @SQLToRun. The parameters are passed in using the next two arguments. If constructed correctly this can encourage query plan reuse and prevent sql injection. Also just like calling a stored procedure from code all parameters are double quoted automatically preventing injection attacks.

DECLARE @ParmDefinition  AS NVARCHAR(200)
SET @ParmDefinition = N’@param1  NVARCHAR(40)’;

EXECUTE [sys].[sp_executesql]
@SQLToRun,
@ParmDefinition,
@param1 = param1In

The @SQLToRun is text from somewhere and can be prone to injection if appended together from different strings rather than using a fixed string and so is still a risk. Ideally @SQLToRun should be a fixed string in the stored procedure, something like

set @SQLToRun = “select mycolumn from mytable where myothercolumn = @myparam1”

However you often use this technique concatenate strings to execute

set @SQLToRun = mystring1 + mystring2; <- High risk

You need to examine carefully how the @SQLToRun is constructed. The strings should be selected by case of if statements depending on the a parameter. What I mean by this is that no user input should end up in @SQLToRun rather it should be build up like below

set @SQLToRun = “select mycolumn from mytable”

IF @param == 1

BEGIN

set @SQLToRun = @SQLToRun +”inner join othertable on mytable.Id = othertable.id

END

set @SQLWhere = “Where ”

IF @param == 1

BEGIN

set @SQLWhere = @SQLWhere +”othertable.id = @userparameter”

END

 

Then called as below. This way any user input only arrives in @userparameterIn as a parameter to a statement

EXECUTE [sys].[sp_executesql]
@SQLToRun + @SQLWhere ,
@ParmDefinition,
@userparameter = @userparameterIn

 

A strong sign something is wrong is if the parameters are missing I.e.

EXECUTE [sys].[sp_executesql]  @SQLToRun

or

EXEC(@SQLToRun)

As the stored procedure is trusted it will be able to perform actions that the service account connecting to the db may not be able to do directly. What I mean is the service account may be limited in its permissions on the database i.e. it may only have permission to read 3 tables. When calling the stored procedure it is trusted and code within it can probably do a lot more than read a few tables. That is why this approach needs to be crafted with care.

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s