为什么PDO比mysql_real_escape_string更好地逃避MySQL查询/查询字符串?

时间:2009-11-16 13:01:13

标签: php pdo escaping mysql-real-escape-string

我被告知我最好使用PDO进行MySQL转义,而不是mysql_real_escape_string

也许我有一个脑死亡的日子(或者可能是因为我没有想象力的自然程序员,而且在PHP方面我还处于新手阶段)但是,在查看了PHP手册并阅读the entry on PDO后,我仍然不清楚PDO实际上是什么以及为什么它比使用mysql_real_escape_string更好。这可能是因为我还没有真正掌握OOP的复杂性(我假设它与OOP有关),但除了变量和数组值似乎在其前面有冒号之外,我仍然不确定它实际上是什么以及你如何使用它(以及为什么它比mysql_real_escape_string更好。(这也可能与我真的没有清楚地了解什么有关“课程”是,所以当我读“PDO课程”时,我真的不是更聪明的。)

在MySQL网站的'Developer Zone'位上读了an article或两个,我仍然不清楚。由于我现在甚至无法弄清楚它到底是什么,我认为现在可能使用它有点超出我的范围,但我仍然有兴趣扩大我的教育并找出如何改进的方法。

有人能用“简单的英语”向我解释PDO是什么(或指向我用简单英语写的主题的方向),以及你如何使用它?

6 个答案:

答案 0 :(得分:57)

由于当前的答案会详细介绍,而您的问题更多是针对概述,我会试一试:

PDO类旨在封装与数据库交互所需的所有功能。他们通过定义'方法'(功能的OO客厅)和'属性'(变量的OO客厅)来做到这一点。您可以将它们用作完全替换,用于您现在用于与数据库通信的所有“标准”功能。

因此,不是调用一系列'mysql_doSomething()'函数,将结果存储在自己的变量中,而是从PDO类中“实例化”一个对象('class'= abstract definition,'object'= concrete ,可用的类实例)并在该对象上调用方法来执行相同的操作。

例如,如果没有PDO,你可以这样做:

// Get a db connection
$connection = mysql_connect('someHost/someDB', 'userName', 'password');
// Prepare a query
$query = "SELECT * FROM someTable WHERE something = " . mysql_real_escape_string($comparison) . "'";
// Issue a query
$db_result = mysql_query($query);
// Fetch the results
$results = array();
while ($row = mysql_fetch_array($db_result)) {
  $results[] = $row;
}

虽然这相当于使用PDO:

// Instantiate new PDO object (will create connection on the fly)
$db = new PDO('mysql:dbname=someDB;host=someHost');
// Prepare a query (will escape on the fly)
$statement = $db->prepare('SELECT * FROM someTable WHERE something = :comparison');
// $statement is now a PDOStatement object, with its own methods to use it, e.g.
// execute the query, passing in the parameters to replace
$statement->execute(array(':comparison' => $comparison));
// fetch results as array
$results = $statement->fetchAll();

所以乍一看,除了语法之外没有太大区别。但PDO版本有一些优点,最大的一个是数据库独立性:

如果您需要与PostgreSQL数据库交谈,则只需在实例化调用mysql:中将pgsql:更改为new PDO()。使用旧方法,您必须完成所有代码,用'pg_doSomthing()'对应替换所有'mysql_doSomething()'函数(始终检查参数处理中的潜在差异)。许多其他受支持的数据库引擎也是如此。

所以回到你的问题,PDO基本上只是给你一个不同的方法来实现相同的东西,同时提供一些捷径/改进/优势。例如,转义将以您正在使用的数据库引擎所需的正确方式自动发生。参数替换(防止SQL注入,未在示例中显示)也更容易,使其更不容易出错。

您应该阅读some OOP basics以了解其他优势。

答案 1 :(得分:40)

我对PDO并不是很熟悉,但“预准备语句”和转义字符串之间存在区别。转义是关于从查询中删除不允许的字符串,但是准备好的语句是关于告诉数据库期望什么类型的查询

查询有多个部分

以这种方式思考:当您向数据库提出查询时,您会告诉它几个不同的事情。有一件事可能是,例如,“我希望你做一个选择。”另一个可能是“将其限制为行,其中用户名是以下值。”

如果您将查询构建为字符串并将其交给数据库,则在获取完成的字符串之前,它不知道任何一个部分。你可以这样做:

'SELECT * FROM transactions WHERE username=$username'

当它获得该字符串时,它必须解析它并决定“这是SELECT WHERE”。

让零件混淆

假设恶意用户将其用户名输入为billysmith OR 1=1。如果你不小心,可以把它放到你的字符串中,结果是:

'SELECT * FROM transactions WHERE username=billysmith OR 1=1'

...会返回所有用户的所有交易,因为1总是等于1.哎呀,你被黑了!

看看发生了什么? 数据库不知道您的查询中需要哪些部分,所以它只是解析了字符串。 WHERE有一个OR,有两个条件可以满足它,这并不奇怪。

保持部件直线

如果只知道期待的内容,即SELECT WHERE只有一个条件,那么恶意用户就不会欺骗它。

通过准备好的陈述,您可以给予正确的期望。您可以告诉数据库“我即将向您发送SELECT,它将仅限于行WHERE username =我将要给您的字符串。这就是全部 - 有没有查询的其他部分。你准备好了吗?好的,这里有与用户名比较的字符串。“

有了这个期望,数据库不会被愚弄:它只会返回username列包含实际字符串'billysmith OR 1 = 1'的行。如果没有人拥有该用户名,则不返回任何内容。

准备好的陈述的其他好处

除了安全性方面的好处之外,准备好的语句还有几个速度优势:

  • 它们可以使用不同的参数重用,这应该比从头构建新查询更快,因为数据库已经基本知道您要求的内容。它已经建立了“查询计划”。
  • 有些数据库(Postgres是一个,我认为)会在获得准备好的语句之后立即开始制定查询计划 - 在您实际发送参数以供其使用之前。所以你甚至可以在第一次查询时看到加速。

有关其他说明,请参阅Theo的回答here

答案 2 :(得分:16)

与mysql_real_escape_string不同,PDO允许您强制执行数据类型。

<?php
/* Execute a prepared statement by binding PHP variables */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);
$sth->execute();
?>

