【问题标题】:Why mysql_real_escape_string() shouldn't avoid any SQL Injection?为什么 mysql_real_escape_string() 不应该避免任何 SQL 注入?
【发布时间】:2011-12-19 11:48:34
【问题描述】:

我听人说(与 C#/SQL Server 相关,但也与 PHP/MySql 相关):不要手动转义字符串 - 改用存储过程。

好的,我可以接受这个建议,但为什么呢?很多人说(包括 SO)mysql_real_escape_string() 就够了,mysql_real_escape_string() 很好,mysql_real_escape_string() 是第一种保护方式。

为什么?是否存在mysql_real_escape_string() 可能失败的情况?至少一个...我不需要很多:)

【问题讨论】:

  • 最大的弱点是你会忘记使用它!
  • 您会发现 MySQL 中存储过程的使用比 SQL Server 中的少,但 PHP/MySQL API 通常鼓励使用预准备语句。
  • 我在使用 mysql_real_escape_string() 时从来没有遇到过任何问题。我认为人们说使用存储过程是因为有机会忘记使用 mysql_real_escape_String()。
  • @Tomalak Geret'kal:怎么了?
  • @Khronos:“我从来没有遇到过问题”并不重要。对于大多数人(基本用法,没有针对您的应用程序的攻击),如果“他们遇到了问题”,那么整个世界也会遇到同样的问题。这意味着早在您了解mysql_real_escape_string 是什么之前,问题就已经解决了。

标签: php mysql security sql-injection


【解决方案1】:

当mysql_real_escape_string 失败:

$sql = "SELECT * FROM users WHERE id=" + mysql_real_escape_string($_GET['id']);

如果 $_GET['user_id'] 设置为 1 OR 1=1,则没有特殊字符且不会被过滤。

结果:返回所有行。

情况变得更糟。这个怎么样...如果 $_GET['user_id'] 设置为 1 OR is_admin = 1?

该函数仅设计为在单引号内使用。

【讨论】:

  • 我在野外经常看到这种情况
  • 是的!通常,如果我考虑一个 int,我会先检查 $_GET['user_id'] 是否为 int!是的,我当然应该使用 PDO 来避免这些检查...
  • 这更像是对函数的滥用。毕竟,它被命名为mysql_real_escape_string,而不是mysql_real_escape_integer。这并不意味着与整数字段一起使用。
【解决方案2】:

mysql_real_escape_string 有两点可能出错:

  • 你可以忘记使用它
  • 如果您使用某些特定的多字节连接编码,并且您已将这些编码设置为 SET NAMES 而不是 mysql_set_charset

更新:

  • 使用 UTF-8 是安全的(尽管 这并不意味着您应该继续使用 SET NAMES!)
  • 解释见here

【讨论】:

  • 你能给我举一个第二点的例子吗?我使用 SET NAMES 和 UTF8,应该是安全的吧?
  • 第三件事:你可以记住使用它,不要在引号内使用。
【解决方案3】:

仅供参考: mysql_real_escape_string() 不会转义 % 和 _。如果与 LIKE, GRANT, or REVOKE 结合使用,这些是 MySQL 中的通配符。

【讨论】:

    【解决方案4】:

    mysql_real_escape_string() 可能无法清理输入。 由于mysql_real_esacpe_string() 在清理字符串时考虑了字符集。 有问题。您可以通过mysql_query 函数发送查询来更改字符集以更改连接的字符集。但是,mysql_real_escape_string() 忽略了您正在使用的集合,它会不正确地转义某些字符。

    另一件事是不断手动调用它。甚至将它包装在一个函数中也是一个 P.I.T.A.因为这意味着您必须自己创建某种数据库包装器/数据库抽象层,以便能够自动调用mysql_real_escape_string()。

    这就是我们在 PHP 中使用 PDO 的原因,它可以帮助我们缓解上述所有问题,并且您可以使用参数化的预处理语句,这是处理更改数据的重复查询的首选方式。

    准备语句,绑定输入变量,它将根据正在使用的数据库驱动程序和连接的字符集清理输入。它的代码更少,而且完全安全。

    【讨论】:

    • 没有什么是完全安全的,即使是 PDO。 IE。它没有使用表名和列名作为参数的机制,因此无论哪种方式,您最终都会使用手动消毒来防止注射,即使使用 PDO。
    • @holodoc - 如果您发现自己需要通过 PDO 绑定列名和表名,这意味着您正在从用户的输入中接收列/表名,这进一步意味着您正在做的方式这是完全错误的。否则,如果您已将 col./tables 编码到 DBAL 的数组中,那么需要额外检查它们是否导致 sql 错误? PDO 对于保存用户的输入是完全安全的。 mysql_real_escape 有一些缺陷,已经指出。
    • 为什么使用动态列名和表名意味着它们是从用户输入中检索的?在很多情况下,表/列名是由简单的流控制决定的。
    • @holodoc 那么您关于 PDO 不安全的论点是没有实际意义的,如果您不是从用户那里而是从安全来源接收输入,为什么还要使用 PDO 来清理输入(列、表名)?抱歉,您的整个观点毫无意义。
    【解决方案5】:

    mysql_real_escape_string()和SQL注入只有一件事——

    前者与后者没有半点关系。

    虽然听起来自相矛盾,但没有什么比这更真实的了。

    这里有 2 个陈述可以证明这一点

    1. 您只需转义带引号的字符串,因为此函数无其他帮助。
    2. 事实上,您必须转义要添加到查询中的每个 字符串,即使是最安全的字符串。只是因为它可能包含一些特殊字符,从而破坏了查询(只是一个意外,而不是恶意情节)。

    因此,在适用的情况下,无论如何都必须使用此功能,尽管有任何危险或恐惧。在任何其他情况下,它都无济于事。

    我唯一要补充的是,准备好的语句也不提供完全保护。以下是解释和建议:https://stackoverflow.com/a/8255054/285587

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-02
      • 2014-12-15
      • 2011-07-10
      • 2014-10-28
      • 1970-01-01
      • 2017-11-10
      • 1970-01-01
      • 2013-12-31
      相关资源
      最近更新 更多