7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【覚書】PHP 問い合わせフォームの作り方

Posted at

備忘録として。

#作成コード全体

form.php

<?php
session_start();
//csrf対策①セッション使いますよという宣言

require 'validation.php';
//バリデーションファイルをimport

header('X-FRAME-OPTIONS: DENY');
//clickjacking対策

function h($str) {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
//XSS対策




// echo '<pre>';
// var_dump($_POST);
// echo '<pre>';
// echo $_GET['name'];
//スーパーグローバル変数
//全9種類ある
//連想配列になっている
//[]の中にはinputタグのキー(name="キー")を入力
//値がどうなっているかを確認できる


$pageFlag = 0;
//1ページで入力→確認→完了まで表示する場合
//pageFlagという変数を使って遷移させる

$error = validation($_POST);
//バリデーションのエラー表示を受け取る変数

if(!empty($_POST['btn_confirm']) && empty($error)){
  $pageFlag = 1;
}
//確認ボタンが空じゃない且つ、エラーメッセージが空だったらページを変える


if(!empty($_POST['btn_submit'])){
  $pageFlag = 2;
}
?>

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  <title>Document</title>
</head>
<body>

<style>
.form-group{
  width:90%;
  max-width:500px;
  margin:0 auto;
}

</style>

<?php if($pageFlag === 0) : ?>
<!-- 入力画面 -->

<?php
if(!isset($_SESSION['csrfToken'])){
  $csrfToken = bin2hex(random_bytes(32));
  $_SESSION['csrfToken'] = $csrfToken;
}
$token = $_SESSION['csrfToken'];
?>
<!--csrf対策② 合言葉の設定 -->

<?php if(!empty($_POST['btn_confirm']) && !empty($error)) :?>
<!-- 確認ボタンが空ではなく、且つエラーが空ではなかったら -->
<ul>
<?php foreach ($error as $value) :?>
<!-- $errorは連想配列なのでforeachで分解していく -->
<li><?php echo $value ; ?></li>
<!-- 分解したエラー文をlistの中に表示していく -->
<?php endforeach ;?>
</ul>
<?php endif ;?>


  <form method="POST" action="input.php">
    <!-- method="GET"または"POST"を記入する -->
    <!-- action="処理をするファイル" -->
    <div class="form-group">
    <h1>お問い合わせ</h1>
      <label for="name">名前</label>
      <input type="text" name="your_name" id="name" class="form-control" value="<?php echo h($_POST['your_name']) ; ?>">

      <label for="email">メールアドレス</label>
      <input type="email" name="email" id="email" class="form-control" value="<?php echo h($_POST['email']) ; ?>">

      <label for="url">ホームページ</label>
      <input type="url" name="url" id="url" class="form-control" value="<?php echo h($_POST['url']) ; ?>">

      <br>
      <p>性別</p>
      <!-- <label for="women">女</label> -->
      <input type="radio" name="gender" id="women" class="form-check-inline" value="0"><!-- <label for="men">男</label> -->
      <input type="radio" name="gender" id="men" class="form-check-inline" value="1"><br>

      <br>
      <label for="age">年齢</label>
      <select name="age" id="age" class="form-control">
      <option value="">選択してください</option>
      <option value="1">10代</option>
      <option value="2">20代</option>
      <option value="3">30代</option>
      <option value="4">40代</option>
      <option value="5">50代</option>
      <option value="6">60代</option>
      </select>
      <br>

      <label for="message">お問い合わせ内容</label>
      <textarea name="message" id="message" class="form-control" value="<?php echo h($_POST['message']) ; ?>"></textarea>
      <br>

      <input type="checkbox" name="caution" id="caution" class="form-check-inline" value="1">注意事項にチェックする
      <br>



      <br>
      <input type="submit" name="btn_confirm" value="確認" class="btn btn-primary mb-2">
      <input type="hidden" name="csrf" value="<?php echo $token; ?>">
      <!-- csrf対策③ inputタグに合言葉を設定しておく -->

    </div>
  </form>
<?php endif; ?>

<?php if($pageFlag === 1) : ?>
<!-- 確認画面 -->

<?php if($_POST['csrf'] === $_SESSION['csrfToken']) : ?>
  <!-- csrf対策④ 入力できたcsrfと$_SESSIONの情報があってるか確認する-->

  <form method="POST" action="input.php" class="form-group">
  <!-- method="GET"または"POST"を記入する -->
  <!-- action="処理をするファイル" -->

  <br>
  <label for="name">名前:</label>
  <?php echo h($_POST['your_name']) ; ?>
  <br>

  <br>
  <label for="email">メールアドレス:</label>
  <?php echo h($_POST['email']) ; ?>
  <br>

  <br>
  <label for="url">ホームページ:</label>
  <?php echo h($_POST['url']) ; ?>
  <br>

  <br>
  <span>性別:</span>
  <?php
  if($_POST['gender'] === '0'){echo '女性';}
  if($_POST['gender'] === '1'){echo '男性';}
  ?>
  <br>

  <br>
  <label for="age">年齢:</label>
  <?php
  if($_POST['age'] === '1'){echo '10代';}
  elseif($_POST['age'] === '2'){echo '20代';}
  elseif($_POST['age'] === '3'){echo '30代';}
  elseif($_POST['age'] === '4'){echo '40代';}
  elseif($_POST['age'] === '5'){echo '50代';}
  elseif($_POST['age'] === '6'){echo '60代';}
  ?>
  <br>

  <br>
  <label for="message">お問い合わせ内容:</label>
  <?php echo h($_POST['message']) ; ?>

  <br>
  <input type="submit" name="btn_submit" value="送信" class="btn btn-primary mb-2">
  <input type="submit" name="back" value="戻る" class="btn btn-primary mb-2">

  <input type="hidden" name="your_name" value="<?php echo h($_POST['your_name']) ; ?>">
  <input type="hidden" name="email" value="<?php echo h($_POST['email']) ; ?>">
  <input type="hidden" name="url" value="<?php echo h($_POST['url']) ; ?>">
  <input type="hidden" name="gender" value="<?php echo h($_POST['gender']) ; ?>">
  <input type="hidden" name="age" value="<?php echo h($_POST['age']) ; ?>">
  <input type="hidden" name="message" value="<?php echo h($_POST['message']) ; ?>">

  <input type="hidden" name="csrf" value="<?php echo h($_POST['csrf']) ; ?>">
  <!-- csrf対策⑤ページが入力→確認に変わるタイミングでcsrfの値が消えてしまうのでtype="hidden"のinputで値を保持させておく -->

  <!-- 消えては困る情報を面には出さないが保持しておく行 -->



  </form>

<?php endif; ?>
<?php endif; ?>

<?php if($pageFlag === 2) : ?>
<!-- 完了画面 -->
<?php if($_POST['csrf'] === $_SESSION['csrfToken']) : ?>
<!-- csrf対策⑥完了画面でも合言葉があってるか確認する -->

送信が完了しました。

<?php unset($_SESSION['csrfToken']); ?>
<!-- csrf対策⑦合言葉を削除する -->

<?php endif; ?>
<?php endif; ?>

</body>
</html>
validation.php
<?

function validation($data){

  $error = [];

  //名前
  if(empty($data['your_name']) || 20 < mb_strlen($data['your_name'])){
    $error[] ='名前は20字以内で入力してください。';
  }
  //未入力、且つ、20字以上の場合はエラー表示

  //メールアドレス
  if(empty($data['email']) || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)){
    $error[] ='メールアドレスは正しい形式で入力してください。';
  }
  //未入力、且つ、filter_varのメール形式に引っかかったらエラー表示

  //url
  if(!empty($data['url'])){
    if(!filter_var($data['url'], FILTER_VALIDATE_URL)){
      $error[] ='ホームページは正しい形式で入力してください。';
    }
  }
  //もし入力があれば
  //filter_varのurl形式に引っかかったらエラー表示

  //性別
  if(!isset($data['gender'])){
    $error[] = '性別は必ず入力してください。';
  }
  //issetでラジオボタンの入力チェック

  //年齢
  if(empty($data['age']) || 6 < $data['age']){
    $error[] ='年齢を入力してください。';
  }
  //未選択、且つ、6より大きい数字の場合はエラー表示

  //お問い合わせ内容の文字数チェック
  if(empty($data['message']) || 200 < mb_strlen($data['message'])){
    $error[] ='お問い合わせ内容は200字以内で入力してください。';
  }
  //未入力、且つ、200文字以上の場合はエラー表示

  //注意事項
  if($data['caution'] !== '1'){
    $error[] ='注意事項をご確認ください。';
  }
  //チェックがついてなかったらエラー表示


  return $error;
}

