1

I have a very simple stored procedure which generates the next sequence as part of a simple Auto Number solution.

The SQL store procedure is

CREATE PROCEDURE [dbo].[NextAutoNumber]

@Key varchar(100),
@UserId varchar(50),
@Return bigint output

AS

BEGIN

SET NOCOUNT ON;

DECLARE @cmd NVARCHAR(1000)

IF (NOT EXISTS (SELECT * FROM sys.sequences WHERE name = @Key))
BEGIN
--CREATE SEQUENCE FOR NEW ENTITY
EXEC('CREATE SEQUENCE [dbo].' + @Key + ' AS BIGINT START WITH 1 INCREMENT BY 1 NO CYCLE')
END

SET @cmd = 'SET @Return = (NEXT VALUE FOR ' + @Key + ')'

EXEC sp_executesql @cmd, N'@Return int output', @Return output;

END

GO

I am calling this stored procedure from a Web Service as below.

        using (var conn = new SqlConnection(_connectionString))
        using (var cmd = new SqlCommand("NextAutoNumber", conn))
        {

            cmd.CommandType = CommandType.StoredProcedure;
            if (conn.State == System.Data.ConnectionState.Closed)
                conn.Open();

            cmd.Parameters.Add(new SqlParameter("@Key", SqlDbType.Text)
            {
                Value = key,
                Direction = ParameterDirection.Input
            });

            cmd.Parameters.Add(new SqlParameter("@UserId", SqlDbType.Text)
            {
                Value = userId,
                Direction = ParameterDirection.Input
            });

            cmd.Parameters.Add(new SqlParameter("@Return", SqlDbType.BigInt) { Direction = ParameterDirection.Output });

            cmd.ExecuteNonQuery();
            return (long)cmd.Parameters["@Return"].Value;
        }

Now, they have reported that this is susceptible to SQL injection attacks. Any ideas on how to modify this to prevent SQL injection?

I have looked at the following options - such as using a parameterized query but my SQL skills are pretty basic!

Kanini
  • 1,947
  • 6
  • 33
  • 58
  • 4
    Quote your object names. `[dbo].' + @Key + '` should be `[dbo].' + QUOTENAME(@Key) + '`. [Dos and Don'ts of Dynamic SQL](https://www.sqlservercentral.com/articles/dos-and-donts-of-dynamic-sql). – Thom A Jun 14 '19 at 14:07
  • 5
    Why not use an identity instead? This seems overly complicated as I am guessing this gets a new sequence for every table. – Sean Lange Jun 14 '19 at 14:10
  • I agree with using @@identity upon insert. Return it as an output parameter of the insert proc. – DrDoomPDX Jun 14 '19 at 14:36
  • @Larnu: Thanks! Would this prevent someone from calling my REST API as `{ "Key": "test); WAITFOR DELAY '0:0:5'--", "UserId": "TestUser1@nodomain.co.in" }` and let the query run for 5 seconds due to the WAITFOR DELAY? – Kanini Jun 14 '19 at 14:38
  • @SeanLange: Thanks! I am a little lost on where to use the identity. My SQL skills are pretty basic, so I am currently on reading up about `identity`....can you tell me how to modify it or point me to something which I can read up on. I am reading the Microsoft documentation at the moment. – Kanini Jun 14 '19 at 14:43
  • 1
    The identity property is an auto number for a column. You would use that in your table definition instead of a separate sequence for each table. Would pretty much eliminate the need for all this complication. – Sean Lange Jun 14 '19 at 14:47
  • "Now, they have reported that this is susceptible to SQL injection attacks" - they are correct, and I don't think you can fix that as long as you need to issue DDL rather than DML. So the "fix" here is: don't issue DDL! As already mentioned, this sounds a case for an `IDENTITY` column (DDL=commands that change the database *schema* - `CREATE SEQUENCE`, etc; DML=commands that change the database *data* within the existing schema - `INSERT`, `UPDATE`, etc) – Marc Gravell Jun 14 '19 at 14:56
  • @MarcGravell Thanks! I will look at that. But, there was a recommendation that we use parameterized queries. Isn't the existing code already using it? – Kanini Jun 20 '19 at 13:52
  • @Larnu: I did try using `QUOTENAME` but unfortunately, if I were to call the REST API with `WAITFOR DELAY` it still had the same issue. – Kanini Jun 20 '19 at 13:55
  • @Kanini no, you've completely bypassed parameters by using `EXEC`; the only "safe" way to use `EXEC` is via `sp_executesql`, by declaring and using them **as parameters** in the inner-query, and if you do *that*, you'll get the same SQL errors that you would have got if you'd have tried doing it without `EXEC` in the first place. So basically: don't use `EXEC` (unless you use it correctly, via `sp_executesql` and inner-parameters) – Marc Gravell Jun 20 '19 at 15:58

0 Answers0