在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个位置参数来强制它,包括许多重复?
答案 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注入风险,并显着提高了代码的可读性。