在存储的proc中将运算符作为参数传递,同时防止SQL注入的风险

时间:2018-11-01 15:37:02

标签: sql sql-server sql-server-2014

我有以下动态查询,想将运算符作为参数传递,但出现错误

  

@ageOperator附近的语法不正确

此外,我想防止任何SQL注入。我可以像下面那样进行串联,但这会导致SQL注入。

@sWhere = ' WHERE AGE1 ' + @ageOperator + ''' + @age + '''

提前谢谢!

DECLARE @age INT, 
        @ageOperator VARCHAR(10), 
        @sSQL NVARCHAR(MAX), 
        @sWhere NVARCHAR(MAX)

SET @age = 21
SET @ageOperator = ' = '

SET @sWhere = ' WHERE AGE1 @ageOperator @age '

SET @sSQL = ' SELECT * FROM WEBPM_COPY.DBO.Test1111 ' + @sWhere 

EXEC sp_executesql @sSQL, N'@age INT, @ageOperator char(10)', @age, @ageOperator;

5 个答案:

答案 0 :(得分:4)

如果要执行此代码,必须将运算符连接到@sWhere变量:

set @sWhere = ' WHERE AGE1 '+ @ageOperator +' @age ';

但是,10个字符足以写'=1 or 1=1--'(恰好是10个字符),因此它不是真正的SQL注入安全。

更好的实现方式是接受一组有限的运算符并将其列入白名单:

DECLARE @age INT, 
        @ageOperator VARCHAR(10), 
        @sSQL NVARCHAR(MAX), 
        @sWhere NVARCHAR(MAX);

SET @age = 21;
SET @ageOperator = ' = ';

IF @ageOperator NOT IN('=', '<=', '>=', '<>') 
    RAISERROR ('Invalid @ageOperator', -1, 1);  

SET @sWhere = ' WHERE AGE1 '+ @ageOperator +' @age ';

SET @sSQL = ' SELECT * FROM WEBPM_COPY.DBO.Test1111 ' + @sWhere ;

EXEC sp_executesql @sSQL, N'@age INT, @ageOperator char(10)', @age, @ageOperator;

答案 1 :(得分:1)

您无法参数化运算符。符合sp_executesql parameter description

  

每个参数定义均由参数名称和数据类型组成

您只能使用有价值的数据作为参数。 回答您的问题,我建议您实施自己的逻辑来检查运算符:

case @ageOperator when '=' then ... else <invalid case> end

答案 2 :(得分:1)

如果打印出@sSQL变量是什么,结果将是SELECT * FROM WEBPM_COPY.DBO.Test1111 WHERE AGE1 @ageOperator @age,因此它实际上是将变量名作为字符串而不是其值传递。这是您的错误消息的来源。

例如将@sWhere的分配更改为

set @sWhere = ' WHERE AGE1 ' + @ageOperator + CAST(@age AS VARCHAR(3))导致@sSQL成为SELECT * FROM WEBPM_COPY.DBO.Test1111 WHERE AGE1 = 21,这是有效的语法。

我不确定这是否仍然可以防止SQL注入-我认为应该这样做,因为您不能对'age'的3个字符进行太多操作,但是它可以解决您遇到的“ @ageOperator附近的语法不正确”错误得到。

答案 3 :(得分:1)

您可以参数化查询,只需要在动态sql中执行动态sql。我使用以下查询来获得您的预期结果:

DECLARE @dSQL NVARCHAR(MAX)
        ,@dage INT = 21
        ,@dageOperator VARCHAR(10) = ' = '

SET @dSQL = N'
DECLARE @sSQL NVARCHAR(MAX)
    ,@sWhere NVARCHAR(MAX)

SET @sWhere = '' WHERE AGE1'' + @ageOperator + CAST(@age AS VARCHAR)
SET @sSQL = '' SELECT * FROM WEBPM_COPY.DBO.Test1111 '' + @sWhere

EXEC(@sSQL)'

EXECUTE sp_executesql @dSQL, N'@age INT, @ageOperator varchar(10)', @dage, @dageOperator;

答案 4 :(得分:0)

动态sql是否必要?如果您的问题确实像在INT范围内的条件运算符一样简单,那么您应该能够执行以下操作而无需借助动态命令:

-- test setup
declare @YourTable table (Id int primary key, Age int);
insert into @YourTable
    select 1, 18 union all
    select 2, 25 union all
    select 3, 40 union all
    select 4, 2;


declare @Age int = 19;
declare @AgeOperator char(1) = '<';


-- option 1: 
-- No need for dynamic sql if you just use conditional filters in the
-- where clause.
select  * 
from    @YourTable
where   (Age > @Age and @ageOperator = '>') or
        (Age < @Age and @ageOperator = '<') or
        (Age = @Age and @ageOperator = '=');



-- option 2: 
-- You may see better performance by calculating the range once and using 
-- it in the query instead of the OR conditions
declare @MinAge int;
declare @MaxAge int;

select @MinAge = case @AgeOperator 
    when '<' then 0 
    when '>' then @Age + 1
    when '=' then @Age 
    else null
end;

select @MaxAge = case @AgeOperator
    when '<' then @Age - 1
    when '>' then 2147483647
    when '=' then @Age
    else null
end;


select * 
from   @YourTable
where  Age >= @MinAge and Age <= @MaxAge;