未経験からエンジニア転職をするための最強ロードマップロードマップ

【PHP+SQL教材②】フォームから送られてきた値をデータベースで検索する方法やセキュリティー対策についてを徹底解説

ファド

こんにちは!
PHPのLaravelやJavaScriptでWeb開発をしているフリラーンスエンジニアのファドと申します!

こちらの記事はPHP+SQL教材の第2回目の記事になります。

その他のPHP+SQL教材を学習したい方は下記リンクから直接教材へ飛ぶことができます。

目次

FORMからデータ検索

フォルダ名:「search」

今回はHTMLのform送信でPHPで値を受け取り、受け取った値を使ってDBからデータを取得してみましょう。

以下の流れを機能を実装する予定です。
①HTMLでidをPOSTで送信
②PHPでidを受け取る
③DB(sample)のtest01テーブルから、受け取ったidをもつレコードを取得。
④取得したものを画面に出力する。

まずはindex.phpを作成し、下記の通り編集してください。

<!DOCTYPE html>
<html>

<head>
      <title>DB</title>
      <meta charset="UTF-8">
</head>

<body>
      <form action="./receive.php" method="POST">
            <h1>ID検索</h1>
            id:<input type="text" name="id">
            <input type="submit" value="検索">
      </form>
</body>

</html>

ファイルの編集が出来たら、ブラウザで確認してみましょう!
http://localhost:8888/search/ または、http://localhost/search/ にアクセスしましょう!
アクセスし、以下の画像のように表示されていればOKです!

送信フォームが完成したので、受け取る処理を作成しましょう。

searchフォルダ内に、commonsフォルダを作成し、dbconnect.phpを作成しましょう。

前回同様に、dbconnect.phpにDBと接続する処理を書いていきます。

dbconnect.phpを下記の通り編集してください。

<?php
function dbconnection()
{
      global $pdo, $e;
      try {
            // データベースに接続
            $pdo = new PDO(
                  'mysql:dbname=sample;host=localhost;charset=utf8mb4', //dbname=で参照DB名を切り替えられます。
                  'root', //初期設定ではユーザー名は「root」になっています。
                  'root', //初期設定ではパスワードは「root」もしくは「''(空文字)」になっています。「'root'」でうまくいかない場合は「''」で試してみて下さい。
                  [
                        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                  ]
            );
      } catch (PDOException $e) {
            //エラー発生時
            echo $e->getMessage();
            exit;
      }
}

これで、DBと接続を行うための準備が完了したので、DBから検索する処理を作成していきましょう!

receive.phpを作成し、下記の通り編集してください。

<?php
// 受け取る処理
if (isset($_POST['id'])) { // 受け取ったときのみ実行
      $input_id = $_POST['id'];

      // DB接続
      require_once("./commons/dbconnect.php");
      dbconnection();

      // 検索
      $sql = "SELECT * FROM sample.test01 WHERE id = " . $input_id . ";";
      $stmt = $pdo->prepare($sql);
      $stmt->execute();
      $data = $stmt->fetchAll(PDO::FETCH_ASSOC);

      // 表示
      var_dump($data);
}

これで、フォームから入力されたidを受け取り、DBから検索する処理が作成できました。

ここまで作成したsearchフォルダの構造は以下のようになっています。

search
├── index.html
├── receive.php
└── commons
    └── dbconnect.php

searchフォルダの配下にindex.phprecieve.phpcommonsフォルダがあり、commonsフォルダ配下にdbconnect.phpが配置されている構造になっています。

実際に動かして確認してみましょう!
http://localhost:8888/search/ または、http://localhost/search/ にアクセスしましょう!
アクセスし、今回はidが2のレコードを検索してみましょう!

画像のようにinput要素に2を入力して検索ボタンをクリックしてください。

画像のように、idが2のハナコさんのデータが取得できていればOKです。

input要素に他のidを入れてみて他のデータが取得できるか確認してみてください。

データのハッシュ化

フォルダ名:「hash」

ハッシュ化とは、ハッシュ関数によって文字列を置換して、元の文字を推測できなくすることです。