请注意,在上面的示例中,第一个参数calories必须是一个整数(PDO :: PARAM_INT)。

其次,对我来说,PDO参数化查询更容易阅读。我宁愿阅读:

SELECT name FROM user WHERE id = ? AND admin = ? 

大于

SELECT name FROM user WHERE id = mysql_real_escape_string($id) AND admin = mysql_real_escape_string($admin);

第三,您无需确保正确引用参数。 PDO负责这一点。例如,mysql_real_query_string:

SELECT * FROM user WHERE name = 'mysql_real_escape_string($name)' //note quotes around param

VS

SELECT * FROM user WHERE name = ?

最后,PDO允许您将应用程序移植到不同的数据库,而无需更改PHP数据调用。

答案 3 :(得分:14)

想象一下你写的东西:

$query = 'SELECT * FROM table WHERE id = ' . mysql_real_escape_string($id);

这不会使您免于注射,因为$ id可能是1 OR 1=1,您将从表中获得所有记录。你必须将$ id强制转换为正确的数据类型(在这种情况下为int)

pdo还有另一个优势,那就是数据库后端的可互换性。

答案 4 :(得分:3)

除了防止SQL注入外,PDO还允许您准备一次查询并多次执行。如果你的查询被多次执行(例如在一个循环中),这个方法应该更有效(我说“应该是”,因为在旧版本的MySQL上看起来并非总是如此)。准备/绑定方法也更符合我使用过的其他语言。

答案 5 :(得分:3)

  

为什么PDO比mysql_real_escape_string更好地逃避MySQL查询/查询字符串?

仅仅因为“逃避”是没有意义的 而且,它是不同的无与伦比的事项。

逃避的唯一问题是每个人都错了,假设它是某种“保护”。
每个人都说“我逃脱了我的变量”,意思是“我保护了我的查询” 虽然单独逃避与保护毫无关系。

我转义并引用我的数据的情况下,可以大致实现保护,但它并不适用于所有地方,例如标识符(以及PDO,顺便说一句)。

所以答案是:

  • PDO,在为绑定值进行转义时,不仅适用于转义,还适用于引用 - 这就是为什么它更好。
  • “逃避”不是“保护”的同义词。 “逃避+引用”大致是。
  • 但对于某些查询部分,这两种方法都不适用。