32

我们都知道我们应该使用准备好的语句或适当的替换/格式化规则来防止我们的应用程序中的 sql 注入。

但是,在查看 MySQL 的字符文字列表时,我注意到它包含以下字符:

  • \0 一个 ASCII NUL ( 0x00) 字符。
  • \' 单引号 ( ') 字符。
  • \" 双引号 ( ") 字符。
  • \b 退格字符。
  • \n 换行符(换行符)。
  • \r 一个回车符。
  • \t 制表符。
  • \Z ASCII 26 ( Ctrl+ Z)。见表格后面的注释。
  • \\ 反斜杠 ( \) 字符。
  • \% 一个%字符。
  • \_ 一个_字符。

现在,%and_字符需要转义以防止将不需要的通配符注入 LIKE 语句,而'(单引号)、\(反斜杠)和"(双引号)都需要转义以防止注入任意 SQL - 这些其他字符中的任何一个未转义是否会直接导致 SQL 注入漏洞,否则该漏洞不会出现?有没有人有这种利用的现实世界的例子?

假设我们正在构建我们的查询,例如:

SELECT * FROM users WHERE username='$user'

$user对于唯一未转义的字符文字是\b(退格)、\0(NUL)、\n(换行)、\r(换行)、\t(制表符)或\ZCtrl+ ),是否有任何值Z允许将任意 SQL 注入此查询?

4

2 回答 2

8

2020年的强制性附录:

处理角色被证明是低效且过时的

您必须使用准备好的语句,忘记转义、“危险人物”或任何此类事务。

由于以下原始答案中提供的原因,使用参数化查询被认为是防止 SQL 注入的唯一正确方法:

哪些字符实际上能够导致mysql中的SQL注入

没有这样的字符。

导致 SQL 注入的不是“字符”。但格式不正确。任何角色,视情况而定,可能是“危险的”或绝对无害的。将保护限制在某个子集是一种危险的错觉,实际上迟早会导致 SQL 注入。

您的问题中有两个错误的陈述导致您感到困惑:

  1. 我们都知道我们应该使用......适当的替换规则,以防止我们的应用程序中的sql注入。

这种说法是错误的。不是替换而是格式化。区别是必不可少的。单独替换并不能防止注入,而格式化可以。请注意,查询的每个不同部分都需要不同的格式,这对任何其他部分都是无用的。比如说,还有另一个字符,对于注入保护至关重要 - 反引号 (`)。但是您没有列出它,因为它与字符串文字无关。

  1. '(单引号)、\(反斜杠)和 "(双引号)都需要转义以防止注入

这是一个严重错误的说法。逃逸并不妨碍注射。这些字符需要转义才能格式化字符串,并且与注入完全无关。虽然正确格式化的查询部分确实是无懈可击的。但事实是 - 你必须格式化动态查询部分只是为了它,遵循语法规则,而不是因为任何注入。而且您的查询会像副作用一样难以理解。

现在你可以看到为什么你的最后一句话,

为什么所有这些其他字符都容易被 mysql_real_escape_string 转义,因为这对我来说并不是很明显。

错误地提出:
字符串格式规则需要这些字符,而不是任何“漏洞”。其中一些只是为了方便而被转义,一些是为了可读性,一些是为了转义分隔符的明显原因。就这样。

要回答评论中的最新问题:

我真的想要一个答案,因为 PHP 的 mysql_real_escape_string 也没有引用这些文字。

再说一遍:尽管在普通 PHP 用户的心目中,mysql_real_escape_string()无论什么吓人的注入都与此密切相关,但实际上并非如此。没有“危险”角色。一个都没有。有一些具有特殊含义的服务字符。在某些情况下,它们必须被转义,这取决于上下文。

因此,此函数转义的字符与任何“危险”之间没有任何联系。当您开始认为mysql_real_escape_string()' 的目的是逃避“危险”角色时,您确实将自己置于危险之中。虽然只要您使用此函数只是为了转义字符串(并且无条件地这样做) - 您可能会认为自己是安全的(当然,如果您也不要忘记格式化所有其他文字,使用它们各自的格式化规则)

我想知道“%”字符是否会导致 LIKE 子句中的额外结果。

不。

于 2013-01-17T05:37:14.467 回答
8

考虑mysql_real_escape_string()手册中的以下几行:

MySQL 只要求对查询中用于引用字符串的反斜杠和引号字符进行转义。mysql_real_escape_string()引用其他字符以使它们更容易在日志文件中阅读。

MySQL 中的 SQL 注入不应该单独使用这些特殊字符:\b \0 \n \r \t \Z

然而,字符串文字手册指出以下但指定(或不指定)的原因与 SQL 注入无关:

如果要将二进制数据插入字符串列(例如BLOB列),则应使用转义序列表示某些字符。反斜杠 (“\”) 和用于引用字符串的引号字符必须进行转义。在某些客户端环境中,可能还需要转义 NUL 或 Control+Z。如果未转义, mysql客户端会截断包含 NUL 字符的引用字符串,如果未转义,则可能会在 Windows 上将 Control+Z 用于 END-OF-FILE。

此外,在一个简单的测试中,无论天气如何,上面列出的特殊字符是否被转义,MySQL 都产生了相同的结果。换句话说,MySQL 甚至不介意:

$query_sql = "SELECT * FROM `user` WHERE user = '$user'";

上面的查询对于上面列出的字符的非转义和转义版本的工作类似,如下所示:

$user = chr(8);     // Back Space
$user = chr(0);     // Null char
$user = chr(13);    // Carriage Return
$user = chr(9);     // Horizontal Tab
$user = chr(26);    // Substitute
$user = chr(92) .chr(8);    // Escaped Back Space
$user = chr(92) .chr(0);    // Escaped Null char
$user = chr(92) .chr(13);   // Escaped Carriage Return
$user = chr(92) .chr(9);    // Escaped Horizontal Tab
$user = chr(92) .chr(26);   // Escaped Substitute

简单测试中使用的测试表和数据:

-- Table Structure

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user` varchar(10) CHARACTER SET utf8 NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

-- Table Data

INSERT INTO `user` ( `user` ) VALUES
( char( '8' ) ),
( char( '0' ) ),
( char( '10' ) ),
( char( '13' ) ),
( char( '9' ) ),
( char( '26' ) );
于 2013-10-26T12:38:28.493 回答