第6章 フォームデータの扱い方

6.2 Webアプリケーションのセキュリティを考える

Webアプリケーションはインターネット上に公開してしまうと、世界中の人がアクセスできるようになります。そのため、不正な攻撃を受けるかもしれないという脅威にさらされています。Webアプリケーションの開発者はそんな脅威から自己防衛するために、セキュリティ対策に十分な注意を払う必要があります。
完璧なセキュリティを築くには深い専門知識が必要になり大変難しいですが、PHPプログラムで行える最低限の方法について本章で学習して行きましょう。

6.2.1 Webアプリケーションの危険性とその対策方法について

WebサーバでPHPプログラムが動作するとき、入力されたデータの流れに着目すると図6.2.1になります。基本的には画面から入力データを受け取り、目的の機能を動作させるための処理を行い、その結果を出力する、という流れになっています。


図 6.2.1:PHPプログラムでのデータの流れ

上記の流れを見ると、入力データは、ユーザから送られて来るデータなので、どんなデータが含まれているのか分かりません。もしかしたら不正な攻撃をしかけるための情報の可能性もあります。万が一入力データが不正な場合、それに気づかずに処理を行ってしまうと、それをブラウザで画面に出力することでユーザー側やサーバ側に多大なダメージを与えてしまう結果になってしまいます。
Webアプリケーションを開発する場合プログラムが入出力するデータについては、不正なものがないかどうか常に注意を払う必要があります。セキュリティ対策の基本として以下の2点を行います

  • 入力データのチェック:6.2.2項で詳しく学習します。
  • 出力データの無害化(サニタイズ):6.2.3項で詳しく学習します。


図 6.2.2:入力データのチェックと出力データの無害化を行う

6.2.2 フォーム画面から入力されたデータのチェックを行おう

ユーザーが利用するフォーム画面では、必ずしも正しい内容を入力してくれるとは限りません。そのためWebアプリケーション側は、フォーム画面からの入力データが不正な値ではないことを確認するために「入力値チェック」を行う必要があります。
入力値チェック自身は簡単に行える内容が多いですが、入力情報に応じて様々なパターンがあるため全ては紹介しきれません。Webアプリケーションで、よく行うチェック方法について以下に例を示します。

  • 入力データの空チェック: …いずれかの入力データがあるかどうかを確認する。
  • 入力文字制限チェック: …入力データが制限の文字数内であるかを確認する。
  • 数値データの範囲チェック: …入力数値が有効範囲内であるかを確認する。
  • 入力データの形式チェック: …入力データが求めている形式になっているか確認する。(メール、数値など)

入力値チェックを行っていないWebアプリケーションの場合、以下のような結果になってしまいます。

■入力値チェックを行わないWebアプリケーション


図 6.2.3:入力値チェックを行わない実行結果

上記の図6.2.3のように、単に受け取ったデータを出力するだけなら、出力結果がおかしくなるだけでそれほど影響はないと感じます。しかし、受け取ったデータを使った処理が多くなるとどうでしょうか。入力データが不正なままでは、後の処理が動作しなくなるのは簡単に想像ができます。このような場合のために、入力値チェックが必要になってくるのです。

では先ほどの図6.2.3のプログラムに、入力値のチェックを行ったプログラムを見てみましょう。

入力値チェックを行うプログラム

画面からの入力値が不正でないかをチェックして、状況に応じたメッセージを画面に表示できるようにします。

ソース・フォルダー: myproj_basic/ch06
パッケージ: checkForm1.php
アクセスURL:http://localhost/myproj_basic/ch06/checkForm1.php

checkForm1.php

ソース・フォルダー: myproj_basic/ch06
パッケージ: checkInputData.php
アクセスURL:checkForm1.phpからの画面遷移でアクセスされる

checkInputData.php

実行結果

解説

このプログラムはフォームからの入力データに対してチェックを行い、不正なデータではないかを確認します。確認の結果に応じて、メッセージの表示を変えることがポイントになります。

入力値を受け取ってチェックを行うcheckInputData.phpについて説明を行っていきます。
3、4行目で画面からの入力値を$_POSTを使って各変数、$nameと$ageに代入しています。

