Login System
Login Flow Overview
A safe login flow: validate input, fetch user by email, verify password hash, regenerate session ID, store minimal session state, and return a safe message. Never reveal whether the email exists.
Minimal DB Schema (Example)
Your real schema may differ, but the idea is: store email + password_hash + role.
-- users table (example) -- id (int), email (varchar), password_hash (varchar), role (varchar), created_at (datetime)
Login Handler (POST)
Validate inputs, verify password, and start a session. Use a generic error message for all failures.
<?php
session_start();
$email = trim($_POST["email"] ?? "");
$password = $_POST["password"] ?? "";
if ($email === "" || $password === "") {
echo "Invalid credentials";
exit;
}
$stmt = $pdo->prepare("SELECT id, email, password_hash, role FROM users WHERE email = ? LIMIT 1");
$stmt->execute([$email]);
$user = $stmt->fetch();
if (!$user || !password_verify($password, $user["password_hash"])) {
// optional: add delay or rate limiting (see below)
echo "Invalid credentials";
exit;
}
// prevent session fixation
session_regenerate_id(true);
$_SESSION["user_id"] = (int)$user["id"];
$_SESSION["role"] = $user["role"];
echo "OK";
Basic Brute-force Protection
At minimum, rate limit login attempts by IP/email. A quick baseline is to add a small delay on failure and log attempts. Stronger options: Redis rate limit, per-account lockouts, and monitoring.
<?php
// On failure (baseline):
usleep(200000); // 200ms delay (small friction)
error_log("Login failed for email: " . $email);
Production Tip
Use HTTPS, secure session cookies, generic error messages, session regeneration on login, and real rate limiting. Log failures (without storing raw passwords) and monitor suspicious patterns.