哪些字符实际上能够导致 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 注入?的主要内容,如果未能解决你的问题,请参考以下文章