PHP、第三章:会員登録システム。入力データの検証と重複チェック

PHP

前回の記事では、会員登録フォームから送信されたデータをデータベースに登録する基本機能を構築しました。この記事では、フォームから送信されたデータを安全に処理する方法を、セキュリティの観点から説明します。最初はシンプルなコードから始め、各段階で様々なバリデーション追加していくことで、最終的に堅牢でセキュアなコードに仕上げていきます。

フォームのデータ処理において、セキュリティの向上は段階的に進めることが大切です。この手法を通じて、セキュリティの重要性を理解しながら、実際に使える技術を学んでいきましょう。

基本的なデータ受け取り

このコードでは、ユーザーから送信されたデータを簡単に受け取り、特にセキュリティ対策としてパスワードをハッシュ化する方法を示します。password_hash() 関数を使用することで、データベースに安全にパスワードを保存できます。

$name = $_POST['name'] ?? '';
$mail = $_POST['mail'] ?? '';
$pass = $_POST['pass'] ?? '';
$pass = password_hash($pass, PASSWORD_DEFAULT);
PHP

説明

  1. $_POST からのデータ取得
    • フォームで送信された name, mail, pass をそれぞれ変数に格納。
    • 未入力の場合は ?? ” で空文字を代入し、エラーを防止。
  2. パスワードのハッシュ化
    • password_hash() を使用して、送信されたパスワードを安全にハッシュ化。
    • データベースに保存する際に、パスワードを直接保存せず、安全性を高める処理。

未入力チェックの追加

ユーザーがフォームに入力したデータに対して空欄チェックを行い、エラーメッセージを表示する仕組みを追加します。これにより、必要なデータが確実に入力されていることを確認できます。

$name = $_POST['name'] ?? '';
$mail = $_POST['mail'] ?? '';
$pass = $_POST['pass'] ?? '';

// エラーメッセージを格納する配列
$errors = [];

// 名前が空でないかチェック
if (empty($name)) {
    $errors[] = '名前を入力してください。';
}

// メールアドレスが空でないかチェック
if (empty($mail)) {
    $errors[] = 'メールアドレスを入力してください。';
}

// パスワードが空でないかチェック
if (empty($pass)) {
    $errors[] = 'パスワードを入力してください。';
}

// エラーがある場合はまとめて表示して終了
if (!empty($errors)) {
    foreach ($errors as $error) {
        echo $error . "<br>";
    }
    exit;
}
$pass = password_hash($pass, PASSWORD_DEFAULT);
PHP

解説

  1. 空欄チェックを導入
    • フォームの入力が空の場合にエラーメッセージを表示。
    • これにより、ユーザーが何を修正すべきか分かりやすくなります。
  2. エラー管理の開始
    • $errors 配列を使用してエラーメッセージを管理。
    • 全てのエラーをまとめて表示する仕組みを追加。

入力値の形式チェックを追加

このコードでは、ユーザーの入力値に対してより詳細な検証を行います。名前、メールアドレス、パスワードそれぞれに対して形式や文字数の制限を設けることで、意図しないデータの登録やセキュリティリスクを回避します。また、全てのエラーを一括で表示する仕組みも取り入れています。

$name = $_POST['name'] ?? '';
$mail = $_POST['mail'] ?? '';
$pass = $_POST['pass'] ?? '';

// エラーメッセージを格納する配列
$errors = [];

// 名前の形式と長さを検証
if (empty($name)) {
    $errors[] = '名前を入力してください。';
} elseif (!preg_match('/^[a-zA-Z_-]{1,20}$/', $name)) {
    $errors[] = '名前はローマ字、アンダーバー、ハイフンのみで、20文字以内にしてください。';
}

// メールアドレスの形式と長さを検証
if (empty($mail)) {
    $errors[] = 'メールアドレスを入力してください。';
} elseif (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
    $errors[] = '無効なメールアドレスです。';
} elseif (strlen($mail) > 100) {
    $errors[] = 'メールアドレスは100文字以内にしてください。';
}