5行目では変数$messageを初期値は空で設定しています。この変数は入力値のチェックを行い、不正だったならメッセージが入っていく形になります。入力値のチェックが終わった後に、この変数が初期値の状態だと「正常な入力値」、そうでなければ「不正な入力」と判断に利用するのが目的になります。

8~10行目が名前の入力がされているかどうかのチェック処理になっています。ここでは組み込み関数の「empty」と「strlen」2つを使ってチェックを行います。empty関数は変数の中身が空なのかを判定するものですが、仕様上「0」の数値も空と判定してしまいます。そのため条件に追加してstrlen関数を使って文字数を取得して、それが0文字ではないことを確認しています。これで数値の0が入力された場合でも、empty関数の条件は満たしてしまいますが、strlen関数の文字数が0文字であることを満たさないので、正式な入力値の空判定が行えます。名前が入力されてない場合に、変数$messageにメッセージを代入します。

続いて12~17行目で年齢の入力値チェックを行っています。年齢は名前と違って数値を入力して欲しいので、名前と同じ空判定を行い、空判定を通過できたらis_numeric関数を利用して、入力値が数値なのかを確認しています。
ここでは入力値のチェックに引っかかった場合、変数$messageに「.=」を使って文字連結を行っています。ここで文字連結を行う理由は、年齢のメッセージをお名前が空だった場合のメッセージに追記したいためです。こうすることで、入力エリア毎に対してどのようなエラーだったのか表示することができます。


図 6.2.4:変数$messageの値の遷移結果

19~22行目のif文で変数$messageの値が、「空(初期値)」であるか判断しています。空なら変数$messageに入力値を使ったメッセージを代入しています。
変数$messageは5行目で初期値を空にしているので、メッセージが空である場合は入力値チェックに引っかからなかったということが判断できます。

29行目で結果を画面に表示しています。実行結果からも確認できますが、入力値に不正が合った場合は、それに関するメッセージを出力し、正しく入力されると入力値を使ったメッセージが出ることが分かります。

30行目はリンクタグを設定して入力するフォーム画面に戻れるようにしています。

ポイント
  • 入力値チェックを行うことで、不正な情報を事前に処理することができる。
入力値チェックの別の手段JavaScript

入力値チェックはPHP(サーバ側)で行えますが、PHPで行うとサーバへ必ずアクセスをすることになります。そうなるとたくさんの人に利用されるWebページでは、それだけも結構な負荷になってしまいます。そこで簡単な入力値チェック(空なのか、数値なのか、桁数が有効範囲内…)ならば、クライアント側で動作するJavaScriptを使うって行うと、サーバアクセスを発生させずに済みます。そうすると動作も速く、快適なシステムをユーザへ提供することができるようになります。
興味のある方はJavaScriptで行う、入力値チェックも調べてみてください。

6.2.3 安全にWeb画面にデータを出力しよう(データの無害化)

Webアプリケーションでフォーム画面からのデータを扱う場合に、入力値チェックだけではまだ安心とは言えません。なぜなら入力値チェックでは「入力データが空ではない」「文字制限が有効」など形式的な内容のことしか行えず、その入力の内容自体が不正ではないと判断はできないからです。
入力内容自体は入力される情報によってそれぞれ異なるため、その内容全てに対してチェック処理を設定することはほぼ不可能です。PHPではそのチェック処理ができない代わりに、データ自体を「無害化(サニタイズ)」して安全性を高める方法を取ります。

データの無害化を行う対象として、Webブラウザで動作するHTML文章やJavaScriptなどで使われる「< 、> 、”」などの記号に対して行います。

上記のように記号を別の表現に置き換えることで、データの無害化を行えます。このような本来の意味を打ち消す無害化処理を「エスケープ処理」といいます。
PHPにはこのエスケープ処理を行ってくれる関数が用意されていますので、その関数の使い方を学習してデータの無害化を行っていきます。

書式:関数を使ってエスケープ処理

