6.
ユーザ登録
確認画面
送信されたPOSTデータから、確認画面を表示します。

ファイル構成
php_sns/
├── lib/
│ └── Model.php
├── regist/
│ ├── add.php
│ ├── complete.php
│ ├── confirm.php
│ ├── index.php
│ └── input.php
├── app.php
└── env.php
確認画面作成
HTML基本タグ作成
POSTデータを取得処理の下に、HTMLの基本タグを追加します。
confirm.php
<?php
require_once "../app.php";
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
exit;
}
$regist = $_POST;
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= SITE_TITLE ?></title>
<base href="<?= BASE_URL ?>">
<script src="https://cdn.tailwindcss.com"></script>
</head>
<main id="register" class="flex justify-center">
<div class="w-1/2 mt-3 p-5">
<h2 class="text-2xl mb-3 font-normal text-center">Sign Up</h2>
</div>
</main>
</html>
HTML表示確認
入力画面からPOST送信して、確認画面が表示されるか確認します。

データ表示
取得した「アカウント名」「Email」「名前」をHTML表示します。
regist/confirm.php
...
<main id="register" class="flex justify-center">
<div class="w-1/2 mt-3 p-5">
<h2 class="text-2xl mb-3 font-normal text-center">Sign Up</h2>
<div class="bg-white border rounded px-8 pt-6 pb-8 mb-4">
<div class="mb-4">
<p class="text-center text-gray-700 text-md">この内容で登録しますか?</p>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-md font-bold mb-2"
for="account_name">
アカウント名
</label>
<p class="appearance-none w-full py-2 px-0 text-gray-700" id="account_name">
<?= $regist['account_name'] ?>
</p>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-md font-bold mb-2" for="email">
Email
</label>
<p class="appearance-none w-full py-2 px-0 text-gray-700" id="email">
<?= $regist['email'] ?>
</p>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-md font-bold mb-2" for="name">
名前
</label>
<p class="appearance-none w-full py-2 px-0 text-gray-700" id="name">
<?= $regist['name'] ?>
</p>
</div>
</div>
</div>
</main>
...

戻るリンク
入力画面「regist/input.php」に戻るリンクを追加します。
regist/confirm.php
<div class="flex">
<a href="regist/input.php" class="w-full
mx-1 mb-4 py-2 px-4
text-gray-500
text-center
bg-gray-100
hover:bg-gray-200
rounded">
修正
</a>
</div>
リンク確認
【修正】リンクで入力画面に戻ります。
入力画面では、前回の入力データは保持されていません。
セッション
入力したデータを入力画面やデータ登録処理で利用できるように、セッションに保存します。
セッション登録処理
セッション開始処理
「app.php」が実行されたら、セッション開始するようにします。
app.php
<?php
require_once "env.php";
// セッション開始
session_start();
session_regenerate_id(true);
const BASE_DIR = __DIR__;
const APP_DIR = __DIR__ . "/app/";
const LIB_DIR = __DIR__ . "/lib/";
require_once LIB_DIR . 'Model.php';
セッション登録
POSTデータをセッションに登録します。$_SESSIONのキーは「env.php」で設定した APP_KEY と「regist」にします。
regist/confirm.php
<?php
require_once "../app.php";
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
exit;
}
// POSTデータをセッション登録
$_SESSION[APP_KEY]['regist'] = $_POST;
$regist = $_POST;
?>
セッション取得
入力画面でセッションデータを取得してデバッグ確認します。
regist/input.php
<?php
require_once "../app.php";
$regist = $_SESSION[APP_KEY]['regist'];
// デバッグ確認
var_dump($regist);
?>
...
セッションデータ確認
確認画面でセッション登録したら、入力画面に戻ってセッションが保存されているか確認します。これで、どの画面でもセッションデータにアクセスできます。

