PHP 速習チュートリアル

PHP フォームバリデーション

1. PHP フォームバリデーションの重要性

PHP でフォームを処理する際は、常に セキュリティ を最優先に考えてください。

これからのセクションでは、セキュリティを意識した PHP フォームの処理方法について解説します。ハッカーやスパマーからフォームを守るためには、フォームデータに対して適切なバリデーション(入力検証)を行うことが極めて重要です。

本章で扱う HTML フォームには、必須入力項目、任意入力項目、ラジオボタン、送信ボタンなど、さまざまな入力フィールドが含まれています。

1.1 バリデーションルール

このフォームに適用するバリデーションルールは以下の通りです。

フィールドバリデーションルール
名前 (Name)必須。文字と空白のみ許可。
メールアドレス (E-mail)必須。有効なメール形式であること(@ と . を含む)。
ウェブサイト (Website)任意。入力がある場合は有効な URL であること。
コメント (Comment)任意。複数行入力フィールド(textarea)。
性別 (Gender)必須。いずれか一つを選択。

2. フォームの構成要素

まず、フォームに使用する基本的な HTML コードを確認しましょう。

2.1 テキストフィールド

名前、メール、ウェブサイトの各フィールドはテキスト入力要素であり、コメントフィールドは textarea です。

HTML コード:

名前: <input type="text" name="name">
メール: <input type="text" name="email">
ウェブサイト: <input type="text" name="website">
コメント: <textarea name="comment" rows="5" cols="40"></textarea>

2.2 ラジオボタン

性別フィールドはラジオボタンを使用します。

HTML コード:

性別:
<input type="radio" name="gender" value="female">女性
<input type="radio" name="gender" value="male">男性
<input type="radio" name="gender" value="other">その他

3. フォーム要素とアクションの設定

フォーム自体の HTML コードは以下のようになります。

<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">

フォームが送信されると、データは method="post" で送られます。ここで重要な2つの要素について説明します。

3.1 $_SERVER["PHP_SELF"] 変数とは

$_SERVER["PHP_SELF"] は、現在実行中のスクリプトのファイル名を返すスーパーグローバル変数です。

これを使用することで、送信されたフォームデータを別のページに飛ばすのではなく、同じページ(自分自身)に送ることができます。これにより、ユーザーはフォームと同じページ上でエラーメッセージを確認できるようになります。

3.2 htmlspecialchars() 関数とは

htmlspecialchars() 関数は、特殊文字を HTML エンティティに変換します。つまり、<> といった HTML 文字を <> に置き換えます。

これにより、フォーム内に HTML や JavaScript コードを注入して悪用する攻撃(クロスサイトスクリプティング / XSS 攻撃)を防ぐことができます。

4. セキュリティ警告:$_SERVER["PHP_SELF"] の脆弱性

$_SERVER["PHP_SELF"] 変数は、適切に扱わないとハッカーに悪用される恐れがあります。

もしページ内で PHP_SELF がそのまま使用されていると、ユーザーが URL の末尾にスラッシュ / に続けて XSS コマンドを入力し、実行させることが可能になります。

XSS(クロスサイトスクリプティング) とは、Web アプリケーションに見られるセキュリティ脆弱性の一種です。攻撃者はこれを利用して、他のユーザーが閲覧する Web ページにクライアントサイドのスクリプトを注入します。

例えば、"test_form.php" というページに以下のフォームがあると仮定します。

<form method="post" action="<?php echo $_SERVER["PHP_SELF"];?>">

通常の URL http://www.example.com/test_form.php でアクセスした場合、コードは以下のようにレンダリングされます。

<form method="post" action="test_form.php">

しかし、悪意のあるユーザーが以下のような URL をアドレスバーに入力した場合はどうなるでしょうか。

http://www.example.com/test_form.php/%22%3E%3Cscript%3Ealert('hacked')%3C/script%3E

この場合、コードは以下のように変換されてしまいます。

<form method="post" action="test_form.php/"><script>alert('hacked')</script>

このコードにより script タグと alert コマンドが追加され、ページが読み込まれると JavaScript が実行されます(アラートボックスが表示されます)。これは PHP_SELF が悪用される単純な例に過ぎません。

<script> タグ内には、あらゆる JavaScript コードを追加できるという点に注意してください。ハッカーはユーザーを別のサーバー上のファイルにリダイレクトさせたり、そのファイルからグローバル変数を書き換えたり、ユーザーデータを保存するためにフォームを別の宛先に送信させたりすることが可能です。

4.1 $_SERVER["PHP_SELF"] の悪用を回避する方法

これらのエクスプロイト(脆弱性攻撃)は、前述の htmlspecialchars() 関数を使用することで回避できます。

推奨されるコード:

<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">

htmlspecialchars() が特殊文字をエンティティに変換するため、もしユーザーが PHP_SELF を悪用しようとしても、出力は以下のようになります。

<form method="post" action="test_form.php/"><script>alert('hacked')</script>">

攻撃の試みは無効化され、実害は及びません。

5. PHP によるデータバリデーションの実装

最初のステップとして、すべての変数を PHP の htmlspecialchars() 関数に通します。

例えば、ユーザーがテキストフィールドに以下の内容を送信しようとした場合:
<script>location.href('http://www.hacked.com')</script>

これは実行されません。なぜなら、以下のように HTML エスケープされた状態で保存されるからです。
<script>location.href('http://www.hacked.com')</script>

これにより、ページに表示したりメールに含めたりしても安全な状態になります。

さらに、フォーム送信時に以下の2つの処理も追加で行います。

  1. 不要な文字の除去: trim() 関数を使用して、入力データから余分なスペース、タブ、改行を削除します。
  2. バックスラッシュの削除: stripslashes() 関数を使用して、入力データからバックスラッシュ \ を削除します。

5.1 test_input() 関数の作成

これらのチェックを毎回記述するのは非効率なため、すべての処理をまとめた関数を作成します。ここでは関数名を test_input() とします。

この関数を使用して各 $_POST 変数をチェックするスクリプトは以下の通りです。

実装例:

<?php
// 変数を定義し、空文字で初期化
$name = $email = $gender = $comment = $website = "";

if ($_SERVER["REQUEST_METHOD"] == "POST") {
  $name = test_input($_POST["name"]);
  $email = test_input($_POST["email"]);
  $website = test_input($_POST["website"]);
  $comment = test_input($_POST["comment"]);
  $gender = test_input($_POST["gender"]);
}

/**
 * 入力データをサニタイズする共通関数
 */
function test_input($data) {
  $data = trim($data);            // 前後の空白を削除
  $data = stripslashes($data);    // バックスラッシュを削除
  $data = htmlspecialchars($data); // HTMLエンティティに変換
  return $data;
}
?>

スクリプトの冒頭で $_SERVER["REQUEST_METHOD"] を使用して、フォームが送信されたかどうかを確認している点に注目してください。REQUEST_METHODPOST であればフォームが送信されたと判断し、バリデーションを実行します。送信されていない場合はバリデーションをスキップし、空のフォームを表示します。

なお、上記の例ではすべての入力フィールドが任意(Optional)となっているため、ユーザーがデータを入力しなくてもスクリプトは正常に動作します。次のステップでは、必須項目のバリデーションについて学んでいきましょう。