php PDO的模拟预编译语句和本地预处理语句区别

今天得知php中使用pdo的时候,并不一定能避免sql注入的问题,PDO::ATTR_EMULATE_PREPARES,默认是true,通过查资料我得知如果这里是true,则启动模拟预编译,false则使用本地预处理语句,网上其他资料告知:如果是模拟预编译的话,则还是可能导致sql注入,请大神赐教这两种编译的区别,还有如果还是可能导致sql注入的话,为什么其他资料上从为提及过这个问题?是否开发的时候时候pdo也需要加上这句
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false)才能达到真正意义上的防sql注入?

阅读 6.4k
2 个回答

开发的时候最好是加上这句$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false),这样如果支持本地预处理则会使用本地预处理,在不支持的情况下会自动使用模拟预处理方式。

使用模拟预处理的方式是在客户端本地执行预处理的模拟,最终将拼好的sql语句发送到mysql服务器进行执行(实际上就是完成了字符串拼接,据说对表名使用占位符会出现sql注入,我没有验证过),mysql会对sql进行parsing->resolution->optimization->execution。使用本地预处理方式则是分两步:第一步是prepare阶段,发送带有占位符的sql语句到mysql服务器(parsing->resolution),然后就可以多次发送占位符参数给mysql服务器进行执行(多次执行optimization->execution)。

使用模拟预处理方式实际上就是一次发送完整的sql给mysql执行,不需要mysql做额外处理(如保存会话状态等),因此性能比较好一些,而使用本地预处理则需要多次发送,mysql服务器需要保存会话状态,性能上会有一些损耗。

另外,使用本地预处理的一个好处是在prepare阶段就能检测出sql语句的错误,而使用模拟的预处理方式制定在exec阶段才能发现(因为模拟方式拼接好sql在exec阶段才会发送到mysql服务器)。

总之,为了安全,那一点性能损耗算得了什么,如果没有什么理由不加,那还是加上吧。

为什么PDO默认要模拟预处理,就是因为有些数据库如SQLite不支持预处理.
模拟预处理也是能防止SQL注入的,如果不能,请举例.
medoo.php这个基于PDO的查询构造器(Query Builder)封装,
medoo连PDO预处理参数化查询都没有使用,
直接就是PDO::quote转义用户输入后,用query/exec执行查询.
我理解的模拟预处理本质就是PDO::quote.
MySQLi中类似PDO::quote的东西叫做mysqli_real_escape_string.

function get_posts_by_ids(array $ids) {
    global $app;
    $db = db();
    $table = $app['db_prefix'].'post';
    $place_holders = implode(',', array_fill(0, count($ids), '?'));
    //SELECT * FROM `post` WHERE `id` IN ('1','3','5') 包含三个参数
    $sql = "SELECT * FROM `{$table}` WHERE `id` IN ({$place_holders})";
    $stmt = $db->prepare($sql); $stmt->execute($ids); //所有id都当做字符串处理,值传递.
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题