セキュアなフォーム認証の実装(PHP + JS)

▼ 前提条件

▼ バージョン1:PHPのみのセキュア認証フォーム

login.php(ログイン画面)

#code(php){{

?php session_start(); $csrf_token = bin2hex(random_bytes(32)); $_SESSION['csrf_token'] = $csrf_token; ?>

form method="post" action="auth.php">

   <label for="username">ユーザー名:</label>
   <input type="text" name="username" required><br>
   <label for="password">パスワード:</label>
   <input type="password" name="password" required><br>
   <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
   <button type="submit">ログイン</button>

/form>

#}}

auth.php(認証処理)

#code(php){{

?php session_start();

if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {

   die('CSRFトークンが無効です');

}

$username = $_POST['username'] ?? ''; $password = $_POST['password'] ?? '';

$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?"); $stmt->execute([$username]); $user = $stmt->fetch();

if ($user && password_verify($password, $user['password'])) {

   session_regenerate_id(true);
   $_SESSION['loggedin'] = true;
   $_SESSION['username'] = $user['username'];
   echo "ログイン成功!";

} else {

   echo "ユーザー名またはパスワードが間違っています";

}

#}}

▼ バージョン2:PHP + JavaScriptでのフォーム(UX改善)

login.php(クライアント側入力チェックあり)

#code(php){{

?php session_start(); $csrf_token = bin2hex(random_bytes(32)); $_SESSION['csrf_token'] = $csrf_token; ?>

form id="loginForm" method="post" action="auth.php">

   <label for="username">ユーザー名:</label>
   <input type="text" id="username" name="username" required><br>
   <label for="password">パスワード:</label>
   <input type="password" id="password" name="password" required><br>
   <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
   <button type="submit">ログイン</button>

/form>

script> document.getElementById('loginForm').addEventListener('submit', function(e) {

   const username = document.getElementById('username').value.trim();
   const password = document.getElementById('password').value.trim();
   if (username === '' || password === '') {
       e.preventDefault();
       alert("ユーザー名とパスワードは必須です。");
   }

});

/script>

#}}

auth.php は同じ内容を再利用可能

▼ セキュリティ対策チェックリスト

▼ 補足:次に実装する機能例