【PHP】フォームのセキュリティ対策|入力値バリデーションの実装方法

PHPでお問い合わせフォームや会員登録フォームを作成する際、入力値の妥当性チェック(バリデーション)は欠かせません。
バリデーションが不十分なフォームは、スパムメールの踏み台にされたり、フィッシング詐欺メールの送信に悪用されたりする危険性があります。
また、不正なデータがデータベースに登録されてしまうと、システム全体に影響を及ぼす可能性もあります。
この記事では、PHPでフォームの入力値を正しくチェックする方法を、項目ごとに具体的なコード例を交えて解説していきます。
入力値の妥当性チェックとは?
入力値の妥当性チェック(バリデーション)とは、ユーザーがフォームに入力したデータが、想定した形式や条件を満たしているかを確認する処理のことです。
例えば、以下のようなケースが考えられます。
- メールアドレス欄に正しい形式で入力されているか?
- 電話番号欄に数字以外の文字が含まれていないか?
- 年齢欄に現実的な範囲の数値が入力されているか?
- 必須項目が空のまま送信されていないか?
これらのチェックを怠ると、不正なデータの送信やセキュリティ上のリスクにつながります。
XSS対策やSQLインジェクション対策と同様に、バリデーションはフォームのセキュリティ対策の基本として必ず実装しましょう。
メールアドレスのバリデーション
メールアドレスの形式チェックは、フォームで最もよく使われるバリデーションの一つです。
filter_var()を使う方法(推奨)
PHPには、メールアドレスの形式をチェックするための専用フィルターが用意されています。
filter_var()関数にFILTER_VALIDATE_EMAILを指定する方法が最も簡単で確実です。
<?php
$email = "test@example.com";
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "正しいメールアドレスです。";
} else {
echo "メールアドレスの形式が正しくありません。";
}
?>
filter_var()はPHPの組み込み関数で、RFC準拠のチェックを行ってくれるため、自分で正規表現を書くよりも安全です。
正規表現を使う方法
正規表現でチェックすることも可能ですが、メールアドレスの仕様は複雑なため、完全な正規表現を書くのはかなりの作業量になります。
<?php
function checkMailAddress($mail = "") {
if (preg_match("/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/", $mail)) {
return true;
}
return false;
}
if (!checkMailAddress($mail)) {
echo "メールアドレスを正しく入力してください。";
}
?>
特別な理由がない限り、メールアドレスのチェックにはfilter_var()を使うのがおすすめです。
正規表現では対応しきれないケースも多いため、組み込みのフィルター関数を活用しましょう。
電話番号のバリデーション
電話番号は入力形式がいくつか考えられるため、フォームの仕様に合わせた正規表現を使い分ける必要があります。
ハイフンなし(数字のみ)の場合
<?php
function checkTelNumber($tel) {
// 「0」から始まる10桁または11桁の数字
if (preg_match("/^0\d{9,10}$/", $tel)) {
return true;
}
return false;
}
?>
固定電話は「0」から始まる10桁、携帯電話は11桁なので、9,10の範囲で指定しています。
ハイフンあり(例:03-1234-5678)の場合
<?php
function checkTelWithHyphen($tel) {
if (preg_match("/^\d{2,4}-\d{2,4}-\d{4}$/", $tel)) {
return true;
}
return false;
}
?>
携帯電話番号のみを許可する場合
<?php
function checkMobileNumber($number) {
// 070・080・090から始まる11桁
if (preg_match("/^0[789]0\d{8}$/", $number)) {
return true;
}
return false;
}
?>
その他のよくあるバリデーション
メールアドレスと電話番号以外にも、フォームではさまざまな項目のチェックが必要です。
よくある項目ごとに実装例を紹介します。
氏名・ふりがな(全角チェック)
氏名やふりがなに半角文字が混入していないかをチェックするには、mb_convert_kana()を活用する方法があります。
<?php
function checkZenkaku($string) {
// 半角→全角に変換して、元の文字列と比較
if (mb_convert_kana($string, "AK", "UTF-8") !== $string) {
return false; // 半角が混入している
}
return true;
}
$name = "山田太郎";
if (!checkZenkaku($name)) {
echo "氏名は全角で入力してください。";
}
?>
mb_convert_kana()で半角を全角に変換した結果と元の文字列を比較し、差異があれば半角が含まれていると判定しています。
年齢(数値の範囲チェック)
<?php
function checkAge($age) {
// 1~3桁の半角数字のみ許可
if (preg_match("/^\d{1,3}$/", $age)) {
return true;
}
return false;
}
?>
数値のチェックにis_numeric()関数を使うのは避けましょう。
is_numeric()は「1.5」や「0x1A」(16進数)などもtrueと判定するため、意図しない値が通過してしまいます。数値のバリデーションには正規表現を使うのが安全です。
郵便番号(形式チェック)
<?php
function checkPostalCode($code) {
// 「3桁-4桁」の形式をチェック
if (preg_match("/^\d{3}-\d{4}$/", $code)) {
return true;
}
return false;
}
?>
日付(妥当性チェック)
日付のバリデーションには、PHPのcheckdate()関数が便利です。
存在しない日付(例:2月30日)を検出できます。
<?php
$month = 2;
$day = 30;
$year = 2025;
if (checkdate($month, $day, $year)) {
echo "有効な日付です。";
} else {
echo "無効な日付です。";
}
// 出力:無効な日付です。
?>
セレクトボックス・ラジオボタン(想定値チェック)
セレクトボックスやラジオボタンの値は、ブラウザの開発者ツールで簡単に改ざんできます。
送信された値が、あらかじめ用意した選択肢の中に含まれているかを必ずチェックしましょう。
<?php
$allowed = ["male", "female", "other"];
$gender = $_POST["gender"];
if (!in_array($gender, $allowed, true)) {
echo "不正な値が送信されました。";
}
?>
テキスト入力欄の文字数チェック
テキストボックスやテキストエリアには、HTMLのmaxlength属性で入力制限をかけるのが一般的です。
しかし、この値はブラウザの開発者ツールで簡単に改ざんできます。
そのため、サーバー側でもPHPで文字数のチェックを必ず行いましょう。
テキストボックスの文字数チェック(氏名・住所など)
氏名や住所などのテキストボックスでは、最小文字数と最大文字数の両方をチェックするのがおすすめです。
<?php
$name = $_POST["name"];
$minLength = 1;
$maxLength = 50;
$nameLength = mb_strlen($name, "UTF-8");
if ($nameLength < $minLength) {
echo "氏名を入力してください。";
} elseif ($nameLength > $maxLength) {
echo "氏名は{$maxLength}文字以内で入力してください。";
}
?>
テキストエリアの文字数チェック(お問い合わせ内容など)
テキストエリアはテキストボックスよりも長文の入力が想定されるため、最小文字数を設けてスパム投稿を防止するのも効果的です。
また、最大文字数を設定しておかないと、大量のテキストを送りつけられるリスクもあります。
<?php
$message = $_POST["message"];
$minLength = 10;
$maxLength = 2000;
$messageLength = mb_strlen($message, "UTF-8");
if ($messageLength < $minLength) {
echo "お問い合わせ内容は{$minLength}文字以上で入力してください。";
} elseif ($messageLength > $maxLength) {
echo "お問い合わせ内容は{$maxLength}文字以内で入力してください。";
}
?>
文字数チェック用の汎用関数を作る
複数の項目で文字数チェックを行う場合は、汎用的な関数を作っておくとコードの重複を減らせます。
<?php
function checkLength($value, $min, $max) {
$length = mb_strlen($value, "UTF-8");
if ($length < $min) {
return "min_error";
} elseif ($length > $max) {
return "max_error";
}
return "ok";
}
// 使用例
$result = checkLength($_POST["name"], 1, 50);
if ($result === "min_error") {
echo "氏名を入力してください。";
} elseif ($result === "max_error") {
echo "氏名は50文字以内で入力してください。";
}
?>
HTMLのmaxlengthはあくまでブラウザ側の制御です。
悪意あるユーザーは開発者ツールで値を変更したり、HTMLを直接編集して送信できます。テキストボックスもテキストエリアも、サーバー側での文字数チェックを必ず実装しましょう。
バリデーションをまとめて管理する
チェック項目が増えてくると、コードが煩雑になりがちです。
実務では、バリデーション処理を一つの配列にまとめて管理すると保守しやすくなります。
<?php
$errors = [];
// 氏名のチェック
if (empty($_POST["name"])) {
$errors[] = "氏名を入力してください。";
} elseif (mb_strlen($_POST["name"], "UTF-8") > 50) {
$errors[] = "氏名は50文字以内で入力してください。";
}
// メールアドレスのチェック
if (empty($_POST["email"])) {
$errors[] = "メールアドレスを入力してください。";
} elseif (!filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) {
$errors[] = "メールアドレスの形式が正しくありません。";
}
// 電話番号のチェック
if (!empty($_POST["tel"]) && !preg_match("/^0\d{9,10}$/", $_POST["tel"])) {
$errors[] = "電話番号を正しく入力してください。";
}
// エラーがあれば表示
if (!empty($errors)) {
foreach ($errors as $error) {
echo "<p>" . htmlspecialchars($error, ENT_QUOTES, "UTF-8") . "</p>";
}
} else {
echo "入力内容に問題はありません。";
}
?>
エラーメッセージを配列に格納しておくことで、すべてのチェックを通過してからまとめてエラーを表示できます。
まとめ
今回は、PHPフォームにおける入力値の妥当性チェック(バリデーション)について紹介しました。
- メールアドレスのチェックには
filter_var()関数がおすすめ - 電話番号や郵便番号は、フォームの仕様に合わせた正規表現でチェックする
- セレクトボックスやラジオボタンの値も、サーバー側で必ず検証する
- テキストボックスやテキストエリアは、最小・最大文字数のチェックをサーバー側で必ず行う
- HTMLの
maxlengthはブラウザで改ざんできるため、PHPでも文字数チェックを行う - 数値チェックに
is_numeric()は使わず、正規表現を使う
バリデーションはチェック項目が多く手間のかかる作業ですが、セキュリティ対策の基本として必ず実装しましょう。
エラーメッセージを配列でまとめて管理すると、コードの見通しも良くなります。






















