mysql_escape_string漏洞

时间:2012-10-23 08:37:39

标签: php mysql sql-parametrized-query

我最近向我的朋友解释了参数化及其优点,并且他在安全方面询问它比mysqli_escape_string更好。具体来说,您是否可以想到尽管输入字符串被转义(使用mysqli_escape_string),但SQL注入的任何示例都会成功?

更新:

我为在原始问题中不够清楚而道歉。这里要问的一般问题是,尽管转换输入字符串,SQL注入是否可行?

2 个答案:

答案 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