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

Bli*_*ssC 48 php pdo escaping mysql-real-escape-string

我被告知我会更好地使用PDOMySQL转义,而不是mysql_real_escape_string.

也许我有一个脑死亡的日子(或者可能是因为我没有想象力是一个天生的程序员,而且我仍然处于PHP的新手阶段),但是检查了PHP手册并阅读了关于PDO的条目,我仍然不清楚PDO究竟是什么以及为什么它比使用更好mysql_real_escape_string.这可能是因为我还没有真正掌握OOP的复杂性(我假设它与OOP有关),但除了变量和数组值似乎在其前面有冒号之外,我仍然不确定它到底是什么以及你如何使用它(以及为什么它比它好mysql_real_escape_string.(它也可能与我没有真正理解'类'是什么的事实有关,所以,当我读到"PDO课程"时,我真的不是更聪明的.

在MySQL网站的"开发者专区"中读过两篇文章后,我仍然不清楚.由于我现在甚至无法弄清楚它到底是什么,我认为现在可能使用它有点超出我的范围,但我仍然有兴趣扩大我的教育并找出如何改进的方法.

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

Hen*_*pel 57

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

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

因此,不是调用一系列'mysql_doSomething()'函数,将结果存储在自己的变量中,而是从PDO类中"实例化"一个对象('class'=抽象定义,'object'=具体,可用的实例一个类)并调用该对象上的方法来做同样的事情.

举个例子,如果没有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;
}
Run Code Online (Sandbox Code Playgroud)

虽然这与使用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();
Run Code Online (Sandbox Code Playgroud)

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

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

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

您应该阅读一些OOP基础知识,以了解其他优势.

  • @YourCommonSense:OMG确实 - 令人难以置信的是,人们可以在自己的答案中忽略这些不好的例子:/改为(仍然简化)但希望更可接受的版本. (3认同)

Nat*_*ong 40

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

查询有多个部分

可以这样想:当你向数据库提出查询时,你会告诉它几个不同的东西.有一件事可能是,例如,"我希望你做一个选择." 另一个可能是"将其限制为行,其中用户名是以下值."

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

'SELECT * FROM transactions WHERE username=$username'
Run Code Online (Sandbox Code Playgroud)

当它获得该字符串时,它必须解析它并决定"这是一个SELECT带有WHERE".

使零件混乱

假设恶意用户将其用户名输入为billysmith OR 1=1.如果您不小心,可以将其放入字符串中,从而导致:

'SELECT * FROM transactions WHERE username=billysmith OR 1=1'
Run Code Online (Sandbox Code Playgroud)

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

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

保持部件笔直

如果只有它早知道会发生什么,即,一SELECT,其WHERE只有一个条件下,恶意用户可能不会欺骗它.

通过准备好的声明,您可以给予正确的期望.您可以告诉数据库"我即将发送给您SELECT,并且它将限制为WHERE username =我要给您的字符串行.这就是全部 - 查询没有其他部分.您准备好了吗?好的,这里有与用户名比较的字符串."

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

准备好的陈述的其他好处

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

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

对于另一种解释,见西奥的答案在这里.


Cor*_*use 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();
?>
Run Code Online (Sandbox Code Playgroud)

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

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

SELECT name FROM user WHERE id = ? AND admin = ? 
Run Code Online (Sandbox Code Playgroud)

SELECT name FROM user WHERE id = mysql_real_escape_string($id) AND admin = mysql_real_escape_string($admin);
Run Code Online (Sandbox Code Playgroud)

第三,您不必确保正确引用参数.PDO负责这一点.例如,mysql_real_query_string:

SELECT * FROM user WHERE name = 'mysql_real_escape_string($name)' //note quotes around param
Run Code Online (Sandbox Code Playgroud)

VS

SELECT * FROM user WHERE name = ?
Run Code Online (Sandbox Code Playgroud)

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


kni*_*ttl 14

想象你写的东西是这样的:

$query = 'SELECT * FROM table WHERE id = ' . mysql_real_escape_string($id);
Run Code Online (Sandbox Code Playgroud)

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

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