在SQL Server中,如何通过SQL注入来消除逃避单引号的卫生设施?

时间:2013-03-21 00:31:09

标签: sql-server security tsql sql-injection sanitization

为了开始这个,我很清楚参数化查询是最好的选择,但我想知道是什么让我在下面提出的策略容易受到攻击。人们坚持认为下面的解决方案不起作用,所以我想找一个为什么不会这样做的例子。

如果在发送到SQL Server之前使用以下转义在代码中构建动态SQL,那么哪种注入可以解决这个问题?

string userInput= "N'" + userInput.Replace("'", "''") + "'"

类似的问题已经回答here,但我不相信任何答案都适用于此。

在SQL Server中无法使用“\”转义单引号。

我相信使用Unicode(概述here)的 SQL走私会被生成的字符串被单引号前面的N标记为Unicode这一事实所阻碍。据我所知,SQL Server没有其他字符集会自动转换为单引号。没有未转义的单引号,我不相信注射是可能的。

我不相信 String Truncation 也是一个可行的载体。 SQL Server肯定不会截断,因为nvarchar的最大大小为2GB according to microsoft。在大多数情况下,2 GB的字符串是不可行的,在我的情况下是不可行的。

二次注射是可能的,但是如果可能的话:

  1. 使用上述方法
  2. 对进入数据库的所有数据进行清理
  3. 数据库中的值永远不会附加到动态SQL中(当你只能在任何动态SQL字符串的静态部分引用表值时,为什么还要这样做?)。
  4. 我并不是说这比使用参数化查询更好或者替代,但我想知道我概述的内容是如何易受攻击的。有什么想法吗?

6 个答案:

答案 0 :(得分:40)

在某些情况下,此转义功能将失败。最明显的是当不使用单引号时:

string table= "\"" + table.Replace("'", "''") + "\""
string var= "`" + var.Replace("'", "''") + "`"
string index= " " + index.Replace("'", "''") + " "
string query = "select * from `"+table+"` where name=\""+var+"\" or id="+index

在这种情况下,您可以使用双引号“后退”来“突破”。在最后一种情况下,没有什么可以“突破”,所以你可以只写1 union select password from users--或攻击者所需的任何sql负载。

这个转义函数失败的下一个条件是在字符串被转义后获取子字符串(并且我在野外发现了这样的漏洞):

string userPassword= userPassword.Replace("'", "''")
string userName= userInput.Replace("'", "''")
userName = substr(userName,0,10)
string query = "select * from users where name='"+userName+"' and password='"+userPassword+"'";

在这种情况下,abcdefgji'的用户名将由转义函数转换为abcdefgji'',然后通过获取子字符串转回abcdefgji'。这可以通过将密码值设置为任何sql语句来利用,在这种情况下,or 1=1--将被解释为sql,用户名将被解释为abcdefgji'' and password=。生成的查询如下:

select * from users where name='abcdefgji'' and password=' or 1=1-- 

T-SQL和其他已经提到的高级SQL注入技术。 Advanced SQL Injection In SQL Server Applications是一篇很棒的论文,如果你还没有,你应该阅读它。

最后一个问题是unicode攻击。出现这类漏洞是因为转义函数不知道多字节编码,这可能是used by an attacker to "consume" the escape character。在字符串前加上“N”将无济于事,因为这不会影响字符串中稍后的多字节字符的值。但是,这种类型的攻击非常罕见,因为必须将数据库配置为接受GBK unicode字符串(我不确定MS-SQL是否可以执行此操作)。

仍然可以进行二阶代码注入,这种攻击模式是通过信任攻击者控制的数据源创建的。转义用于将控制字符表示为其字符文字。如果开发人员忘记转义从select获取的值,然后在另一个查询中使用此值,则 bam 攻击者将拥有一个字符文字单引号。

测试一切,不要信任。

答案 1 :(得分:17)

