mysql_real_escape_string存在哪些缺陷?

12

我在这里看到一些人说,使用 mysql_real_escape_string 连接查询语句不能完全保护你免受 SQL 注入攻击。

然而,我还没有见过一个示例能说明有哪种输入会绕过 mysql_real_escape_string 的保护。大多数示例都忘记了 mysql_query 仅限于一个查询,并且错误地使用了 mysql_real_escape_string

我唯一能想到的示例是以下内容:

mysql_query('DELETE FROM users WHERE user_id = '.mysql_real_escape_string($input));

这将无法保护您免受以下输入的影响:

5 OR 1=1

我认为这是对mysql_real_escape_string使用不当,而不是其缺陷,因为它是为字符串设计的,而不是数字值。你应该将其转换为数字类型,或者如果你打算将输入作为字符串进行清理,则在查询中也应该这样做,并在其周围包裹引号。

有人能提供一个绕过mysql_real_escape_string的示例吗?这个示例不能依赖于错误处理数字值或忽略mysql_query只能执行一条查询的情况。

编辑:我对mysql_real_escape_string的限制感兴趣,而不是将其与替代方案进行比较。我意识到新项目有更好的选择,但并不争议这一点。


1
这个问题已经过时了。既然有更好的替代方案,为什么还要寻找mysql_*的解决方案呢? - Mihai Iorga
Mihai,我并不否认有更好的替代方案,比如PDO,但这并不是这个问题的重点。 - Mitch Satchwell
我在这里看到过一些人说...你试过问那些人吗?不错的问题。 - NullUserException
我猜他们不知道一个真实的例子,而我也不想用这个讨论来污染其他问题。与其在评论中询问一个人,我宁愿用自己的问题来向整个社区提问。 - Mitch Satchwell
2
这个函数没有转义百分号和下划线(%_),它们在 LIKE 中是通配符。这算不算缺陷? - NullUserException
显示剩余3条评论
2个回答

6
mysql_real_escape_string的主要缺点,或者说mysql扩展的一般性缺点,是它比其他更现代化的APIs(尤其是预处理语句)更难正确应用。mysql_real_escape_string应该只在一个情况下使用:对在SQL语句中作为值被引用的文本内容进行转义。例如:
$value = mysql_real_escape_string($value, $link);
$sql = "... `foo` = '$value' ...";
                     ^^^^^^

mysql_real_escape_string确保以上环境中的$value不会破坏SQL语法。但它在此处的工作方式与您想象的不同:

$sql = "... `foo` = $value ...";

或者在这里:

$sql = "... `$value` ...";

or here:

$sql = mysql_real_escape_string("... `foo` = '$value' ...");

如果应用于SQL语句中除引号字符串以外的任何其他上下文环境中的值,则会错误应用该函数,可能会破坏语法,并/或允许某人提交可能启用SQL注入攻击的值。使用mysql_real_escape_string的用例非常有限,但很少被正确理解。
另一种导致使用mysql_real_escape_string出现问题的方法是使用错误的方法设置数据库连接编码。你应该这样做:
mysql_set_charset('utf8', $link);

您也可以这样做:

mysql_query("SET NAMES 'utf8'", $link);

问题在于后者绕过了mysql_ API,但API仍然认为您正在使用latin1(或其他字符集)与数据库通信。现在使用mysql_real_escape_string时,它会假定错误的字符编码并以不同于数据库稍后解释的方式转义字符串。通过运行SET NAMES查询,您已经在mysql_客户端API如何处理字符串和数据库将如何解释这些字符串之间创建了差异。在某些多字节字符串情况下,这可以用于注入攻击。
据我所知,mysql_real_escape_string本身没有根本性的注入漏洞,如果正确应用。然而,主要问题是很容易错误地应用它,从而导致漏洞。

谢谢deceze,看起来这里关于mysql_real_escape_string易受注入攻击的说法只适用于使用不当的情况。我将接受这个答案。 - Mitch Satchwell
1
另外,请参阅使用 NO_BACKSLASH_ESCAPES 时的额外漏洞 - eggyal

0

好的,除了mysql_*被弃用之外,我理解你想要了解可能存在的任何解决方法。也许这篇博客文章和幻灯片可能会揭示其中一些。
但正如这个旧问题所显示的那样,转换和引用并不是完全可靠的。有太多的事情可能出错,而墨菲定律与那句永远有效的口号“永远不要相信网络”交织在一起,将会出现可怕的错误。

也许你会对这篇文章感兴趣,但更重要的是那篇文章的后续内容可以揭示更多的安全问题。说实话,我知道mysql_real_escape_string即使与类型转换和字符串格式化相结合,也不是完美无缺的:

printf('WHERE id = \'%d\'',(int)mysql_real_escape_string($_REQUEST['id']));

并不能涵盖所有可能的攻击。
我不是这方面的专家,但我可以告诉你的是,对每个输入进行消毒,最多只会给你一种虚假的安全感。大部分时间,你会知道(最初)什么、为什么以及如何保护自己免受攻击,但你的同事可能不知道。他们可能会忘记某些东西,从而导致整个系统被破坏。

总之:是的,你可能能够防止任何形式的恶意输入进入你的数据库,但每个额外的操作都是一个增加的风险。在这种情况下,最大的责任(一如既往)是那些周一早上还没喝第四杯咖啡的开发人员。没有代码,无论多么防御性和深思熟虑,都无法保护自己免受疲惫的开发人员、脾气暴躁、戒掉咖啡因和尼古丁的怪物的侵害。


2
你会如何利用你展示的代码片段?问题(以及我)要求一个具体的、实际的例子。 - deceze
你链接的前两个参考资料都是关于错误应用程序的经典攻击,第三个甚至明确指出:“为避免此类漏洞,请使用mysql_real_escape_string()[...]”。我认为这只是又一个无解答案。 - deceze
@deceze:就本身而言,我发布的代码片段据我所知不能被利用,但可能会产生错误,并且有恶意的人喜欢在探测漏洞时使用良好的调用堆栈。在WHERE子句中很可能会使用多个参数,每个参数都需要三个额外的操作(字符串格式化、类型转换和转义调用),因此这种方法容易出错(如果类型转换或占位符出错,就不再安全)。在我链接的问题中,还提到了排序规则的问题,而我的代码片段根本没有处理这个问题。 - Elias Van Ootegem
@deceze:就第三个链接而言,说实话我主要关注的是评论部分。有人链接了这篇后续文章,我必须承认,它会更具信息性。 - Elias Van Ootegem
1
这些文章能指出的最好的问题就是GBK/字符集问题的常见错误应用。也就是说,当您陷入这样一种情况时,即mysql_real_escape_string在执行其工作时假定与数据库不同的字符集。这可以通过使用mysql_ API来更改字符集而不是使用“SET NAMES”查询来绕过它来轻松避免。我同意这是API的一个缺点,但并不是真正的_real_escape_string漏洞。mysql_ API可能比PDO更难正确应用,但我还没有看到根本漏洞。 - deceze
1
@deceze:我同意你的观点,我不会说这个_extension存在根本性漏洞。这就是为什么我总结说最大的责任在于开发人员,因为他们在任何时候都需要考虑更多的事情。 - Elias Van Ootegem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接