セッション表示
入力画面のフォームに、セッションに登録した前回入力したデータを表示します。
regist/input.php
...
<main id="regist" class="flex justify-center">
<div class="w-1/2 mt-3 p-5">
<h2 class="text-2xl mb-3 font-normal text-center">Sign Up</h2>
<form action="regist/confirm.php" method="post">
<div class="relative mb-4">
<input class="block
px-2.5 pb-2.5 pt-6 mb-3
w-full rounded-lg
text-sm
text-gray-900
ring-1 ring-gray-300
focus:outline-none
focus:ring-1
focus:ring-blue-600
peer"
oninput="checkRegisterInputs()" type="text"
name="account_name" id="account_name"
value="<?= @$regist['account_name'] ?>" placeholder=" " required>
<label for="account_name" class="absolute
text-sm text-gray-400
duration-300
transform -translate-y-4 scale-75
top-4
origin-[0] start-2.5
peer-focus:px-0
peer-focus:text-blue-600
peer-focus:dark:text-blue-500
peer-placeholder-shown:scale-100
peer-placeholder-shown:translate-y-0
peer-focus:scale-75
peer-focus:-translate-y-4">
アカウント名
</label>
<p class="text-sm text-red-600"><?= @$errors['email'] ?></p>
</div>
<div class="relative mb-4">
<input class="block
px-2.5 pb-2.5 pt-6 mb-3
w-full rounded-lg
text-sm
text-gray-900
ring-1 ring-gray-300
focus:outline-none
focus:ring-1
focus:ring-blue-600
peer"
oninput="checkRegisterInputs()" type="email"
name="email" id="email" value="<?= @$regist['email'] ?>"
placeholder=" " required>
<label for="email" class="absolute
text-sm text-gray-400
duration-300
transform -translate-y-4 scale-75
top-4
origin-[0] start-2.5
peer-focus:px-0
peer-focus:text-blue-600
peer-focus:dark:text-blue-500
peer-placeholder-shown:scale-100
peer-placeholder-shown:translate-y-0
peer-focus:scale-75
peer-focus:-translate-y-4">
Email
</label>
<p class="text-sm text-red-600"><?= @$errors['email'] ?></p>
</div>
<div class="relative mb-4">
<input class="block
px-2.5 pb-2.5 pt-6 mb-3
w-full rounded-lg
text-sm
text-gray-900
ring-1 ring-gray-300
focus:outline-none
focus:ring-1
focus:ring-blue-600
peer"
oninput="checkRegisterInputs()"
type="password" name="password" id="password" value=""
placeholder=" " required>
<label for="password" class="absolute
text-sm text-gray-400
duration-300
transform -translate-y-4 scale-75
top-4
origin-[0] start-2.5
peer-focus:px-0
peer-focus:text-blue-600
peer-focus:dark:text-blue-500
peer-placeholder-shown:scale-100
peer-placeholder-shown:translate-y-0
peer-focus:scale-75
peer-focus:-translate-y-4">
パスワード
</label>
<div class="text-sm text-red-600"><?= @$errors['password'] ?></div>
</div>
<div class="relative mb-4">
<input class="block
px-2.5 pb-2.5 pt-6 mb-3
w-full rounded-lg
text-sm
text-gray-900
ring-1 ring-gray-300
focus:outline-none
focus:ring-1
focus:ring-blue-600
peer"
oninput="checkRegisterInputs()" type="text"
name="name" id="name" value="<?= @$regist['name'] ?>"
placeholder=" " required>
<label for="name" class="absolute
text-sm text-gray-400
duration-300
transform -translate-y-4 scale-75
top-4
origin-[0] start-2.5
peer-focus:px-0
peer-focus:text-blue-600
peer-focus:dark:text-blue-500
peer-placeholder-shown:scale-100
peer-placeholder-shown:translate-y-0
peer-focus:scale-75
peer-focus:-translate-y-4">
名前
</label>
<p class="text-sm text-red-600"><?= @$errors['name'] ?></p>
</div>
<div>
<button id="submit_button" class="w-full
mb-2 py-2 px-4 bg-sky-500
hover:bg-sky-700
text-white
rounded-lg
disabled:bg-blue-300" disabled>
次へ
</button>
</div>
<div class="text-center">
<ul>
<li class="pt-3">
<a href="login/" class="text-blue-700 hover:text-blue-600">Sign in はこちら</a>
</li>
<li class="pt-3">
<a href="forget_password/" class="text-blue-700 hover:text-blue-600">
パスワードを忘れた方
</a>
</li>
</ul>
</div>
</form>
</div>
</main>
...

