当使用 sp_executesql 作为过滤器时,保护 t-sql 动态代码的最佳方法是啥
Posted
技术标签:
【中文标题】当使用 sp_executesql 作为过滤器时,保护 t-sql 动态代码的最佳方法是啥【英文标题】:What is best way to secure t-sql dynamic code when it comes as filters using sp_executesql当使用 sp_executesql 作为过滤器时,保护 t-sql 动态代码的最佳方法是什么 【发布时间】:2020-02-11 21:01:32 【问题描述】:我正在制作一个接受动态过滤的存储过程,我遇到的问题是我需要尽可能保持灵活性。
ALTER PROCEDURE astp_test
@WhereClause NVARCHAR(max) = NULL
AS
DECLARE @FilteredResults AS TABLE (testId int, testfield datetime2)
DECLARE @sql AS NVARCHAR(MAX) = N'SELECT testId , testfield
FROM aviw_test
WHERE IsOpen = 1 AND IsLatesInsert = 1
AND testStepNo = 7
AND test2 IS NULL
AND (testfielddate IS NULL OR testfielddate2 < GETUTCDATE())
AND Domain IN (SELECT Domain FROM project WITH (NOLOCK) WHERE Status = ''Active'')' +
CASE WHEN @WhereClause IS NOT NULL
THEN N' AND ' + @WhereClause ELSE N''
END
INSERT INTO @FilteredResults
EXEC sys.sp_executesql @stmt = @sql;
我想保护@WhereClause
输入,但以这种方式,因为有一些复选框会发送类似这样的内容:"AND testDatePick = '2019-10-10' AND testStage = 'InProgress' AND testArea = 'London' "
。那么最好的方法是什么?
【问题讨论】:
最好的办法是不使用动态sql。@WhereClause
包含什么?为什么不使用 ORM 来生成好的 SQL 查询?事实上,为什么不将@sql
转换为视图并在其上编写一个简单的查询呢? PS WITH (NOLOCK)
不会让慢查询运行得更快,它会在获取额外锁的同时读取脏数据。要使这个子查询运行得更快,请在Status, Domain
上添加一个索引,并可能使用 SNAPSHOT 隔离
我不同意这一点,@PanagiotisKanavos。然而,最好的方法是 safely 注入对象名称(使用QUOTENAME
)并参数化语句。但是,注入WHERE
是完全错误的想法,因为你永远无法保证它的安全。
如果您要接受原始 SQL,则没有什么好的方法可以防止任何事情发生;您必须信任客户端,因为在 T-SQL 中解析 T-SQL 是行不通的。在这种情况下,最好将查询逻辑完全集中在客户端中,这样您就可以在一个地方对其进行验证,并为不会更改的查询部分声明一个视图。我还建议阅读this article,了解有关进行动态搜索的不同、更安全的方法(以及与每种方法相关的陷阱)的大量背景信息。
我不同意笼统的说法“最好的方法是不使用动态sql。” @PanagiotisKanavos。该评论已被编辑以扩大,但肯定说只是说“动态 SQL 不好”是错误的。写得不好的动态 SQL 不好,但写得好、安全的动态 SQL 很好;它的用途肯定有时间。
要“修复”(保护)它,@pixe 您需要删除该参数 @WhereClause
并将其替换为每列需要比较的 1 个参数。这将需要对数据库和应用程序进行更改。这是一个要求。如果不这样做,您将无法确保其安全。如果您不能这样做,我强烈建议您删除该功能。
【参考方案1】:
请试试下面的
ALTER PROCEDURE astp_test
@WhereClause NVARCHAR(max) = NULL
AS
DECLARE @FilteredResults AS TABLE (testId int, testfield datetime2)
DECLARE @sql AS NVARCHAR(MAX) = N'SELECT testId , testfield
FROM aviw_test
WHERE IsOpen = 1 AND IsLatesInsert = 1
AND testStepNo = 7
AND test2 IS NULL
AND (testfielddate IS NULL OR testfielddate2 < GETUTCDATE())
AND Domain IN (SELECT Domain FROM project WITH (NOLOCK) WHERE Status = ''Active'')'
if @WhereClause is not null
set @sql=@sql + 'AND ' + @WhereClause
INSERT INTO @FilteredResults
EXEC sys.sp_executesql @stmt = @sql;
如果您有任何问题,请告诉我
谢谢
【讨论】:
以上是关于当使用 sp_executesql 作为过滤器时,保护 t-sql 动态代码的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章
是否可以使用sp_executesql将JSON作为out参数
为啥我得到“程序需要'ntext/nchar/nvarchar'类型的参数'@statement'。”当我尝试使用 sp_executesql 时?