PHP MySQLプリペアド ステートメント
準備済みステートメントは、SQLインジェクションに対して非常に役立ちます。
プリペアド ステートメントとバインドされたパラメーター
準備済みステートメントは、同じ(または類似した)SQLステートメントを効率よく繰り返し実行するために使用される機能です。
プリペアド ステートメントは、基本的に次のように機能します。
- 準備: SQLステートメントテンプレートが作成され、データベースに送信されます。特定の値は未指定のままで、パラメーターと呼ばれます (「?」のラベルが付いています)。例: INSERT INTO MyGuests VALUES(?, ?, ?)
- データベースは、SQLステートメントテンプレートに対してクエリの解析、コンパイル、および最適化を実行し、結果を実行せずに格納します。
- 実行: 後で、アプリケーションが値をパラメーターにバインドし、データベースがステートメントを実行します。アプリケーションは、異なる値を使用してステートメントを何度でも実行できます。
SQLステートメントを直接実行する場合と比較して、準備済みステートメントには次の3つの主な利点があります。
- 準備されたステートメントは、クエリの準備が1回だけ行われるため、解析時間を短縮します(ただし、ステートメントは複数回実行されます)。
- バインドされたパラメーターは、クエリ全体ではなくパラメーターのみを毎回送信する必要があるため、サーバーへの帯域幅を最小限に抑えます
- 後で別のプロトコルを使用して送信されるパラメーター値を正しくエスケープする必要がないため、準備済みステートメントはSQLインジェクションに対して非常に役立ちます。元のステートメント テンプレートが外部入力から派生していない場合、SQLインジェクションは発生しません。
MySQLiのプリペアドステートメント
次の例では、MySQLiで準備済みステートメントとバインドされたパラメーターを使用しています。
例 (プリペアドステートメントを使用するMySQLi)
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// prepare and bind
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);
// set parameters and execute
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();
$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();
$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute();
echo "New records created successfully";
$stmt->close();
$conn->close();
?>
上記の例から説明するコード行は次のとおりです。:
"INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)"
SQLでは、整数、文字列、double、またはblob値を置換する場所に疑問符(?)を挿入します。
次に、bind_param()関数を見てください。
$stmt->bind_param("sss", $firstname, $lastname, $email);
この関数は、パラメーターをSQLクエリにバインドし、パラメーターが何であるかをデータベースに伝えます。 「sss」引数には、パラメーターのデータのタイプがリストされます。s文字は、パラメーターが文字列であることをmysqlに伝えます。
引数は、次の4つのタイプのいずれかです。
- i - integer
- d - double
- s - string
- b - BLOB
パラメータごとにこれらのいずれかが必要です。
期待するデータのタイプをmysqlに伝えることで、SQLインジェクションのリスクを最小限に抑えます。
注:外部ソース(ユーザー入力など)からデータを挿入する場合は、データをサニタイズして検証することが非常に重要です。
PDOのプリペアド ステートメント
次の例では、PDOでプリペアドステートメントとバインドされたパラメーターを使用しています。
例 (プリペアドステートメントを含むPDO)
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDBPDO";
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// prepare sql and bind parameters
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email)
VALUES (:firstname, :lastname, :email)");
$stmt->bindParam(':firstname', $firstname);
$stmt->bindParam(':lastname', $lastname);
$stmt->bindParam(':email', $email);
// insert a row
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();
// insert another row
$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();
// insert another row
$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute();
echo "New records created successfully";
} catch(PDOException $e) {
echo "Error: " . $e->getMessage();
}
$conn = null;
?>
プログラミング学習を加速させる
プログラミングをプロの講師に教えてもらいませんか。