SP_EXECUTESQL中的 SQL 注入

SQL injection in SP_EXECUTESQL

提问人:Patricie Benesova 提问时间:4/17/2019 最后编辑:marc_sPatricie Benesova 更新时间:4/25/2019 访问量:798

问:

我在SQL Server中有一个存储过程,它获取XML作为输入参数。在此XML中定义了 - 应该使用哪些参数值执行哪些存储过程。并据此,存储过程使用动态 SQL 执行所需的 .sp_executesql

问题在于参数的值容易受到 SQL 注入的影响。

我尝试过使用这样的类型化参数:

EXEC sys.sp_executesql 
  @stmt = @sql, 
  @params = N'@Username SYSNAME, @HireDate DATE',
  @UserName = @Username, @HireDate = @HireDate;

但它在我的情况下并不真正起作用,因为我不知道将执行具有哪些参数的程序。参数的数量可能会有所不同,其中一些是可选的/具有默认值等。我所能得到的只是字符串:(的参数名称

在对输入 XML 进行一些解析后,将像这样构建和执行 SQL 查询

declare @params nvarchar(max);
    select @params = coalesce(@params + N', ', N' ') + r.attrName + N' = ' + iif(p.isNumericType = 1, r.Value, '''' + r.Value /*cast(r.Value as nvarchar(max))*/ + '''') --+ r.Value
    from dbo.#ruleConfig r
        left join @spParams p on p.paramName = r.attrName -- datatype of a parameter from information_schema.parameters 

    declare @sql nvarchar(max) = (select @procName + isnull(@params, N''));

    exec dbo.sp_executesql @sql

@sql的值可以如下所示:

'core.GetUser @LogonName = 'myDomain\myLogon''

但也可以看起来像这样:

'core.GetUser @fullLogonName = 'myDomain\myLogon;'WAITFOR DELAY '0:0:20';--'' and that's the problem.
sql-server t-sql 注入 动态 sql

评论

0赞 Panagiotis Kanavos 4/17/2019
In this XML is defined - what store procedure with which parameters values should be executed.为什么?为什么让数据库分析此字符串,而不是让客户端正确调用存储过程?这是问题的根源,而不是sp_executesql
1赞 Panagiotis Kanavos 4/17/2019
该字符串,无论是否可以解析为 XML,本身都容易受到 SQL 注入和转换错误的影响。它也比使用 ADO.NET、ODBC 或您的语言使用的任何协议对数据库的正确调用大数百倍。
0赞 Thom A 4/17/2019
“但在我的情况下,它并不真正起作用,因为我不知道将执行什么程序和什么参数。然后你可以构建一个动态语句(不理想,但我已经看到了),或者传递所有参数,无论它们是否会在动态语句中使用。 不会出错,即使从未在“动态”查询中使用过。实际上,我们这里没有任何信息可以对此给出完整的答案。EXEC sp_executesql N'SELECT 1 AS one;',N'@i int', @i = @i;@i
0赞 Gert-Jan 4/17/2019
我不认为这是不可能的。如果你不能信任你的用户,那就不要这样做。将 XML 解析为单个局部变量,并在常规数据库语句中仅使用这些局部变量。

答:

0赞 Gil Cohen 4/24/2019 #1

首先,如果有任何功能需要发送动态 SQL 存储过程执行命令,则您的设计存在一个大问题,因为使用严格的紧密耦合设计时,每个 API 调用都将映射到单个存储过程。

如果您仍然希望坚持使用当前设计,则必须创建已知存储过程及其值的列表。你不能只是接受这样一个事实,即你“不知道将执行什么过程和什么参数”,因为你发布SQL注入功能是按设计设计的。

您需要创建已知存储过程(例如 1 = proc1、2 = proc2 等)的枚举,并对其参数执行基于正则表达式的输入验证。

在刚才给出的示例中,如果您希望接受“myDomain\myLogon”和“myDomain\myLogon;”WAITFOR DELAY '0:0:20';--“ 被拒绝,例如,您可以使用如下所示的正则表达式:

^([A-Za-z\\])*$

您可以在 regexr 网站上对其进行测试。 您必须为每个字段类型创建一个输入验证正则表达式 - 例如名称、用户名、电子邮件等。基本上,这与为每个过程调用创建一个单独的 API 非常相似,并具有适当的输入验证。