Php 有人能清楚地解释为什么mysqli_prepare()/bind_param()比real_escape_string()更好吗?

Php 有人能清楚地解释为什么mysqli_prepare()/bind_param()比real_escape_string()更好吗?,php,mysql,mysqli,prepared-statement,mysql-real-escape-string,Php,Mysql,Mysqli,Prepared Statement,Mysql Real Escape String,好吧,我还是不太明白。我一直在读到,为了正确地避开MySQL查询,您需要使用mysqli_prepare()和mysqli_bind_param() 我试着使用这个设置,坦率地说,它有点笨重。当我不需要再次引用变量时,我被困在通过引用传递变量的过程中,而完成同样的任务需要更多的代码行 我想我只是不明白这两者之间的区别: <?php $sql = new \MySQLi(...); $result = $sql->query(' UPDATE `table` SET

好吧,我还是不太明白。我一直在读到,为了正确地避开MySQL查询,您需要使用
mysqli_prepare()
mysqli_bind_param()

我试着使用这个设置,坦率地说,它有点笨重。当我不需要再次引用变量时,我被困在通过引用传递变量的过程中,而完成同样的任务需要更多的代码行

我想我只是不明白这两者之间的区别:

<?php
$sql = new \MySQLi(...);
$result = $sql->query('
   UPDATE `table`
   SET
      `field` = "'.$sql->real_escape_string($_REQUEST[$field]).'";
');
?>

我认为,与其说后一种方法本身更安全,不如说它鼓励逻辑分离。对于准备好的语句,SQL查询独立于我们使用的值。这意味着,例如,当我们返回并更改查询时,我们不必将一组不同的值连接到一个字符串,并且可能会忘记转义输入。使代码更易于维护

我发现有两个主要好处写得很好:

  • 因此会产生编译和优化语句的开销 只执行一次,尽管语句执行多次。不 所有优化都可以在准备好的语句时执行 它的编制有两个原因:最佳计划可能取决于 参数的具体值,最佳方案可能会随着 表和索引会随着时间的推移而变化
  • 准备好的语句能够抵抗SQL注入,因为 参数值,稍后使用不同的 协议,不需要正确转义。如果原始声明 模板不是从外部输入派生的,SQL注入无法 发生
  • 另一方面,如果查询只执行一次,则服务器端准备的语句可能会因为到服务器的额外往返而变慢。实现限制也可能导致性能下降:某些版本的MySQL没有缓存准备好的查询的结果,某些DBMS(如PostgreSQL)在执行过程中没有执行额外的查询优化


    我想从PHP5.4.0开始添加它。您应该使用您正在阅读的内容,您需要使用
    mysqli_prepare()
    mysqli_bind_param()
    函数来“正确转义您的MySQL查询”是错误的

    确实,如果使用
    mysqli\u prepare()
    mysqli\u bind\u param()
    ,则不需要(也不应该)“转义”作为绑定参数提供的值。所以,从这个意义上说,你所读的东西是有道理的

    只有当SQL文本(查询的实际文本)中包含不安全变量时,您才需要“正确地转义”变量,通常是通过将变量包装在
    mysqli\u real\u escape\u string()函数调用中

    (我们注意到,可以使用准备好的语句,并且仍然在SQL文本中包含未转义的变量,而不是将变量值作为bind_参数传递。这在某种程度上违背了使用准备好的语句的目的,但问题是,无论哪种方式,您都可以编写易受攻击的代码

    MySQL现在支持“服务器端”准备语句(如果在连接中启用了该选项),这是重复执行相同SQL文本的性能优化(在某些情况下)。(这在其他数据库中得到了长期的支持,比如Oracle,在Oracle中,使用准备好的语句是一种常见的模式,从永远以来都是如此。)

    Q:他们是否实现了[prepared statements],以便人们在发送查询之前不会忘记转义值

    A:基于不使用预处理语句时易受SQL注入攻击的代码示例的数量,尽管有关于
    mysql\u real\u escape\u string()
    函数的文档,但您认为这肯定是充分的理由

    我认为一个很大的好处是,当我们阅读代码时,我们可以将SQL语句视为单个字符串文字,而不是一堆变量的串联,带有引号、点和对mysql_real_escape_string的调用,这对于一个简单的查询来说并不太糟糕,但是对于一个更复杂的查询来说,这太麻烦了de>?
    占位符使SQL语句更易于理解,…没错,我需要查看其他代码行,以找出填充在那里的值。(我认为Oracle样式命名的parameters
    :fee,:fi,:fo,:fum
    比位置符号
    ?,,,,,,,,,,,,
    更可取。)但拥有静态SQL文本才是真正的好处

    Q:还是速度更快

    正如我前面提到的,使用服务器端准备的语句在性能方面可能是一种优势。它并不总是更快,但对于重复执行同一语句,唯一的区别是文字值(如重复插入),它可以提供性能提升

    Q:或者当我打算重复使用同一个查询时(因为mysqli\u stmt可以重用),我应该使用此方法,而在其他情况下使用传统方法吗

    这取决于您。我更喜欢使用静态SQL文本。但这实际上来自于使用Oracle的悠久历史,并且使用MySQL的相同模式自然适合(尽管来自使用DBI接口的Perl,以及使用JDBC和MyBATIS的Java,或其他ORM(Hibernate、Glassfish JPA等)

    遵循相同的模式在PHP中感觉很自然;mysqli_uu和PDO的引入是对神秘(和滥用)mysql_uu接口的一种令人欢迎的缓解

    好的代码可以按照这两种模式编写。但我要求您提前考虑更复杂的SQL语句,以及是否选择使用
    mysqli\u real\u escape\u string()
    <?php
    $sql = new \MySQLi(...);
    $stmt = $sql->prepare('
       UPDATE `table`
       SET
          `field` = ?;
    ');
    $value = $_REQUEST[$field];
    $stmt->bind_param('s', $value);
    $stmt->execute();
    $result = $stmt->get_result();
    unset($value);
    ?>