0

我正在尝试将 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
4

2 回答 2

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

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

于 2012-06-14T15:46:45.093 回答
0

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

阅读一些 SQL Server 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子句作为参数传递。你真正应该做的是重新设计过程。将每个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,
  ...

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

于 2012-06-14T17:24:52.650 回答