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();
// 何もしない
}
?>
一通りのセキュリティー対策は施された状態になっているかと思います。