通过一些额外的规定,您的上述方法不易受SQL注入攻击。要考虑的主要攻击媒介是SQL走私。当类似的unicode字符以意想不到的方式翻译时(例如`更改为'),就会发生SQL走私。有几个位置,应用程序堆栈可能容易受到SQL走私的攻击。

  • 编程语言是否正确处理unicode字符串?如果语言不能识别unicode,它可能会将unicode字符中的字节错误识别为单引号并将其转义。

  • 客户端数据库库(例如ODBC等)是否正确处理unicode字符串? .Net框架中的System.Data.SqlClient可以,但是如何从Windows 95中处理旧库时代?第三方ODBC库实际上确实存在。如果ODBC驱动程序不支持查询字符串中的unicode会发生什么?

  • 数据库是否正确处理输入?假设您正在使用N',现代版本的SQL是免疫的,但是SQL 6.5呢? SQL 7.0?我不知道有任何特殊的漏洞,但这对于1990年代的开发人员来说并不是很明显。

  • 缓冲区溢出?另一个问题是引用的字符串比原始字符串长。在哪个版本的Sql Server中引入了2GB的输入限制?在那之前是什么限制?在旧版本的SQL上,当查询超出限制时会发生什么?从网络库的角度来看,查询的长度是否存在任何限制?或者编程语言中字符串的长度?

  • 是否有任何语言设置会影响Replace()函数中使用的比较? .Net始终对Replace()函数进行二进制比较。总是这样吗?如果.NET的未来版本支持在app.config级别覆盖该行为,会发生什么?如果我们使用regexp而不是Replace()来插入单引号怎么办?计算机的区域设置是否会影响此比较?如果确实发生了行为上的改变,它可能不容易受到sql注入的影响,但是,它可能无意中编辑了字符串,方法是在它到达数据库之前将看起来像单引号的单代码字符更改为单引号。

因此,假设您在当前版本的.Net上使用C#中的System.String.Replace()函数,并在当前(2005-2012)版本的SQL Server上使用内置的SqlClient库,那么方法不容易受到攻击。当你开始改变事物时,就不会有任何承诺。参数化查询方法是提高效率,性能和(在某些情况下)安全性的正确方法。

警告以上评论并非对此技术的认可。还有其他几个很好的理由说明为什么这种生成SQL的错误方法。但是,详细说明这些问题超出了这个问题的范围。

请勿将此技术用于新发展。

请勿将此技术用于新发展。

请勿将此技术用于新发展。

答案 2 :(得分:9)

Using query parameters比转义引号更好,更简单,更快捷。


重新评论,我看到你承认参数化,但值得强调。为什么要在参数化时使用转义?

Advanced SQL Injection In SQL Server Applications中,在文本中搜索“替换”一词,并从此处读取一些示例,其中开发人员即使在转义用户输入后也无意中允许SQL注入攻击。


有一个边缘情况,使用\转义引号会导致漏洞,因为\会成为某些字符集中有效多字节字符的一半。但这不适用于您的情况,因为\不是逃避角色。

正如其他人所指出的那样,您可能还会在SQL中添加动态内容,而不是字符串文字或日期文字。表或列标识符在SQL中由"分隔,或在Microsoft / Sybase中由[ ]分隔。 SQL关键字当然没有任何分隔符。对于这些情况,我建议白名单要插值的值。

底线是逃避的有效防御,如果你可以确保你一贯地做到这一点。这就是风险:应用程序中的一个开发人员团队可能会省略一个步骤,并且不安全地进行字符串插值。

当然,其他方法也是如此,例如参数化。只有持续不断地执行它们才有效。但我发现使用参数更容易,而不是找出正确的转义类型。开发人员更有可能使用方便且不会减慢速度的方法。

答案 3 :(得分:4)

如果用户提供的输入被解释为命令,则会发生SQL注入。这里的命令意味着任何未被解释为可识别的data type文字的内容。

现在,如果您仅在数据文字中使用用户输入,特别是仅在字符串文字中使用,则用户输入只会被解释为与字符串数据不同的内容,如果它能够保留字符串文字上下文。对于字符串或Unicode字符串文字,它是包含文字数据的单引号,而嵌入的单引号需要用两个单引号表示。

因此,为了保留字符串文字上下文,需要提供单个引号(sic),因为两个单引号被解释为字符串文字数据而不是字符串文字结束分隔符。

因此,如果您用两个单引号替换用户提供的数据中的任何单引号,则用户将无法保留字符串文字上下文。

答案 4 :(得分:3)

SQL注入可以通过unicode进行。如果网络应用有这样的网址:

http://mywebapp/widgets/?Code=ABC

生成SQL之类的 从小部件中选择*,其中Code ='ABC'

但是黑客进入了这个:

http://mywebapp/widgets/?Code=ABC%CA%BC;drop表格小部件 -

SQL看起来像 select * from Widgets,Code ='ABC'; drop table widgets - '

并且SQL Server将运行两个SQL语句。一个做选择,一个做下降。 您的代码可能会将url编码的%CA%BC转换为unicode U02BC,这是一个“修饰符字母撇号”。 .Net中的替换功能不会将其视为单引号。但是,Microsoft SQL Server将其视为单引号。这是一个可能允许SQL注入的示例:

string badValue = ((char)0x02BC).ToString();
badValue = badValue + ";delete from widgets--";
string sql = "SELECT * FROM WIDGETS WHERE ID=" + badValue.Replace("'","''");
TestTheSQL(sql);

答案 5 :(得分:2)

如果你正在进行字符串连接,可能没有100%安全的方法。你可以做的是尝试检查每个参数的数据类型,如果所有参数都通过了这样的验证,那么继续执行。例如,如果你的参数应该是int类型,并且你得到了一些无法转换为int的东西,那么就拒绝它。

如果你接受nvarchar参数,这不起作用。

正如其他人已经指出的那样。最安全的方法是使用参数化查询。