当输入被剥离所有撇号时,是否有可能通过 SQL 注入利用查询?

Is it possible to exploit a query via SQL Injection when the input is stripped of all apostrophes?

提问人:Marie 提问时间:4/28/2017 最后编辑:Marie 更新时间:5/3/2017 访问量:328

问:

我一直在将不安全的动态查询转换为参数化查询。我已经找到了利用人们为清理他们的输入而进行的大多数较弱尝试的方法,但我还没有弄清楚如何实际利用本质上是

DECLARE @input VARCHAR(100) = ''';SELECT ''INJECTED''--'
DECLARE @SQL NVARCHAR(100) = 'SELECT ''example'' WHERE ''1'' = ''' + REPLACE(@input, '''', '') + ''''
EXEC sp_executesql @SQL

DECLARE @input VARCHAR(100) = ''';SELECT ''INJECTED''--'
DECLARE @SQL NVARCHAR(100) = 'SELECT ''example'' WHERE ''1'' = ''' + REPLACE(@input, '''', '''''') + ''''
EXEC sp_executesql @SQL

我在网上看到有人中断转义(在MySQL中,但从未在TSQL中),但我无法找到任何方法来中断剥离单引号。

我也计划修复上述实例,但我无法弄清楚如何证明它们是不安全的。

如何利用上述查询?

sql-server sql-server-2008 t-sql sql 注入

评论

2赞 Sean Lange 4/28/2017
不,这实际上并不安全。您仍然可以发送二进制数据,这些数据将不包含撇号,但仍然会造成严重破坏。为什么不使用适当的参数化查询并停止执行字符串?很少有时候你真的需要动态sql...甚至动态 SQL 也可以参数化。
0赞 Sean Lange 4/28/2017
加倍报价甚至不安全。
2赞 SqlZim 4/28/2017
关于 sql server 中动态 sql 的宝贵参考:动态 SQL 的诅咒和祝福 - Erland Sommarskog
1赞 Damien_The_Unbeliever 4/28/2017
为什么要花时间和精力去思考这些事情。正确的解决方案是参数化。这是 100% 有效的,因为它将代码数据分开。即使我们,互联网上的随机陌生人,告诉你你的疯狂编码方案没问题,你怎么能确定
0赞 Marie 4/28/2017
我已经说过我正在解决这些问题。我是发现它们并要求我们修复它们的人。据我自己所知,我很好奇是否有一个简单的方法可以解决我发布的两种方法。

答:

4赞 Sean Lange 4/28/2017 #1

您可以并且仍然应该参数化动态 sql,以使其真正安全。您的示例查询可能如下所示。

declare @z int = 10

EXEC sp_executesql 'SELECT x FROM y WHERE MyColumn = @z', N'@z int', @z

现在,我们已经在动态 sql 中创建了一个参数化查询,并将该参数传递给执行。这不容易受到 sql 注入的影响。

--编辑--

由于您想要回答您的原始查询是否容易受到 sql 注入的影响的问题,我将证明它实际上是相当开放的。您只处理单引号,这在SQL注入方面确实处于冰山一角。你有没有考虑过,如果他们传入字符串的二进制表示形式而不是字符串,会发生什么?

假设您的查询正在接收 nvarchar(max) 作为参数。在我的示例中,我称之为@BadCode。现在,在我的示例中,我没有对您的系统造成任何伤害,但在野外,这个二进制文件实际上可以是任何东西。

下面是一个简单的存储过程,其模式与您演示的模式非常接近。

create procedure InjectionTest
(
    @BadCode nvarchar(max)
) as
    set @BadCode = REPLACE(@BadCode, '''', '')
    EXEC sp_executesql @BadCode

GO

现在,我将从前端传入值0x730065006C0065006300740020002A002000660072006F006D0020007300790073002E00640061007400610062006100730065007300。同样,这是无害的二进制文件。如果你想看,只需把它放在一个 convert(varchar(max),...

下面是调用上述过程的示例。传入的二进制字符串是用户将传入的内容。请注意,其中没有单引号。

declare @Test nvarchar(max) = 0x730065006C0065006300740020002A002000660072006F006D0020007300790073002E00640061007400610062006100730065007300

exec InjectionTest @Test

有很多更长的解释和更多的诡计,但这展示了如何轻松打破它的基本原理。

评论

0赞 pmbAustin 4/28/2017
是的。这应该是一个基本规则,永远不要在任何将暴露给用户输入的系统中从字符串连接构建 SQL。始终参数化 SQL。这还有一个额外的好处,即不会污染计划缓存,从而在一定程度上提高了性能。
0赞 Marie 4/28/2017
我知道这一点,我已经在更新所有这些。我只是好奇它是否容易被利用,因为我想不出办法。这甚至不能远程回答我的问题。
0赞 Marie 4/28/2017
您的编辑无疑证明了第一个例子。看起来它也涵盖了第二个示例,但我无法弄清楚如何将字符串转换为有效的二进制文件。running 返回结果,显示为 .你是怎么得到这个二进制文件的?SELECT convert(binary, '1'';union all select ''test''')0x31273B756E696F6E20616C6C2073656C6563742027746573742700000000✱画楮湯愠汬猠汥捥⁴琧獥❴
0赞 Sean Lange 4/28/2017
使用 convert(varbinary(max), 'Your String Here')
0赞 Marie 4/28/2017
奇怪的是,这对我不起作用,即使将结果从 @Test 转换出来,也会得到略有不同且较短的二进制结果。除此之外,当二进制文件被分配给 @Test 时,转换似乎会立即发生,这意味着仍然会去除任何撇号并防止注入。REPLACE(@BadCode, '''','')