sch*_*tyl 23 mysql security sql-injection escaping
我们都知道我们应该使用预准备语句或适当的替换/格式化规则,以防止在我们的应用程序中进行sql注入.
但是,当看一下MySQL的字符文字列表时,我注意到它包含以下字符:
\0 ASCII NUL(0x00)字符.\' 单引号(')字符.\" 双引号(")字符.\b 退格字符.\n 换行符(换行符).\r 回车符.\t 制表符.\Z ASCII 26(Ctrl+ Z).请参阅下表后面的注释.\\ 反斜杠(\)字符.\% 一个%角色.\_ 一个_角色.现在,虽然%和_字符需要进行转义以防止将不需要的通配符注入到LIKE语句中,并且'(单引号),\(反斜杠)和"(双引号)都需要进行转义以防止注入任意SQL - 未转义的任何其他字符可能直接导致SQL注入漏洞,否则将无法存在?有没有人有这样一个漏洞的真实世界的例子?
让我们假设我们正在构建我们的查询,如:
SELECT * FROM users WHERE username='$user'
Run Code Online (Sandbox Code Playgroud)
$user唯一未转义的字符文字\b(退格),\0(NUL),\n(换行符),\r(换行符),\t(制表符)或\Z(Ctrl+ Z)允许将任意SQL注入此查询的位置是否有任何值?
考虑mysql_real_escape_string()手册中的以下几行:
MySQL仅要求转义反斜杠和用于对查询中的字符串加引号的引号字符。mysql_real_escape_string()引用其他字符以使其更易于在日志文件中读取。
仅凭这些特殊字符本身不可能在MySQL中进行SQL注入:\b \0 \n \r \t \Z。
但是String Literals手册指出以下内容,但指定的原因(是否指定)与SQL注入无关:
如果要将二进制数据插入字符串列(例如BLOB列),则应使用转义序列表示某些字符。反斜杠(“ \”)和用于引号字符串的引号字符必须转义。在某些客户端环境中,可能还需要转义NUL或Control + Z。在MySQL的客户端截断援引包含NULL字符的字符串,如果他们没能逃脱,并控制+ Z,可以采取档案结尾在Windows上,如果没有逃脱。
此外,在一个简单的测试中,无论天气如何,上面列出的特殊字符都已转义或不转义,MySQL产生了相同的结果。换句话说,MySQL甚至都不介意:
$query_sql = "SELECT * FROM `user` WHERE user = '$user'";
Run Code Online (Sandbox Code Playgroud)
上面的查询对于上面列出的那些字符的非转义和转义版本具有类似的作用,如下所示:
$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
Run Code Online (Sandbox Code Playgroud)
测试表和简单测试中使用的数据:
-- 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' ) );
Run Code Online (Sandbox Code Playgroud)
您的问题中有两个错误的陈述使您感到困惑:
- 众所周知,我们应该使用...适当的替换规则,以防止在应用程序中进行sql注入。
这是错误的陈述。不是替换而是格式化。这是必不可少的。替换不能防止注入,而格式化可以防止注入。请注意,查询的每个不同部分都需要不同的格式,这对于其他任何部分都没有用。说,还有另一个对于注入保护至关重要的角色-反引号(`)。但是您没有列出它,因为它与字符串文字无关。
- 为了避免注入,'(单引号),\(反斜杠)和“(双引号)都需要转义
这是完全错误的说法。逃逸不会阻止注射!这些字符需要转义以格式化字符串,并且与注入绝对无关。确实,正确格式化的查询部分对于注入而言是无懈可击的。但是事实是-您仅出于此目的就必须对动态查询部分进行格式化,以遵循语法规则-而不是因为进行任何注入。结果,您的查询将不可穿透。
现在,您可以了解为什么您的最后一句话,
为什么所有其他这些字符都足够脆弱,可以通过mysql_real_escape_string进行转义,因为这对我来说并不立即显而易见。
错误地指出:
字符串格式设置规则要求这些字符,而不是任何“漏洞”。为了方便起见,对其中一些进行了转义,为便于阅读而对其中的一些进行了转义,其中一些出于明显的原因转义了定界符。就这样。
要回答评论中的最新问题:
我真的想要一个答案,因为PHP的mysql_real_escape_string也不引用这些文字。
再说一次:尽管在一般的PHP用户心目中,mysql_real_escape_string()任何令人恐惧的注入都与之紧密相连,但实际上并非如此。从来没有“危险”字符。没有一个。有些服务字符具有特殊含义。在某些情况下,必须视情况而定,将其转义。
因此,此功能转义的字符与任何“危险”之间没有任何联系。当您开始以为自己mysql_real_escape_string()的目的是逃避“危险的”角色时,您的确陷入危险之中。只要您仅使用此函数来转义字符串(并无条件地执行此操作),您就可以认为自己很安全(当然,如果您也不要忘记使用它们各自的格式化规则来格式化所有其他文字)
我想知道“%”字符是否会导致超过LIKE子句中的额外结果。
没有。