DBを使わない簡易的なPHPのメールフォーム

フォームのひな形を作ります

contact.php

<h2>お問い合わせ</h2>
<form action="./contact.php" method="post">
    <p>名前:<input type="text" name="fullname"></p>
    <p>email: <input type="text" name="email"></p>
    <p>お問い合わせ内容:</p>
    <p><textarea name="message" id="" cols="30" rows="10"></textarea></p>
    <p><input type="submit" name="confirm" value="確認"></p>
</form>

同じページで条件分岐により表示させる内容を変えるため、actionにはこのページのファイルを指定します。

条件分岐を施して、contact.php内で表示を変えるようにしました。

<?php if ($_POST) { ?>
    <h2>確認画面</h2>
    <p>名前:<?php echo $_POST['fullname'] ?></p>
    <p>email:<?php echo $_POST['email'] ?></p>
    <p>お問い合わせ内容:</p>
    <p><?php echo $_POST['message'] ?></p>
    <input type="submit" name="back" value="戻る">
    <input type="submit" name="send" value="送信">
<?php } else { ?>
    <h2>お問い合わせ</h2>
    <form action="./contact.php" method="post">
        <p>名前:<input type="text" name="fullname"></p>
        <p>email: <input type="text" name="email"></p>
        <p>お問い合わせ内容:</p>
        <p><textarea name="message" id="" cols="30" rows="10"></textarea></p>
        <p><input type="submit" name="confirm" value="確認"></p>
    </form>
<?php } ?>

POSTとGETで表示される内容を分岐

しかし、これだと入力画面⇒確認画面⇒送信完了画面のロジックが成立しないため、以下のように記述

3つのモードを定義して、送信完了画面まで表示

<?php

// 入力画面
$mode = 'input';
if (isset($_POST['back'])) {
    // 何もしない。
} elseif (isset($_POST['confirm'])\) {
    // 確認画面
    $mode = 'confirm';
} elseif (isset($_POST['send'])) {
    // 送信完了画面
    $mode = 'send';
} else {
    // 何もしない
}

?>

// 省略
// わかりやすいように順番をinput⇒confirm⇒sendにしました。

<?php if ($mode == 'input') { ?>
    <h2>お問い合わせ</h2>
    <form action="./contact.php" method="post">
        <p>名前:<input type="text" name="fullname"></p>
        <p>email: <input type="text" name="email"></p>
        <p>お問い合わせ内容:</p>
        <p><textarea name="message" id="" cols="30" rows="10"></textarea></p>
        <p><input type="submit" name="confirm" value="確認"></p>
    </form>
<?php } elseif ($mode == 'confirm') { ?>
    <h2>確認画面</h2>
    <form action="./contact.php" method="post">
        <p>名前:<?php echo $_POST['fullname'] ?></p>
        <p>email:<?php echo $_POST['email'] ?></p>
        <p>お問い合わせ内容:</p>
        <p><?php echo $_POST['message'] ?></p>
        <input type="submit" name="back" value="戻る">
        <input type="submit" name="send" value="送信">
    </form>
<?php } elseif ($mode == 'send') { ?>
    <p>送信が完了しました。</p>
    <p><a href="contact.php">入力画面に戻る</a></p>
<?php } ?>

バリデーションチェック

バリデーションチェックをして、未入力や、不正な送信をチェックします。 HTMLにて表示させたい場所にエラーメッセージを表示します。

<?php