例えば通販サイトの会員登録をするときにユーザーがフォームにパスワードを入力します。
そしてPHPで受け取ったデータを、DBに保存する際にハッシュ化してから保存します。
ハッシュ化をする理由は2つあります。
・パスワードをサイトの管理者に知られないようにするため。
・DBへの攻撃によるパスワード情報の流出や不正利用を防止するため。

ハッシュ化の必要性がわかったところで、パスワードをハッシュ化する方法を学んでいきましょう!

パスワードをハッシュ化するために、password_hash関数を使います。
使い方は以下のとおりです。

password_hash(ハッシュ化したいデータ , アルゴリズムの指定 , オプションの指定(任意))

使い方がわかったところで、文字列をハッシュ化すると、どのような形になるのか確認してみましょう!
index.phpを作成し、以下のように編集しましょう!

<?php
var_dump(password_hash("abcdefghijklmnopqqrstu", PASSWORD_BCRYPT));

ファイルの編集が出来たら、ブラウザで確認してみましょう!
http://localhost:8888/hash/
または
http://localhost/hash/
にアクセスしましょう!
アクセスし、以下の画像のようにもとの文字列とは違った形に、変換されていればOKです!

今回学んだハッシュ化と、次に学ぶ暗号化をうまく使って、ユーザーのデータが不正利用されないようにしてあげましょう!

データの暗号化

フォルダ名:「encrypted」

暗号化とは
元のデータに対して処理を行い、別データに変換する処理のことです。
それに対して暗号化されたデータを元のデータに戻す処理のことを復号化と言います。

暗号化や復号化の処理にはアルゴリズムに応じたが必要になります。
同じ暗号アルゴリズムを採用しても、が違っていると、処理の結果が変わります。

暗号アルゴリズムについて簡単に説明すると、どのような方法で暗号化を行うかということです。

今回の暗号化にはopensslを使います。

index.phpを作成し、以下のように編集しましょう!

<?php
// 暗号化するデータ
$str = "test@test.com";

// 暗号化パスワード
$password = 'abcdefg';

// 暗号化方式
$method = 'aes-256-cbc';

// 方式に応じたIVに必要な長さを取得 ランダムな文字列
$ivLength = openssl_cipher_iv_length($method);

// IV を自動で生成
$iv = openssl_random_pseudo_bytes($ivLength);

// OPENSSL_RAW_DATA と OPENSSL_ZERO_PADDING を指定可
$options = 0;

// 暗号化
$encrypted = openssl_encrypt($str, $method, $password, $options, $iv);
echo "<br>";
echo $encrypted;

ファイルの編集が出来たら、ブラウザで確認してみましょう!
http://localhost:8888/encrypted/
または
http://localhost/encrypted/
にアクセスしましょう!
アクセスし、以下の画像のようにもとの文字列とは違った形に、変換されていればOKです!

復号化

復号化とは、暗号化されているデータをもとの形に戻すことです。
先程も少し触れましたが、暗号化は復号化(もとに戻す)ことが出来ます!
もとに戻すには、暗号化した際に使用したパスワードを使用するので、それを知っている必要があります。

では早速、先程暗号化したデータを復号化してみましょう!

index.phpを以下のように編集してください。

<?php
// 暗号化するデータ
$mail = "test@example.com";

// 暗号化パスワード
$password = 'secpass';

// 暗号化方式
$method = 'aes-256-cbc';

// 方式に応じたIVに必要な長さを取得 ランダムな文字列
$ivLength = openssl_cipher_iv_length($method);

// IV を自動で生成
$iv = openssl_random_pseudo_bytes($ivLength);

// OPENSSL_RAW_DATA と OPENSSL_ZERO_PADDING を指定可
$options = 0;

// 暗号化
$encrypted = openssl_encrypt($mail, $method, $password, $options, $iv);
echo $encrypted;

// 復号化(ここを追記)
$decrypted = openssl_decrypt($encrypted, $method, $password, $options, $iv);
echo "<br>";
echo $decrypted;

ファイルの編集が出来たら、ブラウザで確認してみましょう!
http://localhost:8888/encrypted/ または、http://localhost/encrypted/ にアクセスしましょう!
アクセスし、以下の画像のように暗号化されたデータと復号化されたデータが表示されていればOKです!

