6.
ユーザ登録
セッション
入力したデータを入力画面で再利用できるように、セッションに保存します。
ファイル構成
php_sns/
├── lib/
│ ├── Database.php
│ └── Sanitize.php
├── regist/
│ ├── add.php
│ ├── index.php
│ └── input.php
├── app.php
└── env.php
セッション登録処理
セッション開始処理
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/";
const COMPONENT_DIR = __DIR__ . "/components/";
require_once LIB_DIR . 'Database.php';
セッション登録
POSTデータをセッションに登録します。$_SESSION
のキーはenv.php
で設定した APP_KEY
と regist
にします。
regist/add.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
<?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);
// ログインページにリダイレクト
header('Location: ../login/');
データ確認
確認画面から【登録】ボタンをクリックし、usersテーブルにデータ登録されるか確認してみましょう。


セキュリティ処理
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データのサニタイズ
lib/Sanitize.php
を作成し、サニタイズの関数を追加します。
lib/Sanitize.php
<?php
/**
* サニタイズ関数1
* @param string $str サニタイズする文字列
* @return string サニタイズ後の文字列
*/
function h($str)
{
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
/**
* サニタイズ関数2
* @param mixed $data サニタイズするデータ
* @return mixed サニタイズ後のデータ
*/
function sanitize($data)
{
// 配列の場合、再帰的にサニタイズ
if (is_array($data)) {
return array_map('sanitize', $data);
}
// サニタイズ処理
// htmlspecialchars() を使用して、HTMLエスケープを行う
// trim() を使用して、前後の空白を削除
// ENT_QUOTES を指定して、シングルクォートとダブルクォートをエスケープ
return trim(h($data, ENT_QUOTES, 'UTF-8'));
}
sanitize()
を追加して、サニタイズ処理の実装します。
regist/add.php
<?php
require_once "../app.php";
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
exit;
}
$_SESSION[APP_KEY]['regist'] = $_POST;
// サニタイズ
$regist = sanitize($_POST);
?>