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

Posted

技术标签:

【中文标题】哪些字符实际上能够导致 MySQL 中的 SQL 注入?【英文标题】:Which characters are actually capable of causing SQL injection in MySQL? 【发布时间】:2012-12-31 11:53:45 【问题描述】:

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

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

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

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

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

SELECT * FROM users WHERE username='$user'

$user 是否有任何值,其中唯一未转义的字符文字是 \b(退格)、\0(NUL)、\n(换行)、\r(换行)、@ 987654349@ (tab) 或 \Z (Ctrl+Z) 允许将任意 SQL 注入此查询?

【问题讨论】:

我想知道“%”字符是否会导致 LIKE 子句中的额外结果。 据我所知,对您的问题的简短回答是,不——这样的字符(即使未转义)不会终止字符串文字上下文并将服务器置于 SQL 上下文中;因此不会出现 SQL 注入。但是,您应该小心,您的转义方法知道服务器用来解码字符串文字的接收字节的字符集:如果一个人的转义是在不同的字符集中进行的,那么精心设计的字符串可能会终止字符串文字并注入任意 SQL。 以 % 开始点赞会将查询计划更改为使用全表扫描,这通常对性能不利。因此,如果处理了其他所有内容,仍然可以用来攻击系统。根据您使用的接口,注入一个 ;可能允许您运行第二个查询。限制运行查询的用户的权限是一个很好的策略。 mysql 中的行注释字符(如 -- )也会导致问题。例如:更新用户设置 access_time = x 其中 id = y;如果 x 是 -9999 -- 这可能用于更新所有用户 access_time。 我没有看到列出的评论字符。这是 SQL 注入攻击中列出的常见攻击之一。 【参考方案1】:

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

MySQL 只要求转义反斜杠和用于在查询中引用字符串的引号字符。 mysql_real_escape_string() 引用其他字符,以便在日志文件中阅读。

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

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

如果要将二进制数据插入字符串列(例如BLOB 列),则应使用转义序列表示某些字符。必须对反斜杠 (“\”) 和用于引用字符串的引号字符进行转义。在某些客户端环境中,可能还需要转义 NUL 或 Control+Z。 mysql 客户端会截断包含 NUL 字符的引用字符串,如果它们没有转义,如果没有转义,Control+Z 可能会被用于 Windows 上的 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' ) );

【讨论】:

【参考方案2】:

2020 年的强制性附录:

处理字符被证明是低效且过时的

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

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

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

没有这样的字符。

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

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

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

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

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

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

现在你可以看到为什么你最后的陈述了,

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

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

回答 cmets 最近提出的问题:

我真的很想回答这个问题,因为 php 的 mysql_real_escape_string 也不引用这些文字。

再次重申:虽然在普通 PHP 用户 mysql_real_escape_string() 的心目中,无论什么吓人的注入都密切相关,实际上并没有。没有“危险”字符。一个都没有。有一些具有特殊含义的服务字符。在某些情况下,它们必须被转义,具体取决于上下文。

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

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

没有。

【讨论】:

虽然我非常感谢您抽出时间来回答这个问题,而且很明显,如果您不转义这些字符,您可能会将格式错误的字符串插入到 sql 查询中,但您认为我的误解是我的措辞的神器,而不是实际的误解。我稍微改写了这个问题,并在原始帖子中加粗了实际问题。我承认我们格式化字符串的原因不是为了防止注入,而是为了在数据库中正确表示数据,但是我只关心这个问题的安全方面。

以上是关于哪些字符实际上能够导致 MySQL 中的 SQL 注入?的主要内容,如果未能解决你的问题,请参考以下文章

20141230 mysql数值类型和列属性一

python 字符串组成MySql 命令时,字符串含有单引号或者双引号导致出错解决办法

Mysql like语句转义字符

MySQL 中的一些坑

php mysql转义特殊字符的函数都有哪些

MySQL在实际应用中的规范(表定义SQL索引等)