我开始知道在使用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
这里重要的是:
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 integer更改为string:
之后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也回答这是正确的。
答案 0 :(得分:6)
您的PDO配置为模拟准备好的查询,而mysqli使用真实准备的查询。
准备好的查询将字符串''1''
绑定为整数参数值。 PHP使用intval()
之类的东西将其强制转换为整数。任何带有非数字前导字符的字符串都被PHP解释为0,因此在 prepare之后发送的参数值是值0。
伪造的查询使用字符串插值(而不是绑定)在 MySQL解析它之前将字符串''1''
添加到SQL查询中。但结果是类似的,因为SQL还将整数上下文中带有非数字前导字符的字符串视为值0.
唯一的区别是当参数在准备之前与准备之后绑定时,一般查询日志中会出现什么。
你也可以让PDO使用真正准备好的查询,所以在这种情况下它应该像mysqli一样:
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
PS:这可能是一个很好的理由,为什么习惯性地将id值设置为1而不是0。