プリペアドステートメントは、SQLインジェクションに対して非常に便利です。
準備文とバインド変数
準備されたステートメントは、同じ実行するのに使用される機能である(or similar)高効率で繰り返しSQL文。
プリペアドステートメントは、基本的にこのように動作します。
- 準備:SQL文のテンプレートが作成され、データベースに送信されます。 一部の値はパラメータと呼ばれ、未指定のままにされている(labeled "?") 例:MyGuestsのINSERT INTO VALUES(?, ?, ?)
- データベースは、解析してコンパイルし、SQL文のテンプレートにクエリの最適化を実行し、それを実行せずに結果を格納します
- 実行:後の時点で、アプリケーションは、パラメータに値をバインドし、データベースが文を実行します。 それは、異なる値と望んでいるように、アプリケーションは、ステートメントを何度でも実行することができます。
直接SQL文を実行すると比較すると、プリペアドステートメントは、2つの主な利点があります:
- プリペアドステートメントは、クエリの準備が一度だけ行われているように、解析時間を短縮(although the statement is executed multiple times)
- あなたが唯一のパラメータごとに、全体ではなく、クエリを送信する必要があるとして、バウンドパラメータは、サーバへの帯域幅を最小限に抑えます
- 後で別のプロトコルを使用して送信されるパラメータ値は、正しくエスケープする必要がないため、プリペアドステートメントは、SQLインジェクションに対して非常に便利です。 元の文のテンプレートは、外部入力から導出されていない場合は、SQLインジェクションが発生することはできません。
MySQLiを内プリペアドステートメント
次の例では、MySQLiをして準備された文とバインドされたパラメータを使用します。
例(MySQLi with Prepared Statements)
<?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 = "[email protected]";
$stmt->execute();
$firstname
= "Mary";
$lastname = "Moe";
$email = "[email protected]";
$stmt->execute();
$firstname = "Julie";
$lastname = "Dooley";
$email = "[email protected]";
$stmt->execute();
echo "New records created successfully";
$stmt->close();
$conn->close();
?>
上記の例から説明するためのコード行:
"INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)"
私たちのSQLでは、私たちは疑問符を挿入(?)私たちは、整数、文字列、ダブルまたはBLOB値に置き換えたいです。
続いて、見ていbind_param()関数:
$stmt->bind_param("sss", $firstname, $lastname, $email);
この関数は、SQLクエリにパラメータを結合し、パラメータが何であるかをデータベースに伝えます。 "sss"引数は、パラメータがデータの種類を示しています。 sの文字は、パラメータが文字列であることにMySQLを伝えます。
引数には、4種類のものであってもよいです。
- I - 整数
- D - ダブル
- S - 文字列
- B - BLOB
私たちは、各パラメータのためにこれらのいずれかを持っている必要があります。
期待するデータの種類MySQLを伝えることで、私たちは、SQLインジェクションのリスクを最小限に抑えます。
注意:我々は、外部ソースから任意のデータを挿入する場合(like user input) 、そのデータがサニタイズして検証されることが非常に重要です。
PDOでプリペアドステートメント
次の例では、PDOに準備された文とバインドされたパラメータを使用します。
例(PDO with Prepared Statements)
<?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 = "[email protected]";
$stmt->execute();
// insert another row
$firstname = "Mary";
$lastname = "Moe";
$email = "[email protected]";
$stmt->execute();
// insert another row
$firstname = "Julie";
$lastname = "Dooley";
$email = "[email protected]";
$stmt->execute();
echo "New records created successfully";
}
catch(PDOException $e)
{
echo "Error: " . $e->getMessage();
}
$conn = null;
?>