我有以下动态查询,想将运算符作为参数传递,但出现错误
@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;
答案 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;