【问题标题】:PDO sends raw query to MySQL while Mysqli sends prepared query, both produce the same resultPDO 向 MySQL 发送原始查询,而 Mysqli 发送准备好的查询,两者都产生相同的结果
【发布时间】:2013-08-04 06:24:17
【问题描述】:

我开始了解使用 MySQLi 和 PDO 时准备好的语句是如何工作的,第一步,我启用了 MySQL 查询监控,如下所述:How can I view live MySQL queries?。然后我创建了以下测试:

使用mysqli:

$stmt = $mysqli->prepare("SELECT * FROM users WHERE username =?")) {
$stmt->bind_param("i", $user);
$user = "''1''";

服务器日志:

  130802 23:39:39   175 Connect   ****@localhost on testdb
    175 Prepare   SELECT * FROM users WHERE username =?
    175 Execute   SELECT * FROM users WHERE username =0
    175 Quit

使用 PDO:

  $user = "''1''";
  $sql = 'SELECT * FROM user WHERE uid =?';
  $sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
  $sth->bindParam(1, $user, PDO::PARAM_INT);

服务器日志:

  130802 23:41:42   176 Connect   ****@localhost on testdb
    176 Query SELECT * FROM user WHERE uid ='\'\'1\'\''
    176 Quit

但是,两者都提供相同的结果:

uid: 0
username: admin
role: admin

注意:uid = 0 是正确的,因为intval("''1''") = 0

这里有什么重要的:

PDO 查询如何在向 MySQL 发送不同的查询时得到相同的结果?

SELECT * FROM user WHERE uid ='\'\'1\'\''

我从 PHP 手册中只找到一个指示:http://www.php.net/manual/en/pdo.prepare.php

注意:

模拟的预处理语句不与数据库通信 服务器所以 PDO::prepare() 不会检查语句。

但我不确定 MySQL 如何处理此查询并将'\'\'1\'\'' 替换为0。在这种情况下,如果使用 PDO 监控查询将不准确,同时,使用 PDO 更好地了解发送到 MySQL 而不是 MySQLi 的确切查询。

更新: 将参数类型frm整数更改为字符串后:

MySQLi 日志:

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

PDO 日志:

    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

这意味着 MySQLi 和 PDO 在使用字符串时会在发送到 MySQL 之前转义数据,而对于整数,mysqli 在发送查询之前应用 intval() 或类似的东西,Bill 也回答这是正确的。

【问题讨论】:

  • 这与准备好的语句有什么关系?
  • Ì第一个代码sn-p,为什么将$user设置为''1''?当你绑定它时,你使用i(整数)。然后,当您将 $user 设置为 ''1'' 时,MySQL 可能不会将其识别为数字,而只是将其设置为 0(零)。
  • @SverriM.Olsen:我假设攻击者输入了这个值,我想查看查询日志是如何发送到 MySQL 的,考虑$user 将是的任何数据......
  • @YourCommonSense:这个对prepared statement的理解的核心,这个是如何深入工作的,有什么不清楚的,我可以解释一下。此外,如果仍然使用错误的关键字进行搜索,请查看返回的错误结果,这表明在执行任何查询之前验证用户输入很重要。
  • 你的问题是mysql如何在原始查询中评估''1''字符串。关于松散类型转换的很常见的问题。

标签: php mysql pdo mysqli


【解决方案1】:

您的 PDO 被配置为模拟准备好的查询,而 mysqli 使用的是真正的准备好的查询。

准备好的查询将字符串''1''绑定为整数参数值。 PHP 使用intval() 之类的东西将其强制转换为整数。任何带有非数字前导字符的字符串都被 PHP 解释为 0,因此 after prepare 发送的参数值就是值 0。

伪准备查询使用字符串插值(而不是绑定)将字符串''1'' 添加到SQL 查询中 MySQL 解析它之前。但结果是相似的,因为 SQL 也将整数上下文中具有非数字前导字符的字符串视为值 0。

唯一的区别是在准备之前和准备之后绑定参数时,一般查询日志中的结果。

你也可以让 PDO 使用真正的准备好的查询,所以在这种情况下它应该像 mysqli 一样工作:

$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

PS:这可能很好地说明了为什么习惯上从 1 而不是 0 开始 id 值。

【讨论】:

  • But the result is similar, because SQL also treats a string with non-numeric leading characters in an integer context as the value 0. 正是我通过实验发现的,你很聪明...
  • 我收到了匿名反对票。请,downvoter,发表评论告诉我这个问题是如何不令人满意的。也许我可以改进它。
  • 我也有一个,但不是在这里,3天前的答案,算了:)
  • @BillKarwin 欢迎使用 PDO 标签。这里几乎所有的答案都被 PDO 警察否决了。
猜你喜欢
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
  • 2016-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多