sp_executesql保护动态搜索关键字

时间:2012-06-14 15:15:10

标签: sql sql-server

我正在尝试转换SQL语句以支持sp_executesql以使其安全但我遇到了一个不安全的区域。希望你们可以帮助我。我创建了临时表,以便更容易地演示问题。

问题出在第6步。我可以使用步骤#5但这不安全,可以轻松入侵。由于系统性能的原因,我真的不想打破关键字并多次搜索。

MS SQL 2008的错误消息4145,级别15,状态1,行4在预期条件的上下文中指定的非布尔类型的表达式,在“ORDER”附近。

GO
/****** Object:  StoredProcedure [dbo].[ups_MultiWareHouse]    Script Date: 06/14/2012 09:12:38 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO
create PROCEDURE ups_TestSearch(
@Keywords nvarchar(4000),
@SortColumns nvarchar(4000)
)
AS
--STEP #1 - Create Temp Table - Begin
    CREATE TABLE #TempTable 
    (
        ProductID uniqueidentifier,
        ProductName varchar(600),
        Price decimal(18,2),
        Active bit
    )

--STEP #2 - Insert couple records to search
    INSERT INTO #TempTable (ProductID,ProductName,Price,Active) VALUES(NEWID(),'Mouse','10.12','1')
    INSERT INTO #TempTable (ProductID,ProductName,Price,Active) VALUES(NEWID(),'Keyboard','20.45','1')
    INSERT INTO #TempTable (ProductID,ProductName,Price,Active) VALUES(NEWID(),'Monitor','150.87','0')--Disable this product

--STEP #3 - Display the current table data
        select 'STEP #3' as STEP,   * FROM #TempTable

--STEP #4 - SETTING UP sp_executesql to support parameter substitution
    --Set definition
    DECLARE @ParmDefinition nvarchar(4000);
    SET @ParmDefinition='
                        @Param1ProductName nvarchar(4000),
                        @Param2SortColumns nvarchar(4000)
                        '                   
    DECLARE @SQLString nvarchar(4000);

--STEP #5-  CONVERT THE @SQLString TO use @Keywords and @SortColumns
    --Run query for the below like this ups_TestSearch'ProductName=''Mouse'' OR ProductName=''Keyboard''', 'Price DESC, ProductName ASC'
    SET @SQLString = N'SELECT ''STEP #5'' as STEP, #TempTable.*  FROM #TempTable WHERE ('+@Keywords+') ORDER BY '+@SortColumns;--unsafe, open to hackers
    EXECUTE sp_executesql @SQLString, @ParmDefinition, @Param1ProductName = @Keywords, @Param2SortColumns=@SortColumns;

--STEP #6-  CONVERT THE @SQLString TO use @Keywords and @SortColumns
    --Run query for the below like this ups_TestSearch'ProductName=''Mouse'' OR ProductName=''Keyboard''', 'Price DESC, ProductName ASC'    
    SET @SQLString = N'SELECT ''STEP #6'' as STEP, #TempTable.*  FROM #TempTable WHERE (@Param1ProductName)  ORDER BY @SortColumns';--Safe but not working
    SELECT @SQLString AS SeeStatement
    EXECUTE sp_executesql @SQLString, @ParmDefinition, @Param1ProductName = @Keywords, @Param2SortColumns=@SortColumns;

--Drop temp table
DROP TABLE #TempTable

2 个答案:

答案 0 :(得分:0)

我认为问题在于,在步骤5中,您没有使用参数替换 - 也就是说,您基本上是通过字符串连接来构建SQL语句。当你通过sp_executesql执行它时,你真的可以这样做:

EXECUTE sp_executesql @SqlString

步骤6中的代码执行参数替换。但是,在这种情况下,您仅限于在“普通”SQL表达式中允许使用参数的位置。例如,您无法在T-SQL中执行此操作:

DECLARE @Criteria NVARCHAR(500);
SET @Criteria = N' WHERE ProductName = ''Mouse'''
SELECT * FROM #MyTempTable + @Criteria

根据您希望过滤器的复杂程度,您可以将条件写入临时表并对临时表执行连接以限制返回的结果数据。除了你在调用代码中这样做之外,我不确定如何最好地对结果数据进行排序?

答案 1 :(得分:0)

您的错误消息表明步骤6中的WHERE子句无效,ORDER BY子句也是如此。这是因为您将字符串作为参数传递给sp_executesql并尝试将它们用作整个子句。此外,该语句引用参数@SortColumns,但您似乎已将参数命名为@Param2SortColumns

阅读一些SQL服务器MVP编写的内容:

http://www.sommarskog.se/dynamic_sql.html

更重要的是:http://www.sommarskog.se/dyn-search.html

http://www.sqlmag.com/article/tsql3/parameterizing-result-order

我没有看到一种简单的方法来改变您的程序以使其工作,因为您将整个WHEREORDER BY子句作为参数传递。你应该做的是重新设计proc。将每个WHERE条件作为单个参数提供给ups_TestSearch。您将每个WHERE参数重新提供给sp_executesql并以这种方式构造初始SQL语句:

SET @SQLString = SELECT and JOIN portions of command
SET @SQLString = @SQLString + 'WHERE 1 = 1 '
  IF (@WhereParam1 IS NOT NULL)
    SET @SQLString = @SQLString + 'AND (SomeTable.SomeColumn = @WhereParam1) '
  IF (@WhereParam2 IS NOT NULL)
    SET @SQLString = @SQLString + 'AND (SomeTable.SomeColumn = @WhereParam2) '
  ...

如有必要,可以使用相同的结构将连接添加到语句中。

ORDER BY结构取决于它可能有多复杂,以及您是否知道所有可能涉及的列。如果它相对简单,您可以将其写为CASE语句,如下所示,或者将其作为单独的参数分解,正如我对WHERE子句所推荐的那样。

ORDER BY
  CASE WHEN CHARINDEX(@SortColumns, 'SortCol1') > 0 THEN SortCol1 ELSE NULL END,
  CASE WHEN CHARINDEX(@SortColumns, 'SortCol2') > 0 THEN SortCol2 ELSE NULL END,
  ...

这里最简单的做法可能是在应用程序级别而不是数据库中进行排序,但这可能与复杂的ORDER BY子句参数化一样不可行。