// パスワードの形式と長さを検証
if (empty($pass)) {
    $errors[] = 'パスワードを入力してください。';
} elseif (!preg_match('/^[a-zA-Z0-9_\-\[\]\.\!@]{8,20}$/', $pass)) {
    $errors[] = 'パスワードはローマ字、数字、アンダーバー、ハイフン、[].!@ のみで、8~20文字にしてください。';
}

// エラーがある場合はまとめて表示して終了
if (!empty($errors)) {
    foreach ($errors as $error) {
        echo $error . "<br>";
    }
    exit;
}
$pass = password_hash($pass, PASSWORD_DEFAULT);
PHP

解説

  1. 名前の形式と長さを検証
    • 正規表現 /^[a-zA-Z_-]{1,20}$/ を使用し、名前に使える文字種と文字数を制限。
    • エラー時には具体的な修正指示を表示。
  2. メールアドレスの形式チェック
    • filter_var() を使用してメールアドレス形式を検証。
    • 入力が無効な場合はエラーとして処理。
  3. パスワードの形式と長さを検証
    • 正規表現 /^[a-zA-Z0-9_\-\[\]\.\!@]{8,20}$/ を使用して、許可される文字種と長さを厳密に制限。
  4. エラーメッセージの改良
    • 全てのフィールドについて具体的なエラーメッセージを表示し、ユーザーが修正しやすいようにしました。

メールアドレスの重複チェック

ユーザー登録システムでは、同じメールアドレスで複数回登録されないように重複チェックが必要です。データベースを使ってメールアドレスが既に存在していないかを確認します。SQLの COUNT 関数とプリペアドステートメントを使用して、安全かつ効率的に重複チェックを実装します。

  // メールアドレスの重複確認
  $checkSql = "SELECT COUNT(*) FROM users WHERE mail = ?";
  $checkStmt = $pdo->prepare($checkSql);
  $checkStmt->execute([$mail]);
  $count = $checkStmt->fetchColumn();

  if ($count > 0) {
      die("このメールアドレスはすでに登録されています。<br><a href='register.html'>戻る</a>");
  }
PHP

解説

  • データベースへの接続
    データベース接続を確立し、メールアドレスを確認する準備を行います。
  • SQL文の準備
    SELECT COUNT(*) FROM users WHERE mail = ?
    • COUNT(*) を使用して、指定されたメールアドレスが users テーブルに何件存在するかを取得します。
  • SQL文の実行
    prepare() でSQL文を準備し、execute() でメールアドレスをバインドして実行します。
  • 件数の取得
    fetchColumn() を使用して、該当する件数を取得します。
    • 0 → 重複していない
    • 1以上 → 重複している
  • 重複確認と処理
    if ($count > 0) で件数を確認し、重複している場合にエラーメッセージを表示し処理を終了します。
    • die() を使用して処理を中断。
    • メッセージ内に戻るリンクを設置して、ユーザーに戻る手段を提供します。

コードまとめ

  • 基本的なデータ受け取り
  • 未入力チェックの追加
  • 入力値の形式チェックを追加
  • メールアドレスの重複チェック

4つの機能を追加したコードをまとめました。

<?php
// データベース接続情報
$host = 'localhost';
$dbname = 'member_system';
$charset = 'utf8';
$username = 'root';
$password = 'pass';

$name = $_POST['name'] ?? '';
$mail = $_POST['mail'] ?? '';
$pass = $_POST['pass'] ?? '';

// エラーメッセージを格納する配列
$errors = [];

// 名前の形式と長さを検証
if (empty($name)) {
    $errors[] = '名前を入力してください。';
} elseif (!preg_match('/^[a-zA-Z_-]{1,20}$/', $name)) {
    $errors[] = '名前はローマ字、アンダーバー、ハイフンのみで、20文字以内にしてください。';
}

