如何通过SQL Server中的SQL注入来克服单引号引起的卫生问题?


71

首先,我很清楚参数化查询是最好的选择,但是我在问什么使下面介绍的策略易受攻击。人们坚持认为以下解决方案无效,因此我在寻找一个示例说明为何不可行。

如果动态SQL在发送到SQL Server之前使用以下转义代码内置在代码中,则哪种注入方式可以克服这种情况?

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

这里回答类似的问题,但我认为任何答案都不适用于这里。

在SQL Server中,不能用“ \”转义单引号。

我相信使用Unicode的SQL走私在此处概述)会因生成的字符串被单引号前面的N标记为Unicode而受到阻碍。据我所知,SQL Server不会自动将其他字符集转换为单引号。没有单引号的转义,我认为注入是不可能的。

我也不认为String Truncation是可行的方法。根据Microsoft的说法, SQL Server肯定不会进行截断,因为一个的最大大小为nvarchar2GB 。2 GB的字符串在大多数情况下是不可行的,在我的情况下是不可能的。

二阶注入是可能的,但在下列情况下是可能的:

  1. 使用上述方法对进入数据库的所有数据进行清理
  2. 数据库中的值永远不会附加到动态SQL中(为什么只要可以在任何动态SQL字符串的静态部分中引用表值,您仍然会这样做吗?)。

我并不是说这比使用参数化查询更好或替代,但是我想知道我概述的内容是多么容易受到攻击。有任何想法吗?


1
否。您仍然容易受到以下形式的攻击:"SELECT * FROM MyTable WHERE Field = " + userInputwhen userInputis 0; DROP TABLE OhNo;
Yuck

10
这是没有道理的。在上面的示例中,您的用户输入将被清除为N'0;拖放表哦,不;' 在执行之前
GBleaney 2013年

3
这仅用于清理字符串变量。如果将诸如“ int”之类的东西在添加到查询之前将其强制转换为int,则无需清理。无论如何,我现在只是在询问如何清洁琴弦。另外,这里也没有礼貌。如果您能想到一种不安全的方法,我很想知道。
GBleaney

好的,userinput =“ test”;删除表ohno;打印'“
Kenneth Fisher

3
当无法避免动态SQL时,这不是一个好的选择吗?
GBleaney 2013年

Answers:


46

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

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注入技术。《 SQL Server应用程序中的高级SQL注入》是一本不错的文章,如果尚未阅读,则应该阅读。

最后一个问题是unicode攻击。之所以出现此类漏洞,是因为转义功能不了解多字节编码,攻击者可以使用它来“消耗”转义字符。在字符串前添加“ N”将无济于事,因为这不会影响字符串后面的多字节字符的值。但是,这种攻击非常罕见,因为必须将数据库配置为接受GBK unicode字符串(而且我不确定MS-SQL可以做到这一点)。

二阶代码注入仍然是可能的,此攻击模式是通过信任攻击者控制的数据源创建的。转义用于将控制字符表示为字符文字。如果开发人员忘记逃脱从获得的值select,然后使用在另一个查询此值,则BAM攻击者将不得不在他们的处置字符单引号。

测试一切,什么都不信任。


3
尽管我同意使用安全性而不是参数化查询是不好的,但是如果您仔细阅读他的问题,他会指出他将采取的“确保”安全性的几个步骤。1)他确保数值实际上是一个数值,因此在您的示例中进行id注入将失败2)他询问有关仅接受'作为字符串定界符的SQL Server 。使用勾号或双引号不会对攻击者有所帮助
NikolaRadosavljević13年

@NikolaRadosavljević是的,但是根据他植入这张支票的方式,抽签和缺乏引号仍然是一个问题。显然,该帖子是从攻击者而非防御者的角度出发的。辩护是众所周知的,没有兴趣。
菜鸟

3
问题中的代码不容易受到攻击。作为转义字符串文字的一般指南,这是一个很好的答案,既正确又有趣。但是列出的方案将无法破坏原始问题中的代码行。对于第二手攻击,无论文本最初是使用参数插入还是包含引号字符,都可能发生。
罗布

18

还有一些其他规定,您的上述方法不容易受到SQL注入的攻击。要考虑的主要攻击媒介是SQL Smuggling。当类似的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级别覆盖该行为,会发生什么?如果我们使用正则表达式而不是Replace()插入单引号怎么办?计算机的区域设置是否会影响此比较?如果确实发生了行为更改,则它可能不会受到sql注入的攻击,但是,在到达数据库之前,它可能无意间通过将看起来像单引号的Unicode字符更改为单引号来编辑了字符串。