ハッシュ化と暗号化の違い

ハッシュ化と暗号化の使い方について学んだところで、ハッシュ化と暗号化はどのような違いがあるのかについて学んでいきましょう!

ハッシュ化は一度変換してしまうと元に戻せませんが、暗号化はがあれば元に戻すことが出来ます。

以下のようなイメージで使い分けます。

・メールアドレスは 個人情報であるが、サイトから案内メールを送信する際に必要となるので暗号化する
・パスワードは ユーザーのみが知っていればよいのでハッシュ化する
というように使い分けます。

メールアドレスは暗号化、パスワードはハッシュ化というように覚えてしまってOKです!

暗号化参考サイト
https://qiita.com/asksaito/items/1793b8d8b3069b0b8d68

SQLインジェクション

フォルダ名:「search」
今回は、FORMからデータ検索で作成したソースコードをもとに説明していきます。

SQLインジェクションとは「不正なSQLの命令を、攻撃対象のウェブサイトに注入する(インジェクションする)」ことです。
つまりハッキング(厳密に言うとクラッキング)の一種です。
過去多くの企業がSQLインジェクションによる攻撃を受け、莫大な被害を被っています。
webサービスを作る上でSQLインジェクションへの対策は必須なので、学んでいきましょう。

原理としては簡単です。
以下のコードを見てください。

receive.phpを下記の通り編集してください。

// 検索
$sql = "SELECT * FROM sample.test01 WHERE id = " . $input_id . ";";

こちらはフォームで入力されたidをSQL文に入れて、DBからidを指定してデータの検索を行う処理です。

例えば$input_id の中身が2であったときには

SELECT
    *
FROM
    sample.test01
WHERE
    id = 2;

というSQL文を実行することになります。

2のところは、ユーザーがフォームで入力したものが入るので2 OR trueというようにフォームに入力し検索すると

その場合には

SELECT
    *
FROM
    sample.test01
WHERE
    id = 2
    OR true;

というSQL文を実行することになります。

こちらを実行してみましょう。

画像のようにフォームに2 or trueが入力できたら、検索ボタンをクリックしましょう!

そうすると

画像のように、DBにあるすべてのデータが取得できてしまします。

こちらを実行したとしても、sampleの中に登録されているデータはレコード5つ程度なので、被害は全くありませんが、もっと規模の大きいwebサービスでは何千万ものレコードを持つサービスもあります。
そのサービスで上記のようなSQLインジェクションを引き起こさせると、サーバーが停止してしまったり、莫大な損害を与えてしまします。

さらに、被害が大きくなるようなSQLインジェクションも存在します。

データを全件検索するだけでなく、テーブルを削除することも、データを改竄することもできます。

では実際に実行してみましょう。
(他のサイトで試すと犯罪になります。必ず学習環境でのみ実行してください。)

以下のSQLを実行したいので

SELECT
    *
FROM
    sample.test01
WHERE
    id = 2;
DROP TABLE sample.test01;

入力欄には
2; DROP TABLE sample.test01

と入力すればOKですね。

そして検索を押すと、
SELECTの後にDROP処理が実行されます。
phpMyAdminを見てみましょう。

test01テーブルが削除されてしまっていることが確認できると思います。

これがもし、実際のサービスのデータだったら非常に恐ろしいですよね。
次からはこの対策をやっていきます。

ちなみに消してしまったtest01テーブルとそのデータは以下SQLで作り直しておきましょう。

CREATE TABLE sample.test01 (
    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    gender VARCHAR(255) NULL,
    code INT NOT NULL
);

INSERT INTO
    sample.test01 (id, name, gender, code)
VALUES
    (1, 'タロウ', '男性', 1),
    (2, 'ハナコ', '女性', 1),
    (3, 'イチロウ', '男性', 2),
    (4, 'キョウコ', '女性', 2),
    (5, 'タケシ', '男性', 3);

SQLインジェクションの対策

フォルダ名:「search」
今回も、FORMからデータ検索で作成したソースコードをもとに説明していきます。

