PHP での SQL インジェクションの防止

Subodh Poudel 2023年1月30日
  1. PHP でプリペアドステートメントと PDO を使用しての SQL インジェクションを防止する
  2. PHP でパラメータ化されたクエリで Prepared ステートメントを使用しての SQL インジェクションを防止する
  3. PDO::ATTR_EMULATE_PREPARES 属性を false に設定して、SQL インジェクションを防止する
PHP での SQL インジェクションの防止

プリペアドステートメントと PHP データオブジェクト(PDO)を使用して、PHP での SQL インジェクションを防止する方法を紹介します。この方法では、PDO を使用してデータベース通信を確立します。このメソッドは、データとクエリを別々にデータベースサーバーに送信します。これにより、データとサーバーが混在するのを防ぎます。

プリペアドステートメントとパラメータ化されたクエリを使用して、PHP で SQL インジェクションを防止する方法を紹介します。この方法では、mysqli を使用してデータベース通信を確立します。この方法は、最初の方法と同様の動作メカニズムを備えています。対照的な点は、SQL インジェクションを防ぐために使用する mysqli 関数のみです。

プリペアドステートメントのエミュレーションを false に設定することにより、PHP で PDO を使用しながら SQL インジェクションから安全にする方法の例を示します。

PHP でプリペアドステートメントと PDO を使用しての SQL インジェクションを防止する

プリペアドステートメントを PDO と一緒に使用して、PHP での SQL インジェクションを防ぐことができます。SQL インジェクションは、データベースへの送信中にクエリとデータが混在している場合に発生します。このメソッドでは、SQL ステートメントでデータの正確な値を指定しません。代わりにプレースホルダーを使用します。このため、パラメーター化された SQL ステートメントは最初の要求としてサーバーに送信されます。これを実現するために、prepare() 関数を使用します。2 番目のリクエストのパラメータの正確な値をデータベースサーバーにバインドします。この目的のために bindValue() 関数を使用します。このようにして、最初のリクエストでプログラムを送信し、2 番目のリクエストでデータを送信します。SQL コードと一緒にデータを要求すると、ユーザーはプログラムを変更して悪意のあるコードを書き込むことができます。したがって、悪意のある SQL コードがサーバーに挿入されるのを防ぎます。

たとえば、テーブル users には次のフィールドとデータが含まれています。

+----+-----------+----------+------------+
| id | firstname | lastname | dob        |
+----+-----------+----------+------------+
|  1 | bruce     |  rose    | 1998-02-13 |
|  2 | jeff      |  james   | 2000-03-30 |
+----+-----------+----------+------------+

変数 $firstname を作成し、bruce という名前を割り当てます。変数 $sql を作成して、SELECT * FROM users WHERE firstname =:fname; というクエリを記述する。

firstname のデータの正確な値を書き込まないでください。代わりに、パラメータ:fname を使用してください。$pdo 変数を使用して、クエリ変数で prepare() 関数を呼び出します。:fname の値を変数 $firstname に置き換えます。execute() 関数を使用してステートメントを実行します。クレデンシャルがデータベースと一致する場合は、fetch() 関数で結果を確認してください。表示されている場合は、選択した行の idfirstname、および lastname を表示します。

以下の例は、プリペアドステートメントの使用法を示しています。firstname フィールドの値を変数に格納して、資格情報が一致するかどうかを確認します。代わりに変数に悪意のあるコードが含まれている場合は、資格情報が一致しませんというメッセージが表示されます。これは、prepared() 関数がパラメーター化されたクエリのみを受け取り、正確なデータを許可しないためです。$pdo 変数には、データベース接続のオブジェクトが含まれています。

サンプルコード:

# php 7.*
<?php
$firstname = "bruce";
$sql = "SELECT * FROM users WHERE firstname =:fname ;";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(":fname", $firstname);
$stmt->execute();
if(!$result = $stmt->fetch(PDO::FETCH_OBJ)){
    echo "Credentials do no match";
} else {
    echo"Id: ".$result->id. " Name: ".$result->firstname." ".$result->lastname;
}
?>

出力:

Id: 1 Name: bruce rose

PHP でパラメータ化されたクエリで Prepared ステートメントを使用しての SQL インジェクションを防止する

PHP での SQL インジェクションを防ぐために、プリペアドステートメントをパラメータ化されたクエリと一緒に使用できます。mysqli() 関数のオブジェクトを使用して、データベース接続を作成します。この方法では、疑問符記号 ? を使用します。データのプレースホルダーとして。上記のメソッドとして prepare() 関数を使用します。プレースホルダー内の実際のデータをバインドするために、bind_param() 関数を使用します。この方法は、上記の方法と同様の動作メカニズムに従います。

たとえば、mysqli() 関数のオブジェクトを作成するデータベース接続を確立し、それを変数 $conn に割り当てます。jeff という名前で変数 $firstname に割り当てます。変数 $sql を作成し、クエリ SELECT * FROM users WHERE first name =?; を記述します。$conn 変数を使用して、クエリ変数で prepare() 関数を呼び出します。プレースホルダー? を置き換えます変数 $firstname を使用します。execute() 関数を使用してステートメントを実行します。get_result() 関数を呼び出して、結果を $result 変数に格納します。num_rows プロパティで行が存在するかどうかを確認し、条件が失敗した場合は exit() 関数でメッセージ No Rows を返します。fetch_assoc() メソッドを呼び出し、while ループの $row 変数に格納します。選択した行の idfirstname、および lastname を表示します。

サンプルコード:

#php 7.x
<?php
$conn = new mysqli($host, $user, $pwd, $dbName);
$firstname = "jeff";
$sql = "SELECT * FROM users WHERE firstname = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $firstname);
$stmt->execute();
$result = $stmt->get_result();
if($result->num_rows === 0) exit('No rows');
    while($row = $result->fetch_assoc()) {
    echo"Id: ".$row['id']. " Name: ".$row['firstname']." ".$row['lastname'];
}

出力:

Id: 2 Name: jeff james

PDO::ATTR_EMULATE_PREPARES 属性を false に設定して、SQL インジェクションを防止する

PDO 属性を正しく設定しないと、PDO でプリペアドステートメントを使用しても SQL インジェクションを防ぐのに十分でない場合があります。インジェクションを防ぐために、PDO::ATTR_EMULATE_PREPARES 属性を false に設定する必要があります。属性を true に設定すると、PDO は準備されたステートメントを使用せずにエミュレートするだけです。したがって、システムは SQL インジェクションに対して脆弱になります。

たとえば、変数 $pdo でデータベースへの PDO 接続を作成します。変数を使用して、setAttribute() 関数を呼び出します。属性 PDO::ATTR_EMULATE_PREPARES を false に設定します。

コード例:

#php 7.x
<?php
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
?>
著者: Subodh Poudel
Subodh Poudel avatar Subodh Poudel avatar

Subodh is a proactive software engineer, specialized in fintech industry and a writer who loves to express his software development learnings and set of skills through blogs and articles.

LinkedIn