session_start();
$mode = 'input'; // 入力画面
$errmessage = array();
if (isset($_POST['back']){
    // 何もしない。
} elseif (isset($_POST['confirm']) {
    if (!$_POST['fullname']) {
        $errmessage[] = '名前を入力してください。';
    } else {
        $_SESSION['fullname'] = $_POST['fullname'];
    }
    if (!$_POST['email']) {
        $errmessage[] = 'emailを入力してください。';
    } else {
        $_SESSION['email'] = $_POST['email'];
    }
    if (!$_POST['message']) {
        $errmessage[] = '本文を入力してください。';
    } else {
        $_SESSION['message'] = $_POST['message'];
    }
    if ($errmessage) {
        $mode = 'input';
    } else {
        $mode = 'confirm';
    }
} elseif (isset($_POST['send'])) {
    $_SESSION['fullname'] = "";
    $_SESSION['email'] = "";
    $_SESSION['message'] = "";
    // 送信完了画面
    $mode = 'send';
} else {
    // 何もしない
}

?>

セッションを開始し、値が入力された時だけセッションに入力した値を保存。

文字数と、その他のバリデーションをチェックし、HTML側では確認画面の$_POSTを$_SESSIONに書き直す。

値が入力されていなければ、変数$errmessageにエラーメッセージを格納。

エラーメッセージが格納されていれば$modeをinputに、正常であれば$modeをconfirmに遷移

最後はセッションに格納されたデータを空にしてinput画面に遷移

html内には、エラーを表示させたい場所で以下のコードを記述

<?php
echo '<div class="alert">';
echo implode('<br>', $errmessage);
echo '</div>';
?>

$errmessageに格納された値が表示されるようになります。

XSS対策

<?php

session_start();
$mode = 'input';
$errmessage = array();
if (isset($_POST['back'])) {
    // 何もしない。
} elseif (isset($_POST['confirm'])) {
    if (!$_POST['fullname']) {
        $errmessage[] = '名前を入力してください。';
    } elseif(mb_strlen($_POST['fullname']) > 50) {
        $errmessage[] = '名前は50文字以内にしてください。';
    }
    $_SESSION['fullname'] = htmlspecialchars($_POST['fullname'], ENT_QUOTES, "UTF-8");
    if (!$_POST['email']) {
        $errmessage[] = 'emailを入力してください。';
    } elseif (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
        $errmessage[] = 'emailの形式が不正です。';
    } elseif (mb_strlen($_POST['email']) > 100) {
        $errmessage[] = 'emailは100文字以内に収めてください。';
    }
    $_SESSION['email'] = htmlspecialchars($_POST['email'], ENT_QUOTES, "UTF-8");
    if (!$_POST['message']) {
        $errmessage[] = '本文を入力してください。';
    } elseif (mb_strlen($_POST['message']) > 500) {
        $errmessage[] = '本文は500文字以内に収めてください。';
    }
    $_SESSION['message'] = htmlspecialchars($_POST['message'], ENT_QUOTES, "UTF-8");
    if ($errmessage) {
        $mode = 'input';
    } else {
        $mode = 'confirm';
    }
} elseif (isset($_POST['send'])) {
    $_SESSION['fullname'] = "";
    $_SESSION['email'] = "";
    $_SESSION['message'] = "";
    // 送信完了画面
    $mode = 'send';
} else {
    $errmessage = array();
    // 何もしない
}
?>

// 省略

        <form action="./contact.php" method="post">
            <p>名前:<input type="text" name="fullname" value="<?php echo $_SESSION['fullname'] ?>"></p>
            <p>email: <input type="email" name="email" value="<?php echo $_SESSION['email'] ?>"></p>
            <p>お問い合わせ内容:</p>
            <p><textarea name="message" id="" cols="30" rows="10"><?php echo nl2br($_SESSION['message']) ?></textarea></p>
            <p><input type="submit" name="confirm" value="確認"></p>
        </form>
    <?php } elseif ($mode == 'confirm') { ?>
        <h2>確認画面</h2>
                <form action="./contact.php" method="POST">
                    <p>名前:<?php echo $_SESSION['fullname'] ?></p>
                    <p>email:<?php echo $_SESSION['email'] ?></p>
                    <p>お問い合わせ内容:</p>
                    <p><?php echo $_SESSION['message'] ?></p>
                    <p><input type="submit" name="send" value="送信"></p>
                    <p><input type="submit" name="back" value="戻る"></p>
                </form>

CSRFのための対策として、トークンを確認画面で発行します

<?php

// 省略
// $errmessageが有り無しの判定をするときの条件分岐で、エラーがなかった場合にトークンを作成します。
    if ($errmessage) {
        $mode = 'input';
    } else {
        $mode = 'confirm';
        $token = bin2hex(random_bytes(32));
        $_SESSION['token'] = $token;
    }

// 省略
?>

// 省略
// hiddenを使い、見えないところでトークンを発行します。

<h2>確認画面</h2>
<form action="./contact.php" method="post">
    <input type="hidden" name="token" value="<?php echo $_SESSION['token'] ?>">
    <p>名前:<?php echo $_SESSION['fullname'] ?></p>
    <p>email:<?php echo $_SESSION['email'] ?></p>
    <p>お問い合わせ内容:</p>
    <p><?php echo $_SESSION['message'] ?></p>
    <input type="submit" name="back" value="戻る">
    <input type="submit" name="send" value="送信">
</form>

tokenが一致していなかった場合に「不正なアクセスです」といったメッセージが出るようにします

<?php

session_start();
$mode = 'input';
$errmessage = array();
if (isset($_POST['back'])) {
    $mode = 'input';
} elseif (isset($_POST['confirm'])) {
    if (!$_POST['fullname']) {
        $errmessage[] = '名前を入力してください。';
    } elseif (mb_strlen($_POST['fullname']) > 50) {
        $errmessage[] = '名前は50文字以内にしてください。';
    }
    $_SESSION['fullname'] = htmlspecialchars($_POST['fullname'], ENT_QUOTES, "UTF-8");
    if (!$_POST['email']) {
        $errmessage[] = 'emailを入力してください。';
    } elseif (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
        $errmessage[] = 'emailの形式が不正です。';
    } elseif (mb_strlen($_POST['email']) > 100) {
        $errmessage[] = 'emailは100文字以内に収めてください。';
    }
    $_SESSION['email'] = htmlspecialchars($_POST['email'], ENT_QUOTES, "UTF-8");
    if (!$_POST['message']) {
        $errmessage[] = '本文を入力してください。';
    } elseif (mb_strlen($_POST['message']) > 500) {
        $errmessage[] = '本文は500文字以内に収めてください。';
    }
    $_SESSION['message'] = htmlspecialchars($_POST['message'], ENT_QUOTES, "UTF-8");
    if ($errmessage) {
        $mode = 'input';
    } else {
        $mode = 'confirm';
        $token = bin2hex(random_bytes(32));
        $_SESSION['token'] = $token;
    }
} elseif (isset($_POST['send'])) {
// 以下の記述を追加
    if (isset($_POST['token']) != $_SESSION['token']) {
        $errmessage[] = '不正な処理が行われました';
        $mode = 'input';
    } else {
        $_SESSION['fullname'] = "";
        $_SESSION['email'] = "";
        $_SESSION['message'] = "";
        // 送信完了画面
        $mode = 'send';
    }
} else {
    $errmessage = array();
    // 何もしない
}
?>

一通りのセキュリティー対策は施された状態になっているかと思います。

プロの腕利きデザイナーに手軽にデザイン制作依頼。ロゴ・チラシ・名刺・パンフレット・看板・図面など、ジャンルに特化した35,000人のデザイナーからあなたにぴったりのデザイナーを選んで手軽にデザイン業務を依頼できます。

コメントを残す