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

时间:2013-09-05 23:21:10

标签: 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
      `field` = "'.$sql->real_escape_string($_REQUEST[$field]).'";
');
?>

<?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);
?>

除了更多代码。

我的意思是,他们是否实现了这一点,以便人们在查询中发送之前不会忘记转义值?还是以某种方式更快?

或者当我打算重复使用相同的查询时(因为mysqli_stmt可以重复使用)并在其他情况下使用传统方法,我应该使用此方法吗?

4 个答案:

答案 0 :(得分:3)

您正在阅读的内容,您需要使用 mysqli_prepare() mysqli_bind_param() 函数来“正确转义MySQL查询”是错误的。

确实如果你使用 mysqli_prepare() mysqli_bind_param() ,你就不需要(也不应该)“逃避”作为绑定参数提供的值。所以,从这个意义上说,你正在阅读的内容中有一些道理。

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

(我们注意到可以使用预准备语句,而仍然在SQL文本中包含未转义的变量,而不是将变量值作为bind_parameters传递。这有点打败了使用预准备语句的目的,但重点是,无论哪种方式,您都可以编写易受攻击的代码。

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

问:他们是否实施了[预备语句],以便人们在查询中发送之前不会忘记转义值?

答:基于不使用预准备语句时易受SQL注入攻击的代码示例数量,尽管有关于mysql_real_escape_string()函数的文档,但您认为这肯定是足够的原因。

我认为一个很大的好处是,当我们阅读代码时,我们可以看到一个SQL语句作为单个字符串文字,而不是一堆变量的连接,带引号和点以及对mysql_real_escape_string的调用,这不是对于简单的查询来说太糟糕了,但是查询更复杂,这太过于繁琐了。 ?占位符的使用使得一个更容易理解的SQL语句,...是的,我需要查看其他代码行来确定在那里填充的值。 (我认为名为参数:fee, :fi, :fo, :fum的Oracle样式优于位置?, ?, ?, ?表示法。)但是使用STATIC SQL文本才是真正的好处。

问:或者它的速度有多快?

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

问:或者当我打算重复使用相同的查询时(因为mysqli_stmt可以重复使用)并且在其他情况下使用传统方法,我应该使用此方法吗?

这取决于你。我更喜欢使用STATIC SQL文本。但这确实来自于使用Oracle的悠久历史,并且使用与MySQL相同的模式非常适合。 (虽然,来自Perl使用DBI接口,Java使用JDBC和MyBATIS,或其他ORM(Hibernate,Glassfish JPA等)

遵循相同的模式在PHP中感觉很自然; mysqli_和PDO的引入是奥术(和滥用)mysql_接口的一个受欢迎的缓解。

可以按照任一模式编写好的代码。但我挑战你提前考虑,更复杂的SQL语句,以及是否选择使用 mysqli_real_escape_string() 并将动态字符串连接在一起,而不是使用静态SQL文本和绑定参数,可能会进行读取和解密,实际执行的SQL对于发现自己维护未编写代码的灵魂来说更复杂。

我认为研究表明,代码读取的次数比编写代码多十倍,这就是为什么我们努力生成可读,易懂的代码,即使这意味着需要更多的代码行。 (当每个语句都在做一个可识别的事情时,我通常比在一个复杂的语句中阅读混杂的连接函数调用更容易理解。

答案 1 :(得分:2)

我认为后一种方法本身本身比鼓励逻辑分离更不是问题。使用预准备语句,SQL查询独立于我们使用的值。这意味着,例如,当我们返回并更改我们的查询时,我们不必将一堆不同的值连接到字符串,并且可能冒险忘记转义我们的输入。使代码更易于维护!

答案 2 :(得分:2)

我发现有一些主要的好处是写得很好:

  1. 会产生编译和优化语句的开销 虽然声明多次执行,但只有一次。不 所有优化都可以在准备好的语句时执行 编译,有两个原因:最好的计划可能取决于 参数的具体值,最佳计划可能会更改为 表和索引随时间而变化。
  2. 准备好的语句可以抵御SQL注入,因为 参数值,稍后使用不同的值传输 协议,无需正确转义。如果是原始声明 模板不是从外部输入派生的,SQL注入不了 发生。
  3. 另一方面,如果查询仅执行一次,则服务器端预处理语句可能会因为额外的服务器往返而变慢。实施限制也可能导致性能损失:某些版本的MySQL没有缓存准备好的查询结果,而某些DBMS(如PostgreSQL)在执行期间不执行额外的查询优化。

    Source

答案 3 :(得分:0)

我想在PHP 5.4.0中添加mysqli_bind_param() has been removed 。您应该使用mysqli_stmt_bind_param()