web-dev-qa-db-ja.com

mysql_real_escape_stringよりもMySQLクエリ/クエリ文字列のエスケープにPDOが優れているのはなぜですか?

MySQLのエスケープには、mysql_real_escape_stringよりもPDOを使用した方が良いと言われました。

たぶん、私は脳死の日を過ごしています(または、それは自然なプログラマーの想像力のストレッチがなく、PHPに関してはまだ初心者の段階にいるという事実かもしれません) PHPマニュアルを読んで PDOのエントリ を読んで、PDOが実際に何であるか、そしてmysql_real_escape_stringを使用するよりも優れている理由についてはまだ明確ではありません。 OOPの複雑さをまだ理解できていないからです(OOPと関係があると思います)が、変数と配列の値がそれらの前にコロンがありますが、実際にそれが何であり、どのように使用するのかまだわかりません(そしてmysql_real_escape_stringよりも優れている理由も。(それは私が本当に明確ではないという事実と関係があるかもしれません「クラス」とは何かを理解しているので、「PDOクラス」を読んだとき、私は本当に賢い人ではありません)。

MySQL Webサイトの 'Developer Zone'ビットで 記事 または2を読んだので、私はまだ明確ではありません。現時点でそれが何であるかさえ理解できないので、おそらくそれを使用することは今私を少し超えていると思いますが、私はまだ教育を広げて、物事を改善する方法を見つけることに興味があります。

誰も私に「プレーン英語」でPDOが何であるかを説明することができますか(またはプレーン英語で書かれた主題に関する何かの方向を教えてください)、それをどのように使用しますか?

48
BlissC

現在の答えは詳細になりますが、あなたの質問はより一般的な概要を目指しているので、試してみましょう。

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;
}
_

これは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データベースと通信する必要がある場合は、new PDO()のインスタンス化呼び出しで_mysql:_から_pgsql:_のみを変更します。古い方法では、すべてのコードを調べて、すべての「mysql_doSomething()」関数を対応する「pg_doSomthing()」関数に置き換えなければなりません(常にパラメーター処理の潜在的な違いをチェックします)。サポートされている他の多くのデータベースエンジンについても同様です。

それで、あなたの質問に戻るために、PDOは基本的に同じことを達成するための異なる方法を提供する一方で、いくつかのショートカット/改善/利点を提供します。たとえば、使用しているデータベースエンジンに必要な適切な方法で、エスケープが自動的に行われます。また、パラメータの置換(例に示されていないSQLインジェクションを防止)がはるかに簡単で、エラーが発生しにくくなります。

some OOP basics を読んで、他の利点を理解してください。

57
Henrik Opel

私はPDOにあまり精通していませんが、「準備済みステートメント」とエスケープされた文字列には違いがあります。エスケープは許可されていない文字列を削除するについてですが、準備されたステートメントはデータベースにどのようなクエリを期待するかを伝えるについてです。

クエリには複数の部分があります

このように考えてください。データベースにクエリを与えると、いくつかの別個のことを伝えていることになります。たとえば、「選択してほしい」などです。別の例としては、「ユーザー名が次の値である行に制限する」ことがあります。

クエリを文字列として作成し、データベースに渡すと、完成した文字列を取得するまで、どちらの部分も認識しません。あなたはこれをするかもしれません:

'SELECT * FROM transactions WHERE username=$username'

その文字列を取得したら、それを解析し、「これはSELECTWHEREである」と判断する必要があります。

部品を混同する

悪意のあるユーザーがbillysmith OR 1=1としてユーザー名を入力したとします。気をつけなければ、それを文字列に入れて、次のような結果になるかもしれません:

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

...これはすべてのユーザーのすべてのトランザクションを返します。1は常に1に等しいためです。おっと、ハッキングされました!

何が起こったの? データベースはクエリでどの部分を期待するかを知らなかったので、文字列を解析しただけです。 WHEREORがあり、それを満たせる2つの条件があることに驚かなかった。

部品を真っ直ぐに保つ

何を期待する、つまりSELECTの条件が1つだけであるWHEREしかわからなかった場合、悪意のあるユーザーはそれをだますことはできませんでした。

準備されたステートメントを使用すると、その正しい期待を与えることができます。データベースに「SELECTを送信しようとしていますが、行WHERE username =に制限されます。これは、これから提供する文字列です。それだけです。クエリの他の部分はありません。準備はいいですか?OK、ユーザー名と比較する文字列が来ます。」

その期待で、データベースはだまされません:username列が実際の文字列 'billysmith OR 1 = 1。'を含む行のみを返します。そのユーザー名、何も返しません。

準備済みステートメントのその他の利点

セキュリティ上の利点に加えて、準備されたステートメントには速度上の利点がいくつかあります。

  • これらは異なるパラメーターで再利用できます。これは、データベースが基本的にあなたが要求しようとしていることを既に知っているので、新しいクエリを最初から作成するよりも速いはずです。既に「クエリプラン」を構築しています。
  • 一部のデータベース(Postgresは1つだと思います)は、準備されたステートメントを取得するとすぐに-使用するパラメーターを実際に送信する前に、クエリプランの作成を開始します。そのため、最初のクエリでも高速化が見られる場合があります。

別の説明については、Theoの回答 here を参照してください。

40
Nathan Long

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

SELECT * FROM user WHERE name = ?

最後に、PDOを使用すると、PHPデータ呼び出しを変更することなく、アプリを別のデータベースに移植できます。

16
Cory House

次の行に沿って何かを書くと想像してください。

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

これは、$ idが1 OR 1=1そして、テーブルからすべてのレコードを取得します。 $ idを正しいデータ型にキャストする必要があります(その場合はint)

pdoにはもう1つの利点があり、それはデータベースバックエンドの互換性です。

14
knittl

Mysql_real_escape_stringよりもMySQLクエリ/クエリ文字列のエスケープにPDOが優れているのはなぜですか?

「エスケープ」だけでは意味がないからです。
さらに、それは異なっています比較できない問題。

エスケープの唯一の問題は、誰もが間違っている何らかの「保護」と想定していることです。
「クエリを保護しました」という意味で「変数をエスケープしました」とみんなが言います。
一方、単独でのエスケープは保護とはまったく関係ありません。

データをエスケープして引用したの場合、大まかに保護を達成できますが、たとえば識別子(どこかでPDOと同様)には適用できません。

したがって、答えは次のとおりです。

  • PDOは、バインドされた値のエスケープを行うときに、エスケープするだけでなく、引用も適用します。そのため、より良い方法です。
  • 「エスケープ」は「保護」の同義語ではありません。 「エスケープ+クォート」は大体です。
  • ただし、一部のクエリパーツでは、両方のメソッドが適用できません。
3

SQLインジェクションの防止に加えて、PDOではクエリを1回準備して複数回実行することができます。クエリが(たとえばループ内で)複数回実行される場合、このメソッドはより効率的です(MySQLの古いバージョンでは常にそうではないように見えるので、 "should be"と言います)。また、prepare/bindメソッドは、私が使用した他の言語とほぼ一致しています。

3
Wes