セキュアなフォーム認証の実装(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 は同じ内容を再利用可能

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

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

 
 
 
 
 
 
 

 
 
 
 
 
 
 

データベースを使わないセキュアなログイン認証

▼ 概要

▼ 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'] ?? '';

$users = [

   'admin' => '$2y$10$8s4zM5k7BnEl9P57lY2qDO1LVJ3AsGHDDe8zUlqD9uJXsM5Xx9RUW', // password: mypass123
   'user'  => '$2y$10$ZK1K9yUO7cV9GrGc9eZXzO.xFNZ4kg9w6FxJYVbmuMyO0r3aPpGQK'  // password: secret456

];

if (isset($users[$username]) && password_verify($password, $users[$username])) {

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

} else {

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

}

#}}

▼ パスワードハッシュの生成方法(事前に実行)

以下のようなスクリプトで一度ハッシュを生成して、auth.php にコピペします。

#code(php){{

?php echo password_hash('mypass123', PASSWORD_DEFAULT);

#}}

▼ 注意点・セキュリティ強化策

▼ この方式が向いているケース