【发布时间】:2010-01-10 22:52:29
【问题描述】:
PDO 和prepared statements 对我来说仍然有点令人困惑,无论我到目前为止阅读了多少关于它们的信息。所以我知道它们更“安全”,但它真的那么重要吗?我的意思是我可以使用带有 mysql_real_escape_string() 和 htmlspecialchars() 的基本 mysql 获得相同的最终结果,对吗?
【问题讨论】:
PDO 和prepared statements 对我来说仍然有点令人困惑,无论我到目前为止阅读了多少关于它们的信息。所以我知道它们更“安全”,但它真的那么重要吗?我的意思是我可以使用带有 mysql_real_escape_string() 和 htmlspecialchars() 的基本 mysql 获得相同的最终结果,对吗?
【问题讨论】:
您可以,但 PDO 和准备好的语句绝对是最安全的。您可以手动完成并使用mysql_real_escape_string() 功能吗?当然。事实上,您的输出可能看起来相同。但最终,如果您手动完成,PDO 所需的代码将比您手动编写的代码短得多。
此外,如果您不使用准备好的语句,则可能会出现人为错误:假设您忘记转义值或清理输入。与您的所有其他代码混合在一起,未正确清理的一行代码可能会成为未来的噩梦。
希望这会有所帮助!
【讨论】:
mysql_real_escape_string() 一样安全,并且输出看起来总是相同的。
我真的很喜欢 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?
【讨论】:
我同意其他人所说的使用准备好的查询通常比使用转义函数更好。它更容易正确使用,并且参数值不会引入 SQL 注入问题,因为该值是与 SQL 查询分开发送到 RDBMS 服务器的。
但是,仅当 SQL 查询的动态部分是参数而不是表达式中的文字值时,使用参数才有用。您不能使用查询参数代替表名、列名、SQL 表达式或值列表(例如 IN( ) 谓词的参数)。
准备好的查询也比未准备的查询具有更好的性能(至少在 MySQL 中)。大多数人说的正好相反,但备受推崇的 mysqlperformanceblog.com 做了测试:
http://www.mysqlperformanceblog.com/2006/08/02/mysql-prepared-statements/
【讨论】:
只要您为查询适当地清理数据,那么您就没有必须使用 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
我希望这足以解释(这是我的第一个答案):)。
【讨论】:
... AND `name` = Simon——你没有在“Simon”周围添加引号,sprintf 绝对不会为你这样做。
sprintf 根本无法保护您。请更新您的解决方案以在 %s 参数中包含 mysql_real_escape_string。 %d 参数应该没问题。
你可以试试 zend_db,它在后台使用 pdo。 (mdb2 是您可以使用的另一个选项。)
【讨论】:
PHP 让您可以做任何您想做的事,而 PDO 恰好可以用更少的代码做“您想做的”。 :)
【讨论】: