0

在 SQL Server 2016 中,我在存储过程中的 OLE-DB 链接服务器上运行复杂的动态 SQL 查询。

我目前正在将动态 SQL 构建为字符串,在许多地方连接参数。所以,我担心 SQL 注入。

链接服务器实际上是连接到 OSISoft PI 的 OLE-DB 提供程序接口,它是一个专门的历史数据库。我无法在 PI 中定义存储过程,因此我认为动态 SQL 是获得所需灵活性的唯一方法。

我确实使用该QUOTENAME(input, '''')函数将用户提供的参数包装在引号中,这也应该转义输入中找到的任何引号。但我不确定这是否构成对 SQL 注入的有效防御。我这样做主要是因为它使字符串连接中的文字更简单。

存储过程当前看起来像这样:

-- Wrap user-supplied parameters in quotes to simplify SQL string building
DECLARE @Tag1   NVARCHAR(30) = QUOTENAME(@Tag1Input, '''')
DECLARE @Tag2   NVARCHAR(30) = QUOTENAME(@Tag2Input, '''')
DECLARE @Tag3   NVARCHAR(30) = QUOTENAME(@Tag3Input, '''')
DECLARE @Tag4   NVARCHAR(30) = QUOTENAME(@Tag4Input, '''')
DECLARE @Tag5   NVARCHAR(30) = QUOTENAME(@Tag5Input, '''')
DECLARE @Tag6   NVARCHAR(30) = QUOTENAME(@Tag6Input, '''')
DECLARE @Tag7   NVARCHAR(30) = QUOTENAME(@Tag7Input, '''')
DECLARE @Tag8   NVARCHAR(30) = QUOTENAME(@Tag8Input, '''')
DECLARE @Tag9   NVARCHAR(30) = QUOTENAME(@Tag9Input, '''')
DECLARE @Tag10  NVARCHAR(30) = QUOTENAME(@Tag10Input, '''')
DECLARE @starttimeq NVARCHAR(10) = QUOTENAME(@starttime, '''')
DECLARE @endtimeq   NVARCHAR(10) = QUOTENAME(@endtime, '''')
DECLARE @timestepq  NVARCHAR(10) = QUOTENAME(@timestep, '''')
DECLARE @calcbasisq NVARCHAR(30) = QUOTENAME(@calcbasis, '''')

-- Build SQL statement
DECLARE @sql NVARCHAR(2000) = 'SELECT tag, time, value 
FROM piarchive..piavg 
WHERE 
    tag IN (' + @Tag1 + ', ' + @Tag2 + ', ' +@Tag3 + ', ' + @Tag4 + ', ' + @Tag5 + ', ' + @Tag6 + ', ' + @Tag7+ ', ' + @Tag8 + ', ' + @Tag9 + ', ' + @Tag10 + ') 
    AND time BETWEEN ' + @starttimeq + ' AND ' + @endtimeq + '
    AND timestep = ' + @timestepq + ' 
    AND calcbasis = ' + @calcbasisq + '
UNION
SELECT ''calculatedValue'' AS tag, time, value 
FROM piarchive..piavg
WHERE 
    expr = ''(''' + @Tag2 + ''' * (''' + @Tag3 + '''-''' + @Tag4 + ''') / (''' + @Tag2 + '''-''' + @Tag4 + ''') * 100.0 + ''' + @Tag5 + ''' * (''' + @Tag4 + '''-''' + @Tag1 + ''') / (''' + @Tag5 + '''-''' + @Tag1 + ''') * (''' +@Tag3 + '''-''' + @Tag2 + ''') / (''' + @Tag4 + '''-''' + @Tag2 + ''') * 100.0) / ((''' +@Tag3 + '''-''' + @Tag4 + ''') / (''' + @Tag2 + '''-''' + @Tag4 + ''') * 100.0 + (''' + @Tag4 + '''-''' + @Tag1 + ''') / (''' + @Tag5 + '''-''' + @Tag1 + ''') * (''' +@Tag3 + '''-''' + @Tag2 + ''') / (''' + @Tag4 + '''-''' + @Tag2 + ''') * 100.0)''
    AND time BETWEEN ' + @starttimeq + ' AND ' + @endtimeq + ' 
    AND timestep = ' + @timestepq + ' 
    AND calcbasis = ' + @calcbasisq + '
ORDER BY time ASC, tag ASC'

-- Invoke dynamic SQL on PI OLEDB linked server
EXEC (@sql) AT PI

据我所知,我不能使用 sp_executesql 在 OLE-DB 链接服务器上运行查询。(如果我错了,请纠正我)。

由于 OLE-DB 的限制,EXEC(@sql, <params>) AT LinkedServer语法似乎只支持位置参数。?由于可怕的expr过滤器子句,我真的想使用命名参数而不是位置参数。

当我不能使用 sp_executesql 或命名参数时,如何安全地准备这个 SQL 字符串以防止 SQL 注入攻击?有没有一种优雅的方法,或者我只需要使用 46 个位置参数(包括许多重复)来强制它?

4

1 回答 1

2

最后,我与 PI System 的管理员合作,在 pifunction 目录中启用了 PI DATE 和 TIME 函数视图。这些函数视图将特定于 PI 的时间文字转换为 SQL Server 可以在 SQL Server 执行上下文中处理的格式。(当我尝试自己设置这些功能视图时,我收到一个错误对话框,上面写着Error creating TIME - View creation failed. [PI SDK] Item not found in collection: %OSI。事实证明,这个错误是由于我的帐户在 PI 中没有足够的安全权限引起的。)

设置函数视图后,我可以使用标准参数替换,将动态 SQL 替换为在 SQL Server 上下文中运行的常规 SQL 查询。这消除了 SQL 注入风险,并显着提高了代码的可读性。

于 2017-07-07T00:25:27.813 回答