PHP - WEB - 14. リクエストパラメータの検証

ここからはWebアプリケーションのセキュリティについて学習していきます。一般的なWebアプリケーションはWebブラウザによって操作するため同時に多数のユーザが利用できます。Webアプリケーションにアクセスするユーザの中には悪意のあるユーザも存在する可能性があります。Webアプリケーションに脆弱性があると機密情報が漏洩したり、データが不正に操作されたり、他にも利用者のなりすましなど、様々な被害が発生します。Webアプリケーションを運営する企業において、これらの被害が発生すると社会的な信用を大きく落とすことにもなりかねません。

ここではPHPでWebアプリケーションを開発する上での脆弱性を作らないように、いくつかの観点からアプリケーションのセキュリティについて見直していきます。

PHPプログラムの開発(入力データの検証)

Webアプリケーションでは入力フォームを利用してユーザからの入力を処理することが頻繁にあります。そのためユーザの入力が妥当なものか検証するために入力チェックを実装することが必要になります。ここでは以前に作成した検索画面( search.html )と検索ボタンをクリックしたときの検索処理( search.php )を例に、PHPにおける入力チェックについて考察します。

それでは順にプログラムを確認していきましょう。まずは検索画面( search.html )です。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>PHP Sample</title>
</head>
<body>
    <h3>Search</h3>
    <hr>
    <form action="search.php" method="get">
        <input type="text" name="name">
        <input type="submit" value="search">
    </form>
    <a href="search.php?name=a">Search: a</a>
</body>
</html>

このプログラムは以前に作成したものと変わりはありません。画面にはGETリクエストを送信する入力フォームがあり、名前を入力するテキストボックスと検索ボタンを表示します。

続いて検索処理( search.php )です。入力データのやりとりを確認するためにプログラムの先頭部分にプログラムを追加します。

<?php
$name = $_GET["name"];
var_dump($name);
die("debug");

$names = file("names.txt", FILE_IGNORE_NEW_LINES);
$searched_names = [];
if ($name !== "") {
  for ($i = 0; $i < count($names); $i++) {
    if (strpos($names[$i], $name) !== false) {
      $searched_names[] = $names[$i];
    }
  }
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>PHP Sample</title>
</head>
<body>
  <h3>Search</h3>
  <hr>
  <ul>
  <?php
    for ($i = 0; $i < count($searched_names); $i++) {
  ?>
    <li><?php echo $searched_names[$i]; ?></li>
  <?php
    }
  ?>
  </ul>
</body>
</html>

ここではプログラムの修正部分を確認しておきましょう。

<?php
$name = $_GET["name"];
var_dump($name);
die("debug");

このプログラムでは先頭部分で $_GET["name"] の値を $name 変数に代入しています。今回はユーザの入力データについて詳しく検証するために var_dump 関数を使って実際にユーザの入力を画面に出力するように修正しています。またその後に die 関数を呼び出すことでプログラムを終了させるようにしています。このように処理の途中で die 関数を呼び出すと、以降の処理は実行されないようになります。

プログラムの修正については以上です。まずはこれまでのプログラムの動作確認のためにビルトインWebサーバを起動しましょう。

$ php -S localhost:8080

続いてWebブラウザから検索画面( search.html )にアクセスします。アドレスバーに以下のURLを入力します。

https://〜.vfs.cloud9.ap-northeast-1.amazonaws.com/search.html

まずは正常な入力パターンとしてテキストボックスに A という文字を入力して検索ボタンをクリックしてみましょう。

そうすると上記のように var_dump 関数の出力によって $name には "A" という文字列( string )データが代入されていることがわかります。またこのとき、アドレスバーに表示されるURLが次のようになっている点を確認していきましょう。

https://〜.vfs.cloud9.ap-northeast-1.amazonaws.com/search.php?name=A

GETリクエストで送信された入力フォームの値はこのようにURLの一部(クエリパラメータ)として表示されるようになります。

ここまでで修正したプログラムの動作確認ができたので、次にユーザの不正な入力パターンとして次の3つのケースを考察してみましょう。

  1. $_GET 変数に "name" キーが存在しない場合
  2. $_GET 変数に "name" キーは存在するが値が空の場合
  3. $_GET 変数に "name" キーが配列形式の場合

1. $_GET 変数に "name" キーが存在しない場合

まずは「 $_GET"name" キーが存在しない」というケースです。このような状態を実現するには検索画面のテキストボックスを空にするのではなく、アドレスバーから検索処理のURLを直接指定するようにします。

https://〜.vfs.cloud9.ap-northeast-1.amazonaws.com/search.php

そうすると画面には次のようなメッセージが表示されるでしょう。

このメッセージはPHPの中でNotice(注意)に分類されるものですので、致命的な状況ではありませんが、このような想定外のケースをそのままにしておくのはあまり好ましくありません。

php.ini ファイルの設定によってはNoticeメッセージが表示されないこともあります。その場合は php.ini ファイルの diplay_errors 項目や error_reporting 項目の設定を確認してください。

2. $_GET 変数に "name" キーは存在するが値が空の場合

次に「 $_GET"name" キーは存在するが値が空」というケースです。このような状態を実現するには検索画面のテキストボックスを空の状態で検索ボタンをクリックするか、アドレスバーから以下のURLに直接指定するようにします。

https://〜.vfs.cloud9.ap-northeast-1.amazonaws.com/search.php?name=

実際にアクセスすると画面には次のような結果が表示されるでしょう。

var_dump 関数によって空文字が出力されているのがわかります。このようなケースは想定外というわけではありませんが、クエリパラメータにキーのみが存在する(値が存在しない)場合は $_GET から取得した値は "" 空文字になると理解しておくようにしましょう。

3. $_GET 変数に "name" キーが配列形式の場合

さいごに3つ目の「 $_GET"name" キーが配列形式」というケースです。これはイメージしにくいかもしれませんが、アドレスバーから以下のようにURLを指定することでクエリパラメータを配列で指定できます。

https://〜.vfs.cloud9.ap-northeast-1.amazonaws.com/search.php?name[]=A&name[]=B

実際にアクセスすると画面には次のような結果が表示されるでしょう。

var_dump 関数によって $name の内容を出力していますが、配列データ( array )として取得できていることがわかります。

このような仕組みは同名の複数のチェックボックスの入力を配列で受け取る、といったシーンで利用します。

ここまで3つのイレギュラーな入力パターンを確認してきましたが、いずれもユーザの操作(Webブラウザの操作)次第で発生する可能性があります。そのためWebアプリケーションを開発する場合には、このような入力パターンも想定してプログラムを記述しておく必要があります。