这个存储过程是否可以安全地从sql注入?

时间:2009-09-03 15:51:38

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

此存储过程使用sp_executesql执行带参数的sql sql注入安全吗?

create procedure ExecutePeopleFilter 
    (@lastNameFilter varchar(20), 
    @companyNameFilter varchar(20), 
    @ageFilter int, 
    @dateFilter datetime)
as
begin
    declare @sql varchar(4000)
    declare @params varchar(1000)
    declare @whereClause varchar(1000)

    set @whereClause = ''

    if ISNULL(@lastNameFilter,'') <> ''
    begin
        if (LEN(@whereClause) <> 0) set @whereClause += ' and '
        if (LEN(@lastNameFilter) < 20) set @lastNameFilter += '%'
        set @whereClause += 'LastName like @lastName '
    end

    if ISNULL(@companyNameFilter,'') <> ''
    begin
        if (LEN(@whereClause) <> 0) set @whereClause += ' and '
        if (LEN(@companyNameFilter) < 20) set @companyNameFilter += '%'
        set @whereClause += 'CompanyName like @companyName '
    end

    if @ageFilter is not null
    begin
        if (LEN(@whereClause) <> 0) set @whereClause += ' and '     
        set @whereClause += 'Age = @age '
    end

    if @dateFilter is not null
    begin
        if (LEN(@whereClause) <> 0) set @whereClause += ' and '
        set @whereClause += 'StartDate = @startDate '
    end


    set @sql = 'select FirstName, LastName, CompanyName, Age, StartDate 
        from People'
    if (LEN(@whereClause) <> 0) set @sql += ' where ' + @whereClause

    set @params = '@lastName varchar(20), 
        @companyName varchar(20), 
        @age int, 
        @startDate datetime'

    execute sp_executesql @sql, @params, 
        @lastName = @lastNameFilter, 
        @companyName = @companyNameFilter, 
        @age = @ageFilter, 
        @startDate = @dateFilter
end

7 个答案:

答案 0 :(得分:3)

为什么在存储过程中这样做?一个更好的解决方案可能是在客户端攻击它,在调用存储过程之前转义字符串并检查长度。像MS Enterprise DAAB(​​.NET)这样的库通过在将命令对象添加到命令对象时指定数据类型和参数长度,提供了方便的方法。

答案 1 :(得分:2)

非常。

防止SQL注入的关键是通过“已批准”机制正确处理参数并避免字符串连接。

您的代码不会使用参数构建一个字符串:它们是通过sp_executesql分隔和清理的。

你是否这样做是另一回事......正如其他答案所示

答案 2 :(得分:1)

您不需要动态SQL来创建可选的WHERE子句......只需使用:

WHERE ((@x IS NULL) OR (@x = ...)) AND ...

也应该更快,并且没有溢出字符串,注入或其他任何东西的风险。

像这样:

CREATE PROCEDURE ExecutePeopleFilter
  (
   @lastNameFilter varchar(20),
   @companyNameFilter varchar(20),
   @ageFilter int,
   @dateFilter datetime
  )
AS 
  BEGIN
    SELECT FirstName, LastName, CompanyName, Age, StartDate
      FROM People
      WHERE (
             (ISNULL(@lastNameFilter, '') = '')
             OR (LastName LIKE @lastNameFilter+'%')
            )
        AND (
             (ISNULL(@companyNameFilter, '') = '')
             OR (LastName LIKE @companyNameFilter+'%')
            )
        AND (
             (@ageFilter IS NULL)
             OR (Age = @ageFilter)
            )
        AND (
             (@dateFilter IS NULL)
             OR (StartDate = @dateFilter)
            ) ;
  END

答案 3 :(得分:1)

除了众所周知的硬编码值之外,您永远不会将任何内容连接到要发布到数据库引擎的SQL语句中,因此它对所有当前已知的SQL注入方法都是安全的(并且应该能够抵御未来的攻击)。但是,它确实存在其他问题(例如未声明@startDate)。

答案 4 :(得分:1)

是的,您对字符串concat和sp_executesql的使用是正确的,并且SQL注入不会成为问题。

不,只是因为LINQ是新的热点并不意味着它是正确的解决方案。

也就是说,你的varchar(20)参数很容易溢出,你可能想要将它们稍微提高一点,至少达到实际字段的大小。

答案 5 :(得分:0)

这样。至少,看起来没关系。但这可能不是我怎么做的。

安全性的关键租户之一永远不会永远编写自己的安全代码。您总是希望尽可能地利用您的平台提供和内置的机制。你通过使用sp_executesql来做这件事,但我希望你也能做得更多。


根据要求,我可能更喜欢这样做:

create procedure ExecutePeopleFilter 
    (@lastNameFilter varchar(20) = NULL, 
    @companyNameFilter varchar(20) = NULL, 
    @ageFilter int = NULL, 
    @dateFilter datetime = NULL)
as
begin

    if (LEN(@lastNameFilter) < 20) set @lastNameFilter += '%'
    if (LEN(@companyNameFilter) < 20) set @companyNameFilter += '%'

    SELECT FirstName, LastName, CompanyName, Age, StartDate 
    FROM People
    WHERE  LastName    LIKE COALESCE(@lastNameFilter, LastName) 
       AND CompanyName LIKE COALESCE(@companyNameFilter, CompanyName)
       AND Age          =   COALESCE(@ageFilter, Age)
       AND StartDate    =   COALESCE(@dateFilter, StartDate)

end

不需要动态sql,WHERE子句中也没有“OR”。

答案 6 :(得分:0)

即使是......“唉”。

为什么不使用临时表,使用与非可选参数匹配的元素结果的ID填充它,然后根据已指定的参数从临时表中消除其余部分?完成后,只需加入您正在寻找的结果集。

CREATE TABLE #People
   (personid int)
INSERT INTO #People SELECT personid FROM people
IF NOT @lastNameParam IS NULL
   DELETE FROM #People WHERE personid NOT IN (SELECT personid FROM people WHERE lastname LIKE @lastNameParam + '%')
-- And so on...