因此,假设您在当前版本的.Net上使用C#的System.String.Replace()函数,并针对当前(2005-2012)版本的SQL Server使用内置的SqlClient库。脆弱的。当您开始改变事物时,就无法做出任何承诺。参数化查询方法是提高效率,提高性能以及(在某些情况下)提高安全性的正确方法。

警告上面的评论不是对这种技术的认可。还有其他一些很好的理由说明这种错误的生成SQL方法的原因。但是,详细说明它们超出了此问题的范围。

请勿使用此技术进行新开发。

请勿使用此技术进行新开发。

请勿使用此技术进行新开发。


如果不是这种技术,那么CREATE LOGIN在新开发中应该使用什么?
binki

9

与转义引号相比,使用查询参数更好,更轻松,更快捷。


关于您的评论,我看到您承认参数化,但是值得重点介绍。为什么可以在参数化时使用转义?

在“ SQL Server应用程序的高级SQL注入”中,在文本中搜索“替换”一词,然后阅读一些示例,其中即使在逃避用户输入后,开发人员也会无意中允许SQL注入攻击。


在极端情况下,用引号转义会\导致漏洞,因为\在某些字符集中,变成有效的多字节字符的一半。但这不适用于您的情况,因为\它不是转义字符。

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

底线是,逃避一种有效的防御措施,如果您可以确保始终如一。这就是风险:您的应用程序上的一组开发人员可能会省略一个步骤,并且不安全地进行一些字符串插值。

当然,其他方法(例如参数化)也是如此。只有您始终如一地进行操作,它们才有效。但是我发现使用参数比找出正确的转义类型更容易,更快捷。开发人员更有可能使用一种方便且不会降低速度的方法。


3
正如我在帖子的第一行所说的那样,我对此很清楚。我只是问是否有某些特定原因导致我概述的内容不安全。
GBleaney

1
这是一个很好的资源,但是其中建议的可能问题在这里并不是真正的问题。首先,使用“ char(0x63)”方法构造引号是没有用的,因为该语句将被解释为字符串并且不会被执行。至于二阶SQL注入,我已经在原始帖子中解决了。
GBleaney

1
“为什么可以在参数化时使用转义?” 有时,遗留代码很难为参数化查询进行改造。
RJ Dunnill '19

@RJDunnill,根据我的经验,即使对于旧代码,修改代码也不是难事。在上一份工作中,我更新了整个应用程序,以在大约4个小时内使用参数化SQL查询。更大的困难是当没人知道如何访问代码,构建代码或部署代码时。但这不是这种情况下的问题。我们假设问题是关于一个项目的代码已经在进行积极的开发,而不是丢失的代码。
比尔·卡温

4

如果用户提供的输入被解释为命令,则会发生SQL注入。在此,命令表示未解释为可识别的数据类型文字的任何内容。

现在,如果仅在数据文字中使用用户输入,特别是仅在字符串文字中使用用户输入,则只有在能够离开字符串文字上下文的情况下,才将用户输入解释为与字符串数据不同的内容。对于字符串或Unicode字符串文字,是用单引号将文字数据括起来,而嵌入的单引号则需要用两个单引号表示。

因此,要离开字符串文字上下文,就需要提供一个单引号(sic),因为两个单引号被解释为字符串文字数据,而不是字符串文字结尾定界符。

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


4

SQL注入可以通过unicode进行。如果网络应用程序具有如下网址:

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

从代码='ABC'的小部件中生成类似select *的SQL

但是黑客输入了以下内容:

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

SQL看起来就像select * from Code ='ABC'; drop table widgets--'的小部件

和SQL Server将运行两个SQL语句。一来进行选择,一来进行放置。您的代码可能会将经过网址编码的%CA%BC转换为Unicode U02BC,后者是“修饰符撇号”。.Net中的Replace函数不会将其视为单引号。但是,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);

这将起作用:字符串sql =“ SELECT * FROM WIDGETS WHERE ID ='” + badValue.Replace(“'”,“”“)+”'“;
linjunhalida

2

如果要进行字符串连接,可能没有100%安全的方法。您可以做的是尝试检查每个参数的数据类型,如果所有参数都通过了这样的验证,然后继续执行。例如,如果您的参数应为int类型,而您得到的内容无法转换为int,则只需拒绝它即可。

但是,如果您接受nvarchar参数,则此方法不起作用。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.