// メールアドレスの形式と長さを検証
if (empty($mail)) {
    $errors[] = 'メールアドレスを入力してください。';
} elseif (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
    $errors[] = '無効なメールアドレスです。';
} elseif (strlen($mail) > 100) {
    $errors[] = 'メールアドレスは100文字以内にしてください。';
}

// パスワードの形式と長さを検証
if (empty($pass)) {
    $errors[] = 'パスワードを入力してください。';
} elseif (!preg_match('/^[a-zA-Z0-9_\-\[\]\.\!@]{8,20}$/', $pass)) {
    $errors[] = 'パスワードはローマ字、数字、アンダーバー、ハイフン、[].!@ のみで、8~20文字にしてください。';
}

// エラーがある場合はまとめて表示して終了
if (!empty($errors)) {
    foreach ($errors as $error) {
        echo $error . "<br>";
    }
    exit;
}
$pass = password_hash($pass, PASSWORD_DEFAULT);

try {
  $dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
  $pdo = new PDO($dsn, $username, $password, [
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  ]);

  // メールアドレスの重複確認
  $checkSql = "SELECT COUNT(*) FROM users WHERE mail = ?";
  $checkStmt = $pdo->prepare($checkSql);
  $checkStmt->execute([$mail]);
  $count = $checkStmt->fetchColumn();

  if ($count > 0) {
      die("このメールアドレスはすでに登録されています。<br><a href='register.html'>戻る</a>");
  }

  $sql = "INSERT INTO users (name, mail, pass) VALUES (?, ?, ?)";
  $stmt = $pdo->prepare($sql);
  $stmt->execute([$name, $mail, $pass]);

  echo "登録が完了しました!";
  echo '<br><a href="index.html">トップに戻る</a>';
} catch (PDOException $e) {
  echo "エラーが発生しました: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
}
?>
insert.php

記事まとめ

フォームから送信されたデータに対し、安全性とデータの整合性を確保するための多岐にわたる対策を講じています。未入力チェックによるエラー防止、名前・メールアドレス・パスワードの形式と長さの検証、そしてエラーメッセージの一元管理を実現しました。

ただし、これらはセキュリティを確保するための一例であり、他にも様々な方法や実装の仕方があります。システムの要件や規模に応じて、より高度なセキュリティ対策や機能の追加を検討することも重要です。

  1. データ未入力の場合のエラー防止
    • $_POST からのデータ取得時に ?? ” を使用し、未入力時にエラーを防止。
  2. 未入力チェック
    • 名前、メールアドレス、パスワードが空でないかを確認。
  3. エラーメッセージの管理
    • $errors 配列を使用し、エラーメッセージをまとめて管理。
    • 全てのエラーをユーザーに一括表示。
  4. 名前の形式と長さの検証
    • 正規表現を使用し、名前に使用できる文字(ローマ字、アンダーバー、ハイフン)と最大20文字の制限を実装。
  5. メールアドレスの形式と長さの検証
    • FILTER_VALIDATE_EMAIL を使用してメールアドレス形式を確認。
    • strlen() で最大100文字の制限を追加。
  6. パスワードの形式と長さの検証
    • 正規表現を使用して、パスワードに含める文字種(ローマ字、数字、アンダーバー、ハイフン、[].!@)を制限。
    • 最大20文字、最小8文字の長さを確認。
  7. パスワードのハッシュ化
    • password_hash() を使用して、パスワードを安全にハッシュ化し、元の値を保持せず保存可能な状態にする。
  8. メールアドレスの重複チェック
    • SQLのCOUNT(*)を使用してメールアドレスの重複件数を取得し、if ($count > 0) で重複を確認します。
    • 重複時にはエラーメッセージを表示し、処理を中断してユーザーに戻るリンクを提供します。

次回の記事では、登録フォームの改善を致します。さらに安全で使いやすいフォームになります。お楽しみに!

コメント

タイトルとURLをコピーしました