MySQL クエリ/クエリ文字列のエスケープには、mysql_real_escape_string よりも PDO の方が優れているのはなぜですか?
-
20-09-2019 - |
質問
使った方が良いと言われました PDO
MySQL エスケープではなく、 mysql_real_escape_string
.
もしかしたら、私は頭が死んでいるような一日を過ごしているかもしれません (あるいは、私が想像力を超えて生来のプログラマーであり、PHP に関してはまだ初心者の段階にあるという事実かもしれません)。 PHPマニュアルを調べて読みました PDO のエントリ, 、PDO が実際に何なのか、そしてなぜそれが使用するよりも優れているのかについてはまだ明確ではありません。 mysql_real_escape_string
. 。これは、私がまだ OOP の複雑さを実際には理解していないためかもしれません (OOP と関係があると推測しています)。ただし、変数と配列の値の前にコロンがあるように見えるという事実を除けば、それが実際に何なのか、そしてそれをどのように使用するのか(そしてなぜそれがより優れているのか)はまだわかりません。 mysql_real_escape_string
. 。(これは、私が「クラス」とは何かを実際には明確に理解していないという事実とも関係があるかもしれません。そのため、「PDO クラス」を読んでも、実際にはそれほど賢明ではありません。)
読んだ上で 記事 MySQL Web サイトの「開発者ゾーン」部分に 1 つまたは 2 つありますが、私にはまだ明確ではありません。現時点ではそれが何なのかさえ理解できないので、おそらくそれを使用することは今の私には少し無理だと思いますが、それでも知識を広げて、どうすれば改善できるかを見つけることに興味があります。
誰か、PDO とは何かを「わかりやすい英語」で説明していただけませんか (または、わかりやすい英語で書かれたこの主題に関する何かの方向性を教えてください)。また、それをどのように使用するか教えていただけますか?
解決
あなたの質問は、より一般的な概要を狙っている間、現在の答えは詳細に入ると、私はそれを試してみるよ。
PDOクラスは、データベースと対話するために必要なすべての機能をカプセル化することを目指しています。彼らは「メソッド」(機能のためのOOパーラー)と「プロパティ」(変数に対するOOパーラー)を定義することによってこれを行います。あなたがデータベースに話をするために現在使用されているすべての「標準」の機能のためのの完全な交換のようにそれらを使用すると思います。
だからではなく、「mysql_doSomething()」のシリーズを呼び出す機能、独自の変数にその結果を格納、あなたが希望「インスタンス化」PDOクラスからオブジェクト(「クラス」=抽象定義、「オブジェクト」=コンクリート、クラスの使用可能なインスタンス)と同じことを行うために、そのオブジェクトのメソッドを呼び出す。
例として、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:
to new PDO()
を変更すると思います。古い方法では、あなたは彼らの「pg_doSomthing()」の対応(常にパラメータ処理における電位差のチェック)ですべての「mysql_doSomething()」関数を置き換え、すべてのコードを通過する必要があると思います。同じことは、他の多くのサポートされているデータベースエンジンのケースになります。
だから、戻ってあなたの質問に取得するには、いくつかのショートカット/改善/利点を提供しながら、PDOは基本的には、あなたに同じことを達成するための別の方法を提供します。たとえば、エスケープは、使用しているデータベースエンジンのために必要な適切な方法で自動的に起こるでしょう。また、パラメータ置換が、それはあまりエラーが発生しやすくなって、はるかに簡単です(例には示されていない、SQLインジェクションを防ぐ)。
あなたは、いくつかのOOPの基本は、他のアイデアを得るためにを上に読んでください長所ます。
他のヒント
私は PDO についてはあまり詳しくありませんが、「準備されたステートメント」とエスケープ文字列には違いがあります。逃げるということは、 許可されていない文字列を削除する クエリからのものですが、準備されたステートメントについては、 どのような種類のクエリが予想されるかをデータベースに伝える.
クエリには複数の部分があります
このように考えてください:データベースにクエリを与えると、データベースにいくつかの別々のことを伝えることになります。一つのことは、たとえば、「私はあなたにセレクトをしてほしい」かもしれません。もう1つは、「ユーザー名が次の値である行に制限する」ことです。
クエリを文字列として作成してデータベースに渡す場合、データベースは完成した文字列を取得するまで、どちらの部分についても知りません。次のようにすることもできます。
'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
, 、それを満たすことができる2つの条件があります。
パーツをまっすぐに保つ
それが分かっていれば 何を期待します, 、つまり、 SELECT
だれの WHERE
条件が 1 つだけあるため、悪意のあるユーザーがそれをだますことはできませんでした。
準備されたステートメントを使用すると、適切な期待を与えることができます。データベースに「これから送信します」と伝えることができます。 SELECT
, 、行に制限されます WHERE username =
これからあげる文字列。これですべてです。クエリには他の部分はありません。準備はできたか?OK、ユーザー名と比較する文字列が来ます。」
この期待があれば、データベースはだまされません。行のみが返されます。 username
列には、実際の文字列「ビリスミスまたは1 = 1」が含まれています。誰もそのユーザー名を持っていない場合、それは何も返されません。
準備されたステートメントのその他の利点
セキュリティ上の利点に加えて、プリペアド ステートメントには速度に関する利点がいくつかあります。
- これらはさまざまなパラメーターを使用して再利用できます。データベースは、何を要求しようとしているのか基本的にすでに知っているため、新しいクエリを最初から作成するよりも高速になるはずです。すでに「クエリ計画」を構築しています。
- 一部のデータベース (Postgres もその 1 つだと思います) は、プリペアド ステートメントを取得するとすぐに、使用するパラメーターを実際に送信する前に、クエリ プランの作成を開始します。したがって、最初のクエリでも高速化が見られる場合があります。
別の説明については、Theo の回答を参照してください。 ここ.
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();
?>
整数(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のデータの呼び出しを変更することなく、別のDBにアプリポートにできます。
あなたはの線に沿って何かを書く想像します:
$query = 'SELECT * FROM table WHERE id = ' . mysql_real_escape_string($id);
$ idは1 OR 1=1
することができ、あなたがテーブルからすべてのレコードを取得するため、これは、注射からあなたを救うことはありません。あなたは適切なデータ型に(その場合、int)を$ IDをキャストする必要があると思います。
PDOは別の利点を有し、それはデータベースバックエンドのインターチェンジである。
SQLインジェクションを防ぐことに加え、PDOは、一度クエリを準備し、それを複数回実行することができます。あなたのクエリが(ループ内で、例えば)を複数回実行された場合(つまり、常に古いバージョンのMySQLの場合ではないように見えるので、私は「あるべき」と言う)、この方法は、より効率的でなければなりません。準備/バインド方法は、また、私が働いている他の言語に沿って、より多くのです。
MySQL クエリ/クエリ文字列のエスケープには、mysql_real_escape_string よりも PDO の方が優れているのはなぜですか?
ただ「逃げる」だけでは意味がないからです。
さらに、それは違います 比類のない 重要です。
逃げる場合の唯一の問題は、 誰もがそれを誤解しており、 それをある種の「保護」とみなします。
誰もが「変数をエスケープした」と言いますが、これは「クエリを保護した」という意味です。
その間 一人で逃げることは保護とはまったく関係ありません。
保護はおおよそ次の場合に達成できます。 私は逃げて自分のデータを引用しました, ただし、これは、たとえば識別子など、どこにでも適用できるわけではありません (ちなみに、PDO も同様です)。
したがって、答えは次のとおりです。
- PDO は、バインドされた値をエスケープするときに、エスケープだけでなく引用符も適用します。これが、PDO の方が優れている理由です。
- 「逃げる」ことは「守る」ことと同義ではありません。ざっくり言うと「エスケープ+引用」です。
- ただし、一部のクエリ部分では両方の方法が適用できません。