使用T-SQL参数过滤表

时间:2012-03-06 22:38:57

标签: sql-server tsql stored-procedures sql-injection

请参阅此简单存储过程:

getUsers(@name, @age, @city) etc...
  • 如果我通过年龄过滤年龄
  • 如果我传递两个参数的名称和城市过滤器

我知道有一种方法可以构建我的SQL语句然后EXEC(mySQL)

但我想做点什么:

SELECT * into #tmp From Users WHERE active = 1
if(@name)
   SELECT * into #tmp From #tmp WHERE name = @name
if(@age)
   SELECT * into #tmp From #tmp WHERE age = @age
if(@city)
   SELECT * into #tmp From #tmp WHERE city = @city
SELECT * From #tmp -- result

这样我们可以避免SQL注入。

3 个答案:

答案 0 :(得分:4)

在我这里使用动态SQL似乎会让你更容易受到注入攻击。尝试:

select *
-- into #tmp
from Users
where active = 1
    or (@age is not null and age = @age)
    or (@name is not null and name = @name)
    or (@city is not null and city = @city)

如果您只是返回查询结果,不确定为什么需要临时表?除非你在其他地方使用它。

答案 1 :(得分:0)

另一种选择是在存储过程中使用sp_ExecuteSQL来使用参数化SQL,以保护您免受SQL注入

e.g。

DECLARE @SQL Nvarchar(max)
SET @SQL ='select *
           from Users 
           where active = 1'

IF @Name is not null 
    SET @SQL = @SQL + ' AND name = @Name'
IF @Age is not null
    SET @SQL = @SQL + ' AND age = @Age'

IF @City is not null
    SET @SQL = @SQL + ' AND city = @City'

exec sp_executeSQL @SQL, N'@Age int,
                         @Name varchar(255), @City varchar(255)',
                         @Age, 
                         @Name, 
                         @City

在这个简单的情况下,它可能不值得,我会使用technique described by AR,但如果你的查询足够复杂,那么它可能是值得的。

例如,假设城市在另一个表中并且记录并不总是存在。使用AR的技术,您需要这样做

select *
-- into #tmp
from Users
     LEFT JOIN user_city 
     ON user.user_Id = user_city.user_Id
         and @City is not null --We only want the join when @City is passed in
where active = 1
    or (@age is not null and age = @age)
    or (@name is not null and name = @name)
    or (@city is not null and city = @city)

请注意LEFT JOIN。使用动态SQL,您只需在需要时动态添加INNER JOIN,在某些情况下可以产生更好的性能。

答案 2 :(得分:-1)

我相信你所寻找的是动态SQL。我们的想法是构建一个查询字符串,然后使用EXEC(或sp_executesql)将字符串作为SQL语句运行。

所以给你一个想法,比如:

set @sql = 'select * into #tmp1 From #tmp2 '    
if @name = @someValidUserInput
begin
   set @sql = @sql + ' where name = @name'
   ...
end
exec(@sql)