htmlentitiesは変換できる記号は全て変換を行い、htmlspecialcharsは「& 、’ 、 ” 、< 、>」の5種類のみを対象に変換を行います。但し「’」と「”」は第2引数のフラグ情報で、変換を行うか行わないかを選択できるようになっています。基本的には「’」も「”」も変換して行ことが一般的になります。
この2つの関数の詳細はPHPマニュアルを参照してみてください。

凡例:関数を使ってエスケープ処理

上記の凡例のように記述すると$data内の値で、該当する記号をエスケープ処理を行ってくれます。第2引数の「ENT_QUOTES」を設定すると、「’」、「”」両方エスケープ処理の対象にしてくれます。第3引数の文字コードは当テキストでは「UTF-8」ですが、実行環境に応じて変更する必要があります。
「< 、>」のようなHTML文章で意味を持つ記号は、「HTML特殊記号」と呼ばれます。エスケープ処理を行った場合に、変換される記号の例を表6.2.1に示します。

表 6.2.1: HTML特殊記号の例一覧

上記以外にもたくさんの種類がありますので調べてみてください。

データの無害化を行う前に前項で作成したプログラムに、HTML文章を入力するとどうなるか確認してみます。


図 6.2.5:HTML文章を入力した実行結果

図6.2.5を見てもらうと分かりますが、テキストボックスが表示されています。入力値チェックでは形式的なチェックしか行えず、入力値のチェックをパスされてしまうと、プログラムでは不正なデータでも正しいものと判断するしかありません。しかし、不正を許さずに細かい部分までチェックを行ってしまうと、フォームからデータ入力が殆ど行えなくなる可能性も出てきてしまいます。最終的に出力する結果に対して、不正になる部分を無害化してから、出力してしまえば安全性を保つことができます。

では実際にエスケープ処理(データの無害化)を行ったプログラムで確認してみましょう。

出力データを無害化するプログラム

フォームからの入力データをエスケープ処理を施し、その結果をWeb画面に表示して確認します。

ソース・フォルダー: myproj_basic/ch06
パッケージ: checkForm2.php
アクセスURL:http://localhost/myproj_basic/ch06/checkForm2.php

checkForm2.php

ソース・フォルダー: myproj_basic/ch06
パッケージ: checkOutputData.php
アクセスURL:checkForm2.phpからの画面遷移でアクセスされる

checkOutputData.php

解説

このプログラムは前項のプログラムに、エスケープ処理を施したプログラムになっています。そのエスケープ処理に関して詳細に説明していきます。

前項のプログラムとの違いは3、4行目のみになります。前項では$_POSTのデータをそのまま利用して変数に代入していましたが、今回では変数に代入する前に「htmlentities関数」を使って特殊記号のエスケープ処理を行っています。これだけで出力データの無害化を行うことができます。

気づいた方もいると思いますが、実行結果の「”」の前に「\」記号が付いているのが確認できます。これはGET送信もしくはPOST送信されてくるデータに「”」または「’」が在る場合に、スーパーグローバル変数に格納する時に自動でエスケープ処理されて付く「\」記号となっています。
今回のプログラムの入力値はhtmlentities関数で、エスケープ処理を行う前に自動で「”」の変換が行われていため「\」記号がつく結果になっています。


図 6.2.6: 自動エスケープ処理

htmlentities関数を使うと該当する特殊記号は全て変換を行ってくれます。また第2引数に「ENT_QUOTES」を設定しているので、「”」と「’」も変換の対象にしてくれます。
今回の実行結果で入力した値と、エスケープ処理した文字列を見比べてみましょう。


図 6.2.7: htmlentitiesを利用してエスケープ処理

図6.2.6で示したように「<」や「>」の特殊記号を対象に変換されているのが分かります。特殊記号と変換された文字では、以下で示すようにWebブラウザでの認識に違いがあります。

  • 出力文字が「<」の場合 → Webブラウザは、タグの「<」と認識する。
  • 出力文字が「<」の場合 → Webブラウザは、文字の「<」を出力すると認識する。

今回の実行結果からも確認できますが、Webブラウザーにそのまま出力すると問題になる特殊記号を「エスケープ処理」することで、本来の意味とは異なる文字列に変換してくれるため、出力データの無害化が行えます。

NEXT>> 6.3 本章のまとめ