#よくあるフォーム構成
①入力画面
②確認画面
③完了画面

#GETメソッドとPOSTメソッドについて

- GETメソッド【HTTPリクエストメソッド】

ホームページを見るソフト(Webブラウザ)からホームページのファイルが置いてあるコンピュータ(Webサーバ)に対して出されるお願いの種類のひとつであり、
WebブラウザからWebサーバに渡す値をURLの後ろにくっつけて送るやり方

例)https:hoge.test.com/input.php?name=山田太郎

- POSTメソッド【HTTPリクエストメソッド】

ホームページを見るソフト(Webブラウザ)からホームページのファイルが置いてあるコンピュータ(Webサーバ)に対して出されるお願いの種類のひとつであり、
WebブラウザからWebサーバに渡す値を見えないところにくっつけて送るやり方

例)https:hoge.test.com/input.php?

#フォームのセキュリティについて
フォームは狙われやすいのでセキュリティ対策をする!

XSSの対策
form.php
function h($str) {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

//確認画面の入力した値を表示させるコードなどに関数をつけていく
  <?php echo h($_POST['your_Email']) ; ?>

※ページ上部のphpタグの中に書いていく

関数リファレンス:https://www.php.net/manual/ja/function.htmlspecialchars.php

クリックジャッキングの対策
form.php

  header('X-FRAME-OPTIONS: DENY');

※ページ上部のphpタグの中に書いていく

CSRF対策の対策

合言葉が入っていなかったら無効とする

form.php

1. ページ上部のphpタグにセッションを使用する宣言をする
session_start();

2. フォーム入力画面に合言葉の設定
<?php if($pageFlag === 0) : ?>
<?php
if(!isset($_SESSION['csrfToken'])){
  $csrfToken = bin2hex(random_bytes(32));
  $_SESSION['csrfToken'] = $csrfToken;
}
$token = $_SESSION['csrfToken'];
?>

3. フォーム入力画面にtype="hidden"のinputタグの中に仕込む
<input type="hidden" name="csfr" value="<?php echo $token ?>">

4. 確認画面で合言葉が正しく入ってきているか確認する記述をする
<?php if($pageFlag === 1) : ?>
<!-- 確認画面 -->

<?php if(_POST['csrf'] === $_SESSION['csrfToken']) : ?>

....

<?php endif; ?>
//確認画面下部でifを閉じるのを忘れずに

5. ページが入力→確認に変わるタイミングでcsrfの値が消えてしまうので
type="hidden"のinputで値を保持させておく
<input type="hidden" name="csrf" value="<?php echo h($_POST['csrf']) ; ?>">

6. 完了画面でも合言葉があってるか確認する
<?php if($pageFlag === 2) : ?>
<!-- 完了画面 -->
<?php if(_POST['csrf'] === $_SESSION['csrfToken']) : ?>

.....
7. 合言葉を削除する
<?php unset($_SESSION['csrfToken']); ?>

<?php endif; ?>
//6.のifはページ下部でしっかりendifで閉じる



#バリデーション
入力に不備がないかチェックする

form.php


1. 外部ファイルでバリデーションの関数を作るのでファイル読み込み
require 'validation.php';
//バリデーションファイルをimport
validation.php
2.外部ファイルでvalidation.phpファイルを作る 
<?

function validation($data){

  $error = [];

  //名前
  if(empty($data['your_name']) || 20 < mb_strlen($data['your_name'])){
    $error[] ='名前は20字以内で入力してください。';
  }
  //未入力、且つ、20字以上の場合はエラー表示

  //メールアドレス
  if(empty($data['email']) || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)){
    $error[] ='メールアドレスは正しい形式で入力してください。';
  }
  //未入力、且つ、filter_varのメール形式に引っかかったらエラー表示

  //url
  if(!empty($data['url'])){
    if(!filter_var($data['url'], FILTER_VALIDATE_URL)){
      $error[] ='ホームページは正しい形式で入力してください。';
    }
  }
  //もし入力があれば
  //filter_varのurl形式に引っかかったらエラー表示

  //性別
  if(!isset($data['gender'])){
    $error[] = '性別は必ず入力してください。';
  }
  //issetでラジオボタンの入力チェック

  //年齢
  if(empty($data['age']) || 6 < $data['age']){
    $error[] ='年齢を入力してください。';
  }
  //未選択、且つ、6より大きい数字の場合はエラー表示

  //お問い合わせ内容の文字数チェック
  if(empty($data['message']) || 200 < mb_strlen($data['message'])){
    $error[] ='お問い合わせ内容は200字以内で入力してください。';
  }
  //未入力、且つ、200文字以上の場合はエラー表示

  //注意事項
  if($data['caution'] !== '1'){
    $error[] ='注意事項をご確認ください。';
  }
  //チェックがついてなかったらエラー表示


  return $error;
}


form.php

3.form.phpにエラーを受け取る変数を定義する

$error = validation($_POST);

4. ページ移動の条件に確認ボタンが空じゃない且つエラーメッセージが空だったらページを変えるというif文を作る
if(!empty($_POST['btn_confirm']) && empty($error)){
  $pageFlag = 1;
}

5. 入力画面でエラーを表示させる
<?php if(!empty($_POST['btn_confirm']) && !empty($error)) :?>
//確認ボタンが空ではなく、且つエラーが空ではなかったら
<ul><?php foreach ($error as $value): ?>
//$errorは連想配列なのでforeachで分解していく
<li><?php echo $value; ?></li>
//分解したエラー文をlistの中に表示していく
<?php endforeach ; ?>
</ul>
<?php endif ; ?>
7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?