78

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

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

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

在这里回答了一个类似的问题,但我不相信任何答案都适用于此。

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

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

我也不相信字符串截断是一个可行的向量。根据微软的说法, SQL Server 肯定不会进行截断,因为 an 的最大大小为nvarchar2GB 。2 GB 的字符串在大多数情况下是不可行的,在我的情况下也是不可能的。

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

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

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

4

6 回答 6

48

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

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 是否可以做到这一点)。

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

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

于 2013-03-24T07:52:01.467 回答
19

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

  • 编程语言是否正确处理 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 注入的攻击,但是,它可能在到达数据库之前通过将看起来像单引号的 uni-code 字符更改为单引号来无意中编辑了字符串。

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

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

不要将此技术用于新的开发。

不要将此技术用于新的开发。

不要将此技术用于新的开发。

于 2013-03-29T19:18:47.213 回答
8

使用查询参数比转义引号更好、更容易、更快。


关于您的评论,我看到您承认参数化,但值得强调。当您可以参数化时,为什么要使用转义?

Advanced SQL Injection In SQL Server Applications中,在文本中搜索单词“replace”,然后阅读一些示例,即使在转义用户输入后,开发人员也会无意中允许 SQL 注入攻击。


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

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

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

当然,其他方法也是如此,比如参数化。它们只有在您始终如一地执行时才有效。但是我发现使用参数比找出正确的转义类型更容易和更快。开发人员更有可能使用方便且不会减慢速度的方法。

于 2013-03-21T00:50:55.560 回答
4

如果用户提供的输入被解释为命令,就会发生 SQL 注入。这里的命令表示任何不被解释为可识别的数据类型文字的东西。

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

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

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

于 2013-03-21T06:27:00.827 回答
4

SQL 注入可以通过 unicode 发生。如果 Web 应用有这样的 URL:

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

生成 SQL,如 select * from widgets where Code = 'ABC'

但是黑客输入了这个:

http://mywebapp/widgets/?Code=ABC%CA%BC;drop table widgets--

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

SQL Server 将运行两条 SQL 语句。一个做选择,一个做删除。您的代码可能会将 url 编码的 %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);
于 2015-07-30T14:24:37.337 回答
1

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

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

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

于 2013-03-21T10:14:47.003 回答