Content · Web Development · PHP

Creating a Database-Backed Login System

Authenticate a submitted password against a stored password_hash value and establish a fresh session. Use password_verify, session_regenerate_id(true), secure cookies, CSRF protection and rate limiting; never store plaintext passwords.

1 views Published Oct 17, 2026 Updated Oct 17, 2026 admin

Authenticate a submitted password against a stored password_hash value and establish a fresh session.

The examples focus on the operation itself, including the important distinction: A login session proves an authenticated request; a database row stores account details.

What It Does


Use the examples below to observe the behavior on a small dataset before extending it to application code.

Before You Start


The examples use PHP 8.2+ features where shown. Database snippets require the corresponding PDO driver, demo tables, and credentials configured outside source code.

Example 1: Basic Use


Use HTTPS and a form that receives a session-specific CSRF token. For production also add rate limits, account lockout policy, and server-side session expiry. The users table needs a password_hash column.

<?php
session_set_cookie_params(["secure" => true, "httponly" => true, "samesite" => "Lax"]);
session_start();
if (($_SERVER["REQUEST_METHOD"] ?? "") !== "POST") { http_response_code(405); exit; }
$token = $_POST["csrf"] ?? "";
if (!is_string($token) || !hash_equals($_SESSION["csrf"] ?? "", $token) || $token === "") { http_response_code(403); exit; }
$email = filter_var($_POST["email"] ?? "", FILTER_VALIDATE_EMAIL);
$password = $_POST["password"] ?? "";
if ($email === false || !is_string($password)) { http_response_code(422); exit; }
$pdo = new PDO(
    "mysql:host=" . (getenv("DB_HOST") ?: "127.0.0.1") . ";dbname=" . (getenv("DB_NAME") ?: "demo") . ";charset=utf8mb4",
    getenv("DB_USER") ?: "app",
    getenv("DB_PASSWORD") ?: "",
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false]
);
$stmt = $pdo->prepare("SELECT id, password_hash FROM users WHERE email = ? LIMIT 1");
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user["password_hash"])) {
    session_regenerate_id(true);
    $_SESSION["user_id"] = (int) $user["id"];
    echo "Authenticated";
} else { http_response_code(401); echo "Invalid credentials"; }

This example is meant for a configured application environment; output depends on the installed extension, external service, or database contents.

Example 2: Checking Behavior


Use HTTPS and a form that receives a session-specific CSRF token. For production also add rate limits, account lockout policy, and server-side session expiry. The users table needs a password_hash column. SameSite=Strict is an option for login forms that do not require cross-site navigation to retain cookies.

<?php
session_set_cookie_params(["secure" => true, "httponly" => true, "samesite" => "Strict"]);
session_start();
if (($_SERVER["REQUEST_METHOD"] ?? "") !== "POST") { http_response_code(405); exit; }
$token = $_POST["csrf"] ?? "";
if (!is_string($token) || !hash_equals($_SESSION["csrf"] ?? "", $token) || $token === "") { http_response_code(403); exit; }
$email = filter_var($_POST["email"] ?? "", FILTER_VALIDATE_EMAIL);
$password = $_POST["password"] ?? "";
if ($email === false || !is_string($password)) { http_response_code(422); exit; }
$pdo = new PDO(
    "mysql:host=" . (getenv("DB_HOST") ?: "127.0.0.1") . ";dbname=" . (getenv("DB_NAME") ?: "demo") . ";charset=utf8mb4",
    getenv("DB_USER") ?: "app",
    getenv("DB_PASSWORD") ?: "",
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false]
);
$stmt = $pdo->prepare("SELECT id, password_hash FROM users WHERE email = ? LIMIT 1");
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user["password_hash"])) {
    session_regenerate_id(true);
    $_SESSION["user_id"] = (int) $user["id"];
    echo "Authenticated";
} else { http_response_code(401); echo "Invalid credentials"; }

This example is meant for a configured application environment; output depends on the installed extension, external service, or database contents.

Example 3: Practical Example


Use HTTPS and a form that receives a session-specific CSRF token. For production also add rate limits, account lockout policy, and server-side session expiry. The users table needs a password_hash column. session_write_close releases the session lock after the authentication result is stored.

<?php
session_set_cookie_params(["secure" => true, "httponly" => true, "samesite" => "Lax"]);
session_start();
if (($_SERVER["REQUEST_METHOD"] ?? "") !== "POST") { http_response_code(405); exit; }
$token = $_POST["csrf"] ?? "";
if (!is_string($token) || !hash_equals($_SESSION["csrf"] ?? "", $token) || $token === "") { http_response_code(403); exit; }
$email = filter_var($_POST["email"] ?? "", FILTER_VALIDATE_EMAIL);
$password = $_POST["password"] ?? "";
if ($email === false || !is_string($password)) { http_response_code(422); exit; }
$pdo = new PDO(
    "mysql:host=" . (getenv("DB_HOST") ?: "127.0.0.1") . ";dbname=" . (getenv("DB_NAME") ?: "demo") . ";charset=utf8mb4",
    getenv("DB_USER") ?: "app",
    getenv("DB_PASSWORD") ?: "",
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false]
);
$stmt = $pdo->prepare("SELECT id, password_hash FROM users WHERE email = ? LIMIT 1");
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user["password_hash"])) {
    session_regenerate_id(true);
    $_SESSION["user_id"] = (int) $user["id"];
    echo "Authenticated";
} else { http_response_code(401); echo "Invalid credentials"; }
session_write_close();

This example is meant for a configured application environment; output depends on the installed extension, external service, or database contents.

Important Behavior and Edge Cases


Use password_verify, session_regenerate_id(true), secure cookies, CSRF protection and rate limiting; never store plaintext passwords.

Comparison with Related Approaches


ApproachDifference
Creating a Database-Backed Login SystemAuthenticate a submitted password against a stored password_hash value and establish a fresh session.
Related API or alternativeA login session proves an authenticated request; a database row stores account details.

Common Beginner Mistakes


MistakeCorrect approach
Ignoring the operation-specific limitsUse password_verify, session_regenerate_id(true), secure cookies, CSRF protection and rate limiting; never store plaintext passwords.
Confusing it with a related APIA login session proves an authenticated request; a database row stores account details.

Key Takeaways


  • Authenticate a submitted password against a stored password_hash value and establish a fresh session.
  • Use password_verify, session_regenerate_id(true), secure cookies, CSRF protection and rate limiting; never store plaintext passwords.
  • A login session proves an authenticated request; a database row stores account details.

Use Creating a Database-Backed Login System when its documented behavior matches the problem. Test the relevant edge cases in your own runtime before deploying it.

Share this resourceSend this page to someone who may find it useful.