備忘録として。
#作成コード全体
<?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>
<?
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の対策
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
クリックジャッキングの対策
header('X-FRAME-OPTIONS: DENY');
※ページ上部のphpタグの中に書いていく
CSRF対策の対策
合言葉が入っていなかったら無効とする
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で閉じる
#バリデーション
入力に不備がないかチェックする
1. 外部ファイルでバリデーションの関数を作るのでファイル読み込み
require 'validation.php';
//バリデーションファイルをimport
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;
}
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 ; ?>