什么是使用real_escape_string的正确时间?当数据到达POST时,或者在编写查询之前?

时间:2013-01-05 09:34:52

标签: php database mysqli escaping code-injection

我们知道,在编写SQL查询之前必须转义防止SQL injection problems字符串值 - 特别是来自用户或其他外部源的字符串。

何时应该逃脱?是否应该在值进入程序时完成,存储转义值以供以后使用?或者应该存储未转义的值,并在查询编写时将其转义?哪种方法更安全?什么是权衡?

1)收到价值时逃离的例子:

$test = $mysqli->real_escape_string($_POST['test']);
. 
. 
. 
$query=" UPDATE * from test_panel where test='" . $test . "'";

2)组成查询时转义的示例:

$test = $_POST['test'];
. 
. 
. 
$query=" UPDATE * from test_panel where test='" . $mysqli->real_escape_string('$test') . "'";

这些方法之间有区别吗?哪种方法更容易注射,哪种方法最安全?

5 个答案:

答案 0 :(得分:1)

这是一个非常有趣的问题,但答案并不那么容易。

  

使用real_escape_string的正确时间是什么?当数据到达POST时,或者在编写查询之前?

<强> NEITHER

让我解释一下。

首先,让我们理清术语。提出问题的方式有很多错误。

  1. 让我们不要使用real_escape_string来谈论转义,而是使用格式化。仅仅因为转义的使用非常有限 - 它只是一种SQL文字格式规则的一部分。其他类型需要不同的格式规则。
  2. 因此,当数据“到达POST”时格式化是不可能的 - 我们无法分辨哪个字段进入查询中的哪个位置,因此我们只是不知道应用哪些规则。
  3. 最后但并非最不重要:POST或任何其他外部源都没有与查询格式完全无关。一旦必须将字符串文字放入查询中,您必须根据SQL格式化它语法规则,无论其来源。数字等也一样。
  4. 因此,我们必须格式化我们的数据的唯一合适时间就在查询撰写之前。

    然而,在应用程序代码中应用real_escape_string()是一种非常糟糕的做法。

    1. 如上所述,转义不足来格式化字符串。字符串格式化涉及转义和引用。因此,无论用于格式化SQL查询字符串的工具如何,它都应始终执行两个任务,而不是一个任务。引用和转义。因为如果将这两个规则应用于另一个规则是完全无用的。因此,在一个设施中将它们连接在一起是必要的
    2. 不要忘记不同数据类型的不同格式规则。数字必须明确地转换为它的类型,而转义对它们没有好处。
    3. 手动逃避只是愚蠢。重复$mysqli->real_escape_string('$test')使您的代码变得臃肿且难以阅读。为什么不让数据库驱动程序为您执行所有格式化?因此,您必须遵循最先进的技术 - 使用占位符来表示查询中的数据。处理这样的占位符时,驱动程序会自动格式化其所在位置的数据 它既安全又方便。
    4. 有两种方法可以轻松使用占位符(没有手动绑定,在可读性方面不比手动转义好):

      • 使用PDO,因为它允许您传递要在准备好的查询中使用的变量

      所以,代码将是

      $db->prepare("SELECT * from test_panel where test=?");
      $db->execute(array($_POST['test']));
      

      和PDO将在内部完成所有格式化

      • 或发明自己的包装器以实现占位符
      像这样一个

      function paraQuery()
      {
          global $mysqli;
      
          $args  = func_get_args();
          $query = array_shift($args);
          $query = str_replace("%s","'%s'",$query); 
      
          foreach ($args as $key => $val)
          {
              $args[$key] = $mysqli->real_escape_string($val);
          }
      
          $query  = vsprintf($query, $args);
          $result = $mysqli->query($query);
          if (!$result)
          {
              throw new Exception($mysqli->error()." [$query]");
          }
          return $result;
      }
      
      $query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
      $result = paraQuery($query, $a, "%$b%", $limit);
      

      或者,对于您当前的查询:

      $result = paraQuery("SELECT * from test_panel where test=%s", $_POST['test']);
      
      看起来 - 它变得简短,健全且安全。

答案 1 :(得分:0)

你应该尽可能地逃避它们。

您希望这样做的原因是您的数据始终是准确的。例如,如果您在开始时直接转义字符串,strlen将不会相同(与非转义版本相同),这可能会在某些情况下导致混淆/错误。


真正的(imo)回答你的问题就是忘记逃避并使用prepared statements'

答案 2 :(得分:-1)

在查询之前或查询中转义输入无关紧要。

是的,一切都必须逃脱。没有理由,为什么你不想逃避自己的事情。

如果你不想逃避字符串,你会意识到它; - )

答案 3 :(得分:-1)

在编写查询的实际使用之前,永远不应对值进行转义。无论您使用预准备语句/ PDO还是使用real_escape_string将查询组成SQL格式的字符串,都是如此。

过早清理/转义数据值并以该形式保存数据的做法会导致错误。如果变量包含数据值(如客户名称或帐号),则该变量应包含未转义的原始值。

只有当您实际形成查询时,才能确保所有值都正确编码,因为它们被放入该查询

将包含原始数据值的变量视为包含查询的变量的不同类型的变量。切勿直接将查询值分配给原始数据值,也不要将原始数据值组合起来进行查询。编写查询是触发器,知道您应该对原始数据值进行编码。

通过实践这一点,在编码发生的情况下,它将是清晰且一致的,您将减少双重编码或编码失败的可能性。

想象一下,您尝试执行相反的操作:预编码所有值。这实际上是不可能的,因为您有许多字符串值,并且并非所有字符串值都用于查询。在某个地方,您可能有一个变量既可用作显示输出,也可用于查询。显示转义值将不正确。同样很难(或不可能)跟踪哪些变量用于查询,哪些变量用于其他非SQL用途。

始终在所有字符串变量中存储原始(未转义)值,直到您实际撰写查询为止。这种做法也与使用预准备语句一致,因为准备好的语句会传递一个未转义的值。

答案 4 :(得分:-2)

首先,您应该将连接作为mysqli real escape string中的第二个参数传递 其次,你还应该使用准备好的陈述

http://php.net/manual/en/mysqli.prepare.php