ユーザ登録処理
登録フォーム追加
確認画面に「regist/add.php」へのフォームリクエスト(POST)を追加します。
regist/confirm.php
...
<!-- 「regist/add.php」へのフォームリクエスト追加 -->
<form action="regist/add.php" method="post">
<div class="flex">
<!-- 登録ボタン追加 -->
<button id="submit_button" class="w-full
mx-1 mb-4 py-2 px-4
text-white
bg-sky-500
hover:bg-sky-700
rounded-lg">
登録
</button>
<a href="regist/input.php" class="w-full
mx-1 mb-4 py-2 px-4
text-gray-500
text-center
bg-gray-100
hover:bg-gray-200
rounded">
修正
</a>
</div>
</form>
...
セッション取得
regist/add.php
<?php
// app.php を読み込み
require_once '../app.php';
// POSTリクエストでなければ何も表示しない
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit;
}
// セッションデータ取得
$regist = $_SESSION[APP_KEY]['regist'];
パスワードハッシュ
ハッシュデータはデータを復号(元に戻す)できないので、パスワードを保存するのに有効です。
password_hash()
password_hash() は、パスワードハッシュするためのメソッドです。
password_hash(パスワード, オプション);
パスワードがハッシュ化されると解読不可能な文字列に変換されます。
$2y$10$S9n.vg7s8T4lRIOiggVlMOuWgR1tZSBKLwToX5S.nbZ2iRcZvK8w6
password_verify()
password_verify() で生のパスワードと、ハッシュが一致しているかの検証(True/False)できます。
bool = password_verify(文字列, ハッシュ)
regist/add.php
<?php
require_once '../app.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit;
}
$regist = $_SESSION[APP_KEY]['regist'];
// パスワードのハッシュ化
$regist['password'] = password_hash($regist['password'], PASSWORD_DEFAULT);
ユーザデータ登録
usersテーブルにデータ登録するSQLを作成し、実行します。データ登録後は「complete.php」にリダイレクトします。
regist/add.php
<?php
require_once '../app.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit;
}
$regist = $_SESSION[APP_KEY]['regist'];
$regist['password'] = password_hash($regist['password'], PASSWORD_DEFAULT);
// データベースに接続
$model = new Model();
// users テーブルにレコードを挿入するSQL
$sql = "INSERT INTO users (account_name, name, email, password)
VALUES (:account_name, :name, :email, :password);
";
// データベースに登録
$stmt = $model->pdo->prepare($sql);
$stmt->execute($regist);
// 「complete.php」にリダイレクト
header('Location: complete.php');
完了画面作成
「regist/complete.php」を作成し、完了画面を表示します。 このとき、ユーザのセッションは削除しておきます。

regist/complete.php
<?php
require_once "../app.php";
if (isset($_SESSION[APP_KEY]['regist'])) {
unset($_SESSION[APP_KEY]['regist']);
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= SITE_TITLE ?></title>
<base href="<?= BASE_URL ?>">
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<main id="register" class="flex justify-center">
<div class="w-1/2 mt-3 p-5">
<h2 class="text-2xl mb-3 font-normal text-center">ユーザ登録完了</h2>
<div class="mb-4">
<p class="text-center text-gray-700 text-md">
ユーザ登録が完了しました。
</p>
</div>
<div class="mb-4 flex justify-center">
<a href="login/" class="w-full
mx-1 mb-4 py-2 px-4
text-gray-500
text-center
bg-gray-100
hover:bg-gray-200
rounded">
ログイン
</a>
<a href="" class="w-full
mx-1 mb-4 py-2 px-4
text-gray-500
text-center
bg-gray-100
hover:bg-gray-200
rounded">
トップ
</a>
</div>
</div>
</main>
</body>
</html>
データ確認
確認画面から【登録】ボタンをクリックし、usersテーブルにデータ登録されるか確認してみましょう。


SQLインジェクション
SQLインジェクションとは
フォームから悪意のあるデータ送信して、SQLを書き換えて実行できる可能性があります。これをSQLインジェクション攻撃といいます。
query() の危険性
送信データをそのまま query() で実行すると、SQLインジェクションの対象となります。 ログインフォームの Email欄に以下の文字を入力してログインしてみましょう。
' or 1 = '1

SQLインジェクションの結果
正しい Email を入力しなくてもユーザデータを取得できました。
array(7) {
["id"]=>
int(1)
["name"]=>
string(13) "三宅 直子"
["email"]=>
string(19) "[email protected]"
["password"]=>
...
}
実行されたSQL
SQLの条件は「Email を空欄」または「すべて」となり、すべてのユーザを取得するSQLに変換されました。
SELECT * FROM users WHERE email = '' or 1 = '1'
サニタイズ
サニタイズとは
フォーム送信で悪意のあるデータを変換して無力化することを、サニタイズ(Sanitize) といいます。
htmlspecialchars() でサニタイズ
HTMLやURLなどはSQLインジェクションの文字列を含んでいるため、htmlspecialchars() で、特殊文字をHTMLエンティティに変換します。
htmlspecialchars(値, ENT_QUOTES, "UTF-8")
例えば '(シングルクォーテーション) は ' に変換されるので、SQLインジェクション対策に有効です。
変換前
' or 1 = '1
変換後
' or 1 = '1
POSTデータのサニタイズ
check() を追加して、サニタイズ処理の実装します。
regist/confirm.php
<?php
require_once "../app.php";
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
exit;
}
$_SESSION[APP_KEY]['regist'] = $_POST;
// サニタイズ
$regist = sanitize($_POST);
function sanitize($posts)
{
foreach ($posts as $column => $post) {
$posts[$column] = htmlspecialchars($post, ENT_QUOTES, 'UTF-8');
}
return $posts;
}
?>
$_POST をサニタイズします。