SQLインジェクションの恐ろしさについて学んだところで、今回はその対策について学んでいきましょう!
SQLインジェクションは対策を行うことができるので、しっかりと学んでいきましょう!

対策としては
SQL文の組み立てをプレースホルダで実装する。
というものがあります。

プレースホルダーとは
変数のように値が変動する箇所データベースへ登録する処理の間に挟むことで変動する箇所をとして処理するしくみです。入力フォームに万が一不正な値(SQL文)が入力されても、SQL命令に関わるような特殊文字は無効化されるため、SQL文として実行されることはありません。

プレースホルダを使うための手順は2つです。
①SQL文の変動箇所をプレースホルダ(:で始まる代替文字列)で指定する。
②bindValueで実際の値をプレースホルダにバインドする。

②にはbindValueというメソッドを使います。
使用方法は以下のとおりです。

bindValue('プレースホルダ名', '実際にバインドするデータ', 'データの型');

第3引数の型の指定は、あらかじめ用意されている設定値から選択します。
よく使うのは以下のような型です。

文字列型の場合:PARAM_STR
数値型の場合:PARAM_INT
ラージオブジェクト(画像データなど)の場合:PARAM_LOB
NULLの場合:PARAM_NULL

では実際に、プレースホルダを使ったSQL文の組み立てを実装していきます。

receive.phpを以下のように編集しましょう。

$sql = "SELECT * FROM sample.test01 WHERE id = :input_id";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':input_id', $input_id, PDO::PARAM_INT);
$stmt->execute();

今回はidで検索を行っているので、PARAM_INTを使用してバインドを行っています。

実装が完了したら実際にSQLインジェクション対策が行われているかを確認しましょう。

2 OR TRUE

と入力しても全てのデータを取られることはありません。

2; DROP TABLE sample.test01

と入力しても、テーブルは消えません。

PHPでユーザー入力のあるSQL文を書く場合はプレースホルダを必ず実装しましょう。

また、セキュリティ対策については以下のURLが参考になりますので、
目を通しておきましょう。
https://blog.codecamp.jp/php_security

おめでとうございます!

PHP+SQLの教材を最後までお読みいただき、ありがとうございました!

次は学習した内容の確認としてPHP+SQLの課題に挑戦してみましょう!

PHP+SQL課題

さあ、これまでの学習の成果を存分に発揮させましょう!

あわせて読みたい
【PHP+SQL課題】PHP+SQLを学んだあとの腕試し問題を公開!【超有料級】 今回はPHP+SQLの課題に挑戦していただきます! まだPHP+SQLの教材を読んでいない方は、下記リンクより学習してください! https://fadotech.com/texts/ 課題の解答につ...

プログラミング学習サポートについて

「独学で挫折した。。。」

「一人でのプログラミング学習がしんどい。。。」

「未経験からエンジニア転職をしたいけど何をしたら良いかわからない。。。」

このような悩みをお持ちの方向けに、本教材作成者のファドがMENTAという学習サイトにてあなたのプログラミング学習とエンジニア転職を徹底サポートいたします!

サポート価格はなんと1日あたりたったの約300円!

教材で分からない箇所のサポートはもちろんのこと、本サイトで公開しているすべての課題の解答も公開しております。
また、MENTAで学習を終わらせていただいた方限定で懇意にしていただいている企業さんを紹介することもあります!

なお、サポート内容の詳細は下記の通りです。

  • 目標設定
  • マインドセット
  • オリジナル教材見放題
  • オリジナル課題見放題
  • オリジナル課題の解答見放題
  • 課題コードレビュー
  • 教材への無制限質問
  • 課題への無制限質問
  • ポートフォリオ作成アドバイス
  • 褒めのコーチング

いくつかのプランを用意させていただいておりますので、下記より一度ご覧ください!

あわせて読みたい
【プログラミング学習】1日あたりたったの「300円」のみでプログラミングを学び、最短で転職or稼ぐ!|【ME... プログラミングを学びたいすべての方へこれからの時代は格安でプログラミングを学び、最短で稼げ自己紹介はじめまして!この度はプランをご覧いただき、誠にありがとうござ...

コメント

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

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

    コメント

    コメントする

    目次