August 17, 2023 at 4:38 pm
This is a contrived example - but illustrates what I'm trying to fix
I have a code where the client can change the sort order via a parameter.
DECLARE @pageTop INT = 1
DECLARE @pageBottom INT = 10
DECLARE @OrderBy NVARCHAR(500) = 'object_id desc';
DECLARE @SQL NVARCHAR(MAX) = '
SELECT * ,ROW_NUMBER() OVER(ORDER BY ' + @OrderBy + ') AS [RowNo] INTO #orderedCols FROM sys.columns ORDER BY ' + @OrderBy + ';SELECT * FROM #orderedCols WHERE RowNo BETWEEN @PageTop AND @PageBottom ORDER BY RowNo
'
EXEC sp_executeSql @SQL, N'@PageTop INT, @PageBottom INT', @pageTop, @pageBottom
And this is vulnerable to injection attacks as follows
DECLARE @pageTop INT = 1
DECLARE @pageBottom INT = 10
DECLARE @OrderBy NVARCHAR(500) = 'name) FROM sys.database_principals;PRINT ''you have been hacked!''--'
DECLARE @SQLUnsafe NVARCHAR(MAX) = '
SELECT * ,ROW_NUMBER() OVER(ORDER BY ' + @OrderBy + ') AS [RowNo] INTO #orderedCols FROM sys.columns ORDER BY ' + @OrderBy + '; SELECT * FROM #orderedCols WHERE RowNo BETWEEN @PageTop AND @PageBottom ORDER BY RowNo
'
EXEC sp_executeSql @SQLUnsafe, N'@PageTop INT, @PageBottom INT', @pageTop, @pageBottom
The Order by parameter cannot be included in the sp_executeSql parameters - this does not work
DECLARE @pageTop INT = 1
DECLARE @pageBottom INT = 10
DECLARE @OrderBy NVARCHAR(500) = 'object_id desc';
DECLARE @SQL_givesError NVARCHAR(MAX) = '
SELECT * ,ROW_NUMBER() OVER(ORDER BY @OrderBy) AS [RowNo] INTO #orderedCols FROM sys.columns ORDER BY @OrderBy; SELECT * FROM #orderedCols WHERE RowNo BETWEEN @PageTop AND @PageBottom ORDER BY RowNo
'
EXEC sp_executeSql @SQL_givesError, N'@OrderBy NVARCHAR(500), @PageTop INT, @PageBottom INT', @OrderBy, @pageTop, @pageBottom
Error is:
Msg 1008, Level 16, State 1, Line 57
The SELECT item identified by the ORDER BY number 1 contains a variable as part of the expression identifying a column position. Variables are only allowed when ordering by an expression referencing a column name.
How can I parameterize @OrderBy to protect against the injection attack
August 18, 2023 at 1:32 pm
It's a bit hacky and not as user-friendly, but if you were to create two arguments as follows
@SortColumnCode TINYINT
@SortOrder BIT = 0
and then decode the SortColumnCode number to a column name inside your proc, the injection threat goes away because the datatype is enforced. But you'd have to provide a Code/Column Name list to the client.
The absence of evidence is not evidence of absence
- Martin Rees
The absence of consumable DDL, sample data and desired results is, however, evidence of the absence of my response
- Phil Parkin
August 18, 2023 at 3:29 pm
Maybe a TVP w/ SortColumnName and Direction/SortOrder columns? SortOrder could be constrained to 'ASC' & 'DESC', or '+' & '-'
With validation in the procedure that SortColumnName is actually a valid column name
August 18, 2023 at 4:27 pm
--
With validation in the procedure that SortColumnName is actually a valid column name
I thought about this too. But this validation needs to be written carefully, also to avoid the possibility of injection.
The absence of evidence is not evidence of absence
- Martin Rees
The absence of consumable DDL, sample data and desired results is, however, evidence of the absence of my response
- Phil Parkin
August 19, 2023 at 10:42 am
For completeness: code for Phil's solution
public enum SortColumn (
Name = 1,
Object_Id = 2
)
public enum SortDirection (
Ascending = 0,
Descending = 1
)
public class SortSubClause (
public SortColumn SortColumn {get; set;}
public SortDirection SortDirection {get; set;} = SortDirection.Ascending
)
public class SortOrder (
public IEnumerable<SortSubClause> SortClauses {get; set;}
)
// build TVP type udt_SortOrder from SortOrder
CREATE TYPE udt_SortOrder AS TABLE (
SortColumnId TINYINT NOT NULL,
SortDirection BIT NOT NULL
)
-- in the PROC add parameter udt_SortOrder
-- example
DECLARE @SortClauses [udt_SortOrder]
INSERT INTO @SortClauses (SortColumnId, SortDirection) VALUES
(1,1) ,(2,0)
DECLARE @OrderBy NVARCHAR(MAX);
SELECT @OrderBy = STUFF ((
SELECT ', ' + CASE SortColumnId WHEN 1 THEN '[Name]'
WHEN 2 THEN '[object_id]'
END + ' ' +
CASE WHEN SortDirection = 1 THEN 'DESC' ELSE '' END
FROM @SortClauses
FOR XML PATH ('') ),1,2,'')
SELECT @OrderBy
Advantage - deals with the injection threat - and client can only use valid sort parameters.
But the disadvantage is maintenance in two places
August 19, 2023 at 12:54 pm
Curious, why not create 2, 4, or ?? procs instead? Instead of enums in C#, a UDT, and dynamic SQL could you not get rid of the parameterization and just create a proc for each situation?
Aus dem Paradies, das Cantor uns geschaffen, soll uns niemand vertreiben können
August 19, 2023 at 1:27 pm
unless the client is able to do their own sql, the list of possible columns, or even formulas used, would be available upfront on the UI - so allowing them to select for a list of possible column from within the output list, and specifying if ASC/DESC should be trivial.
that output number of the column (from output list) would then be the one supplied to the SP, or even inserted into a session table for use within the SP. Using the column ordinal instead of the name would make it trivial to validate data as only valid values would be either a number or ASC/DESC.
August 20, 2023 at 7:42 pm
This was removed by the editor as SPAM
Viewing 8 posts - 1 through 7 (of 7 total)
You must be logged in to reply to this topic. Login to reply