【基礎知識】PHPのデータベース操作の基本とSQLインジェクション対策。(PHP8技術者認定初級試験)

こんにちは、穂苅と申します。

このコラムでは、私が PHP8 技術者認定初級試験のための公式問題集(PHP8 技術者認定初級試験公式問題集)で学習した内容やポイントを取り上げて解説をしていくものです。
P173〜179のChapter 18「データベース連携」を学習してみました。

PHPを使ってSQL文を扱えるSQL(Structured Query Language)は、データベースを操作するために利用する言語です。データの検索(SELECT)、データの追加(INSERT)、データの更新(UPDATE)、データの削除(DELETE)等の操作を行うことができます。SQL文は、データを扱っている人からすると様々な場所で利用するため使っている方も多いかもしれませんが、エンジニア側は例えば、PHPを使ってSQL文を操作する事ができます。

まずはデータベースに接続をする部分ですが、公式マニュアルにサンプルコードがあります。
(参考:データベース接続 – PHP マニュアル

今回はその後の操作について詳しく学んでみました。
書籍の例題を見ると、データベースの本棚テーブル(books)からレビュースコア(review_score)が50点以上の本の情報を一覧で取得したい場合に、どういうコードが最適かというものがありました。booksテーブルの定義は以下のような形です。

カラム名カラム型説明
idint管理用の番号
titletext本のタイトル
review_scoreintレビュースコア

$sth = $dbh->prepare(‘SELECT id, title, review_score FROM books WHERE review_score
>= :review_score’);
$sth->bindValue(‘:review_score’, 50, PDO::PARAM_STR);
$sth->execute();

目次

SQLを扱う場合の注意点、SQLインジェクション脆弱性

正解の書き方を見ると、SQLのSELECT文を使ってデータベース内の検索ができるため、それで検索をかけていきます。

今回、プレースホルダというセキュリティでとても重要なものが入ってきます。これは SQLインジェクションという脆弱性を狙った攻撃を防ぐための有効な手段です。SQL インジェクションは、開発者の想定していないSQL 分を組み立ててデータベースに対して実行をさせ、データベース内の情報(ID, パスワード, 個人情報など)を盗み取るなどの攻撃ですので大変にクリティカルな被害となる可能性があります。そのため極限までこのリスクを排除することがエンジニアの責任になるのですが、それを防ぐためにプレースホルダを利用します。

ここで冒頭の回答例から離れて、SQL インジェクションを防ぐためのプレースホルダについて理解を深めてみます。
例えばこのSQL文を考えます。

SELECT user_name FROM users WHERE user_id = ‘12345’;

例えば、ログインフォームのユーザーIDを入力するところに12345と入れるとこのようなSQL文で検索がされます。usersテーブルから、user_idが12345のuser_nameを検索するというものです。PHP上は以下のようになります

$sql = “SELECT user_name FROM users WHERE user_id = ‘$user_id'”;

ここで、例えば悪意のあるユーザーがユーザー ID を「12345′ OR ‘1’=’1」と入力すると、SQL文はこのようになります。

SELECT user_name FROM users WHERE user_id = ‘12345’ OR ‘1’=’1′;

これ、 OR ‘1’=’1′ がtrueで成り立つため、すべてのユーザー情報がヒットして、本来出るべきでない情報まで流出してしまうという結果になります。
もう1つ、悪意のあるユーザーが「12345′; DELETE FROM users WHERE ‘1’=’1」というユーザーIDを入力すると、どうなるかと言うと、「;」の区切りで2つのSQL文が生成されて、SELECT文だけでなく、DELETE 文も実行されてすべてのユーザー情報が削除されてしまうことになります。つまりSQL文にするとこうなります。

SELECT user_name FROM users WHERE user_id = ‘12345’; DELETE FROM users WHERE
‘1’=’1′;

これらがあると、悪意のあるユーザーがいた場合にデータベースが操作されてしまいますので、対策を行うことが必要です。それが、プレースホルダです。ユーザーが入力する箇所にプレースホルダを示す「?」を入れておくと、SQLの構造を変化させずにユーザーの入力値を安全に扱うことができます。PHPにすると、こちらのようになります。

$sth = “SELECT user_name FROM users WHERE user_id = ?”;
// $dbhはDatabase Handleの略。PDOインスタンス変数の慣習
// $sthはStatement Handleの略。prepareされたPDOインスタンスの慣習
$sth = $dbh->prepare($sql);
$sth->bindValue(1, $user_id);

この状態で、「12345′ OR ‘1’=’1」をユーザーIDに入力してもSQLの構造が変わらないため、すべてがTrueにならず、純粋に「12345′ OR ‘1’=’1」というIDがないか検索するだけになります。
ここまで見たら、最初の回答のコードに戻ってみると、プレースホルダは「?」ではなく名前付きのプレースホルダを使っています。名前付きのプレースホルダにすることで、bindValueの順番に気を遣わなくてもいいというメリットがあるため、正しいコードということになります。

$sth = $dbh->prepare(‘SELECT id, title, review_score FROM books WHERE review_score
>= :review_score’);// prepare は、文を実行する準備を行い、文オブジェクトを返す。SQL文の基本部分が同じで値だけ
異なるような場合に効率的に利用できる。似た機能でqueryがあるがこれは変動値がない場合に使う
$sth->bindValue(‘:review_score’, 50, PDO::PARAM_STR);
// vindValue は、プリペアードステートメントで使用するSQL 文の中で、変数の値をバインドする
(プレースホルダに入力データを割り当てる)ための関数。プリペアードステートメントは、prepare
関数を使うことで使用できる。
$sth->execute();
// execute関数は、PHPの標準関数。プリペアードステートメントを実行する際に使われる関数。

参考:
PDOStatement クラス –PHPマニュアル
PDOStatement::bindValue – PHPマニュアル
定義済み定数 –PHPマニュアル

PHP8技術者認定初級試験で、正しいPHPの理解を

今回は、PHPを使ったデータベース操作について勉強してきました。
データベースを操作するということは、重要なデータを扱うことと同義です。しっかり理解をしたうえで、セキュリティにも配慮した構築が必要となります。
次回以降もPHPで学習した内容をシェアしていきますので、よろしくお願いいたします。また、PHPの学習やエンジニアになるためのマイルストーンとして、PHP試験はおすすめです。PHP8技術者認定初級試験はPHPを扱うあらゆる方にとって有益な試験になるはずですので、ご興味がある方はぜひチャレンジしてみてください。
試験ページ: PHP8 初級試験

この記事が気に入ったら
いいね または フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次