使用复杂的动态SQL定位OLEDB链接服务器避免SQL注入

时间:2017-07-05 03:04:04

标签: sql-server oledb sql-injection linked-server

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

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

链接服务器实际上是连接到OSISoft PI的OLE-DB提供程序接口,OSISoft PI是一个专门的历史数据库。我无法在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个位置参数来强制它,包括许多重复?

1 个答案:

答案 0 :(得分:1)

最后,我与PI系统的管理员合作,在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 Server上下文中运行的常规SQL查询替换动态SQL。这消除了SQL注入风险,并显着提高了代码的可读性。