我最近向我的朋友解释了参数化及其优点,并且他在安全方面询问它比mysqli_escape_string
更好。具体来说,您是否可以想到尽管输入字符串被转义(使用mysqli_escape_string),但SQL注入的任何示例都会成功?
更新:
我为在原始问题中不够清楚而道歉。这里要问的一般问题是,尽管转换输入字符串,SQL注入是否可行?
答案 0 :(得分:4)
具体来说,你能想到SQL注入的任何例子吗? 尽管输入字符串被转义(使用 函数mysql_escape_string)?
不可靠。您指的是mysql_escape_string()
,它没有考虑连接编码(mysql_real_escape_string()
会这样做。)
所以也许是一个精心设计的字符串,前面有一个精心设计的不完整的UTF8代码点,可能会导致一个引用符号被mysql_escape_string()
转义但是逃脱本身被MySQL 忽略,因为它会“看到”它作为UTF8字符。
E.g:
0xC2' OR 1=1 ;--
将被mysql_escape_string()
转义为
0xC2\' OR 1=1 ;--
将组装到
WHERE password='0xC2\' OR 1=1 ;--';
并且由MySQL看到(如果正确的连接编码生效),例如,
WHERE password='€' OR 1=1 ;[--';] <-- the bracketed part is considered a comment and ignored
这将是一个经典的SQL注入。
但这取决于你指定了一个双重弃用的功能,可能是通过分心。如果你真的指的是mysql_real_escape_string()
,那么它就行不通了。
此外,这假设服务器和应用程序层(例如PHP)在填充输入时都不使用任何类型的字符集验证。如果他们这样做,则无效的UTF8将在到达时被删除,甚至永远不会被mysql_escape_string
看到,这当然就足够了。
根本不要使用mysql_escape_string
(或mysql_whatever
)。它们已被弃用,您的代码可能会停止工作。改为使用PDO功能。
答案 1 :(得分:3)
问题已经过编辑(在我的回答发布后)专门定位mysqli_escape_string
,这是mysql_real_escape_string
的别名,因此会考虑连接编码。这使得原始答案不再适用,但我已将其保留为完整性。
新的答案,简而言之: mysqli_escape_string
与参数化查询一样安全性好,前提是你不要用脚射击。
具体而言,PHP doc page上的巨型警告中突出显示了您不能做的事情:
字符集必须在服务器级别设置,或者使用 API函数
mysqli_set_charset()
让它影响mysqli_real_escape_string()
。
如果您不注意此警告(即,如果您使用直接SET NAMES
查询更改字符集)和,则将字符集从单字节编码更改为“方便”(从攻击者的角度来看)多字节编码,您实际上将模仿哑巴mysql_escape_string
的作用:尝试在不知道输入所在的编码的情况下转义字符。
这种情况使您可能容易受到SQL注入攻击,如下面的原始答案所述。
重要说明:我记得在某个地方读过最近的MySql版本已经在他们的端部插入了这个漏洞(在客户端库中?),这意味着你可能完美即使使用SET NAMES
切换到易受攻击的多字节编码也是安全的。但请不要相信我的话。
与mysql_real_escape_string
相比,裸mysql_escape_string
没有考虑connection encoding。这意味着它假设输入采用单字节编码,实际上它可以合法地采用多字节编码。
某些多字节编码的字节序列对应于单个字符,其中一个字节是单引号的ASCII值(0x27
);如果输入这样的字符串,mysql_escape_string
将很乐意“逃避引用”,这意味着用0x27
0x5c
替换0x27
。根据编码规则,这可能会导致多字节字符变为包含0x5c
的另一个字符,并将“剩余”0x27
作为输入中的独立单引号。 Voilà,你已经在SQL中注入了一个未转义的引用。
有关详细信息,请参阅this blog post。