Master secure user authentication in PHP. Learn how to build a robust login system using PDO, prepared statements, and password hashing. Free source code included.
Introduction
In 2026, a "simple" login script is no longer enough. With evolving cyber threats, developers must prioritize security by default. In this tutorial, we will build a secure PHP login system using PDO (PHP Data Objects) to prevent SQL injection and password_hash() for industry-standard credential storage.
Prerequisites
A local server environment (XAMPP, MAMP, or Docker).
Basic knowledge of PHP and MySQL.
PHP 8.2 or higher (recommended for the latest security patches).
Step 1: Database Architecture
First, we need a table to store our users. Using INT UNSIGNED for IDs and VARCHAR(255) for hashed passwords ensures scalability and security.
CREATE DATABASE auth_system;
USE auth_system;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Step 2: Database Connection (config.php)
We use PDO instead of mysqli because it provides a more consistent interface and supports named parameters, which are easier to read and more secure.
<?php
$host = 'localhost';
$db = 'auth_system';
$user = 'root';
$pass = ''; // Your password
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
?>
Step 3: Secure Password Hashing
Security Tip: Never use
md5()orsha1(). In 2026, these are considered "broken" for password storage. Use PHP’s native functions:
Registration:
$hash = password_hash($password, PASSWORD_DEFAULT);Verification:
password_verify($password, $hash);
Step 4: The Login Logic (login.php)
This script handles user input and validates credentials against the database. Notice the use of prepared statements—this is your primary defense against SQL Injection.
<?php
session_start();
require 'config.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$email = $_POST['email'];
$password = $_POST['password'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password_hash'])) {
// Successful login
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
header("Location: dashboard.php");
exit;
} else {
$error = "Invalid email or password.";
}
}
?>
<form method="POST">
<input type="email" name="email" placeholder="Email" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Login</button>
</form>
Key Security Checklist for 2026
To ensure your blog ranks high, include these "Expert Insights" which search engines love:
HTTPS Always: Encrypt data in transit.
Session Hijacking Protection: Use
session_regenerate_id(true)after a successful login.Cross-Site Request Forgery (CSRF): Always implement CSRF tokens in your forms.
Input Sanitization: Even with prepared statements, sanitize output using
htmlspecialchars()to prevent XSS.
Conclusion
Building a login system is a rite of passage for web developers, but doing it securely is what separates professionals from hobbyists. By using PDO and Password Hashing, you’ve created a foundation that protects user data.