【问题标题】:Should I really be using PDO and prepared statements?我真的应该使用 PDO 和准备好的语句吗?
【发布时间】:2010-01-10 22:52:29
【问题描述】:

PDO 和prepared statements 对我来说仍然有点令人困惑,无论我到目前为止阅读了多少关于它们的信息。所以我知道它们更“安全”,但它真的那么重要吗?我的意思是我可以使用带有 mysql_real_escape_string() 和 htmlspecialchars() 的基本 mysql 获得相同的最终结果,对吗?

【问题讨论】:

    标签: php mysql pdo


    【解决方案1】:

    您可以,但 PDO 和准备好的语句绝对是最安全的。您可以手动完成并使用mysql_real_escape_string() 功能吗?当然。事实上,您的输出可能看起来相同。但最终,如果您手动完成,PDO 所需的代码将比您手动编写的代码短得多。

    此外,如果您不使用准备好的语句,则可能会出现人为错误:假设您忘记转义值或清理输入。与您的所有其他代码混合在一起,未正确清理的一行代码可能会成为未来的噩梦。

    希望这会有所帮助!

    【讨论】:

    • “准备好的语句是绝对安全的”,“你的输出可能看起来一样” - 什么?为什么?!准备好的语句与使用 mysql_real_escape_string() 一样安全,并且输出看起来总是相同的。
    • Alix:人为错误的可能性较小。如果您总是正确地转义所有内容(即:PDO),因为您将数据加载到查询中的唯一方法是通过转义函数,那么您可以保证数据是 100% 卫生的。不,输出看起来并不总是相同的。引用样式、转义样式和对数据类型的支持会因您采用的方法而异。
    【解决方案2】:

    我真的很喜欢 PDO 界面。一旦你习惯了它,它就会比 mysql_* 函数风格干净得多。我也花了一段时间才弄明白,但这是值得的。

    我发现令人困惑的部分是记住哪些方法属于 PDO DB 连接对象本身,哪些是语句对象的一部分。

    通过某些操作,您还可以从重复准备好的语句中获得性能优势。例如,如果你在一个循环中进行了一堆插入,你可以准备语句,然后每次插入前在循环中绑定新数据。

    安全性也好得多,因为您依靠经过良好测试的库来转义每次插入时的数据。这就像密码学——为什么要自己做,当它如此重要的时候?没有理由让自己有机会弄错(即,不小心错过了转义插入到查询中的内容)。

    我向 PDO 推荐 this guide,来自写此 excellent giant book on Mysql 的作者。

    我喜欢使用位置参数样式,然后你只需制作一个数据数组并将其传入。我的查询看起来像

    $pdo_db=pdo_connect('cow_db');
    $sql='select this,that,count(those) as snout from lovely_table where name=? and horses=?';
    $data=array($username,$horse_count);
    
    $query_stmt=$pdo_db->prepare($sql);
    $result_handle=$query_stmt->execute($data);
    
    //then I have a function to load data from the result handle
    $info=load_array($result_handle);
    

    您可以使这样的函数与标准的 php mysql 接口一起使用,但为什么不直接使用 PDO?

    【讨论】:

      【解决方案3】:

      我同意其他人所说的使用准备好的查询通常比使用转义函数更好。它更容易正确使用,并且参数值不会引入 SQL 注入问题,因为该值是与 SQL 查询分开发送到 RDBMS 服务器的。

      但是,仅当 SQL 查询的动态部分是参数而不是表达式中的文字值时,使用参数才有用。您不能使用查询参数代替表名、列名、SQL 表达式或值列表(例如 IN( ) 谓词的参数)。

      准备好的查询也比未准备的查询具有更好的性能(至少在 MySQL 中)。大多数人说的正好相反,但备受推崇的 mysqlperformanceblog.com 做了测试:

      http://www.mysqlperformanceblog.com/2006/08/02/mysql-prepared-statements/

      【讨论】:

        【解决方案4】:

        只要您为查询适当地清理数据,那么您就没有必须使用 PDO/prepared 语句。 虽然,我个人建议使用 PDO/prepared 语句,因为它们确实使开发/调试更容易,并且prepared statements 可以防止不正确的数据类型甚至进入查询。

        如果您想了解有关如何创建简单的准备好的语句的更多信息,请查看函数 sprintf。您只需用类型说明符替换任何变量字符串、整数等(在本例中分别为 %s%d)。

        例如,在下面的查询中,我知道id 将是一个整数(它将是数字),name 将是一个字符串(字母数字)。

        $username = 'Simon';
        $id       = 3;
        $query    = "SELECT FROM `users` WHERE `id` = {$id} AND `name` = '{$username}'";
        

        如果我从不受信任的来源(例如 POST/GET)获取这些变量中的任何一个,那么我可以通过将最后一行($query 集)替换为sprintf 像这样打电话:

        $username = 'Simon';
        $id       = 3;
        $query    = sprintf( "SELECT FROM `users` WHERE `id` = %d AND `name` = '%s'", $id, $username );
        

        sprintf 根本不允许我在 $id 被调用时使用字符串或 $name 使用整数,以确保给出正确的数据类型(这给了我一点额外的安全性)。如果给出了不正确的数据类型,那么我相信它会将变量转换为请求的类型。

        要了解有关sprintf 的更多信息,请访问此处:http://php.net/sprintf

        我希望这足以解释(这是我的第一个答案):)。

        【讨论】:

        • 我曾在 wordpress、vbulletin 和其他知名代码中看到过 sprintf()。在您的示例中,然后 $query 将在常规 mysql 函数中运行?我意识到 sprintf() 更安全,但我很好奇,这被认为是真正的“准备好的声明”还是类似的技术?谢谢
        • 但是,它不会为您清理 SQL。那个代码行不通。你最终会得到... AND `name` = Simon——你没有在“Simon”周围添加引号,sprintf 绝对不会为你这样做。
        • 是的,然后您将通过相关的 MySQL 查询函数运行它(如果您使用的是标准驱动程序,则为 mysql_query())。我不确定您是否会将 sprintf 视为一个真正准备好的声明,但它至少足够接近:-) @Samir:抱歉,我错误地删除了 ' 。感谢您指出! +1
        • %s 不会转义 $username 中的任何单引号,即您仍然可以遇到 bobby-tables 情况,xkcd.com/327 sprintf() 不能替代准备好的语句或 mysql_real_escape_string( )。除了 mysql_real_escape_string() 之外还可以使用它。
        • 天哪,sprintf 根本无法保护您。请更新您的解决方案以在 %s 参数中包含 mysql_real_escape_string%d 参数应该没问题。
        【解决方案5】:

        你可以试试 zend_db,它在后台使用 pdo。 (mdb2 是您可以使用的另一个选项。)

        【讨论】:

        • Zend_Db 有许多 PDO 扩展以及一些非 PDO 扩展的适配器,但 MDB2 没有。
        【解决方案6】:

        PHP 让您可以做任何您想做的事,而 PDO 恰好可以用更少的代码做“您想做的”。 :)

        【讨论】:

          猜你喜欢
          • 2016-12-30
          • 1970-01-01
          • 1970-01-01
          • 2012-05-15
          • 2017-09-29
          • 2013-12-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多