A secure login and registration system is the backbone of any dynamic web application. Whether you are building an e-commerce platform, a SaaS product, or a membership blog, users need a safe way to create accounts and access their dashboards.
In this 2027 Secure Authentication Guide, we will build an advanced Login and Registration form using PHP 8+, PDO (PHP Data Objects), and OOP (Object-Oriented Programming) concepts.
To meet modern security standards, this tutorial strictly implements:
- PDO Prepared Statements: To prevent SQL Injection.
- CSRF Tokens: To prevent Cross-Site Request Forgery attacks.
- Secure Password Hashing: Using modern
password_hash() algorithms.
- Session Security: Using
session_regenerate_id(true) to prevent session fixation and hijacking.
- Strict Backend Validation: Validating emails, string lengths, and duplicate records.
1. Create a MySQL Database Table
First, we need to create a database table to store user information securely. Execute the following SQL query in your phpMyAdmin. Notice we use DATETIME DEFAULT CURRENT_TIMESTAMP to handle dates automatically.
CREATE TABLE `users` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`fname` VARCHAR(100) DEFAULT NULL,
`lname` VARCHAR(100) DEFAULT NULL,
`username` VARCHAR(50) NOT NULL,
`email` VARCHAR(150) NOT NULL,
`password` VARCHAR(255) NOT NULL,
`created_date` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2. Secure PDO Connection File (config.php)
Let's create the database connection file. We will use PDO because it supports multiple database types and offers robust security features.
config.php
<?php
define('DBNAME', 'test'); // Your database name
define('DBUSER', 'root'); // Your database username
define('DBPASS', ''); // Your database password
define('DBHOST', 'localhost');
try {
$dsn = 'mysql:host=' . DBHOST . ';dbname=' . DBNAME . ';charset=utf8mb4';
$db = new PDO($dsn, DBUSER, DBPASS);
// Set PDO error mode to exception for better debugging
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch(PDOException $e) {
die("Database Connection Failed: " . $e->getMessage());
}
?>
3. Autoload & Session Security (autoload.php)
The autoload.php file handles two critical tasks: automatically loading our OOP classes and setting up secure sessions with a CSRF Token.
autoload.php
<?php
// 1. Enforce Secure Sessions
session_set_cookie_params([
'httponly' => true,
'samesite' => 'Strict'
]);
session_start();
// 2. Generate CSRF Token for Form Security
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
require_once("config.php");
// 3. Autoload Classes automatically
spl_autoload_register(function($className) {
require_once("classes/$className.php");
});
// Initialize User Object
$getUser = new User($db);
?>
Note: Create a folder named classes in your project directory. We will save our PHP class files inside this folder.
4. Abstract Basic Class (classes/Basic.php)
Abstraction hides complex implementation details. We create an abstract Basic class to handle data sanitization, which we will extend in our User class.
classes/Basic.php
<?php
abstract class Basic {
// Sanitize user inputs to prevent XSS and malformed data
public function sanitize($var, $type) {
$filter = false;
switch($type) {
case 'email':
$var = substr($var, 0, 254);
$filter = FILTER_VALIDATE_EMAIL;
break;
case 'int':
$filter = FILTER_VALIDATE_INT;
break;
case 'string':
default:
$var = htmlspecialchars(trim($var), ENT_QUOTES, 'UTF-8');
return $var;
}
return trim(filter_var($var, $filter));
}
public function easy_date($date) {
return date('d M Y, h:i A', strtotime($date));
}
}
?>
5. Complete User Auth Class (classes/User.php)
This is the core engine of our application. It handles validation, registration, secure login (with session_regenerate_id), and user fetching. Save this entire code inside the classes folder.
classes/User.php
<?php
class User extends Basic {
private $dbConnection;
function __construct($db) {
$this->dbConnection = $db;
}
// --- REGISTRATION VALIDATION ---
public function validation($fname, $lname, $username, $email, $password, $passwordConfirm) {
$error = [];
$fname = $this->sanitize($fname, 'string');
$lname = $this->sanitize($lname, 'string');
$username = $this->sanitize($username, 'string');
$email = $this->sanitize($email, 'email');
if(strlen($fname) < 3 || !preg_match("/^[a-zA-Z\s]+$/", $fname)) {
$error[] = 'First Name must be at least 3 letters and contain no symbols.';
}
if(!preg_match("/^[a-z0-9_-]{3,20}$/i", $username)) {
$error[] = 'Username must be 3-20 characters long and contain no spaces.';
}
if(!$email) {
$error[] = 'Please enter a valid email address.';
}
// Check for duplicate Username
$stmt = $this->dbConnection->prepare("SELECT id FROM users WHERE username = :username");
$stmt->execute([':username' => $username]);
if($stmt->rowCount() > 0) { $error[] = 'Username already exists.'; }
// Check for duplicate Email
$stmt = $this->dbConnection->prepare("SELECT id FROM users WHERE email = :email");
$stmt->execute([':email' => $email]);
if($stmt->rowCount() > 0) { $error[] = 'Email already exists.'; }
if(strlen($password) < 6) {
$error[] = 'Password must be at least 6 characters long.';
}
if($password !== $passwordConfirm) {
$error[] = 'Passwords do not match.';
}
return count($error) > 0 ? $error : null;
}
// --- REGISTER USER ---
public function register($fname, $lname, $username, $email, $password) {
$fname = $this->sanitize($fname, 'string');
$lname = $this->sanitize($lname, 'string');
$username = $this->sanitize($username, 'string');
// Hash password securely (Automatically uses bcrypt/argon2id depending on PHP version)
$hashedpassword = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (fname, lname, username, email, password)
VALUES (:fname, :lname, :username, :email, :password)";
$stmt = $this->dbConnection->prepare($sql);
return $stmt->execute([
':fname' => $fname,
':lname' => $lname,
':username' => $username,
':email' => $email,
':password' => $hashedpassword
]);
}
// --- LOGIN USER ---
public function login($login_var, $password) {
$login_var = $this->sanitize($login_var, 'string');
$sql = "SELECT id, password FROM users WHERE username = :login_var OR email = :login_var LIMIT 1";
$stmt = $this->dbConnection->prepare($sql);
$stmt->execute([':login_var' => $login_var]);
$user = $stmt->fetch();
// Verify Hash
if($user && password_verify($password, $user['password'])) {
// CRITICAL: Prevent Session Fixation attacks
session_regenerate_id(true);
$_SESSION["logged_in"] = true;
$_SESSION["userid"] = $user['id'];
return true;
}
return false;
}
public function is_loggedin() {
return (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true);
}
public function fetch_user($userid) {
$stmt = $this->dbConnection->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute([':id' => $userid]);
return $stmt->fetch();
}
public function logout() {
session_unset();
session_destroy();
}
}
?>
6. Secure Registration Form (signup.php)
We use Bootstrap 5 to create a responsive registration UI. Notice the inclusion of the <input type="hidden" name="csrf_token"> field. This ensures that the form submission is genuine and prevents automated bot attacks.
signup.php
<?php
require_once("autoload.php");
if($getUser->is_loggedin()) {
header("Location: account.php");
exit();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Secure Signup - Techno Smarter</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow-sm p-4">
<h3 class="text-center mb-4">Create an Account</h3>
<?php
if(isset($_POST['submit_form'])) {
// CSRF Token Validation
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die('<div class="alert alert-danger">CSRF token validation failed!</div>');
}
$fname = $_POST['fname'];
$lname = $_POST['lname'];
$username = $_POST['username'];
$email = $_POST['email'];
$password = $_POST['password'];
$passwordConfirm = $_POST['passwordConfirm'];
$error = $getUser->validation($fname, $lname, $username, $email, $password, $passwordConfirm);
if($error === null) {
if($getUser->register($fname, $lname, $username, $email, $password)) {
header("Location: login.php?register=success");
exit();
} else {
$error[] = 'Something went wrong. Please try again.';
}
}
// Display Errors
if(isset($error)) {
foreach($error as $err) {
echo '<div class="alert alert-danger">'.$err.'</div>';
}
}
}
?>
<form action="" method="POST">
<!-- CSRF Token Hidden Field -->
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
<div class="row">
<div class="col-sm-6 mb-3">
<label>First Name</label>
<input type="text" class="form-control" name="fname" required>
</div>
<div class="col-sm-6 mb-3">
<label>Last Name</label>
<input type="text" class="form-control" name="lname" required>
</div>
</div>
<div class="row">
<div class="col-sm-6 mb-3">
<label>Username</label>
<input type="text" class="form-control" name="username" required>
</div>
<div class="col-sm-6 mb-3">
<label>Email Address</label>
<input type="email" class="form-control" name="email" required>
</div>
</div>
<div class="row">
<div class="col-sm-6 mb-3">
<label>Password</label>
<input type="password" class="form-control" name="password" required>
</div>
<div class="col-sm-6 mb-3">
<label>Confirm Password</label>
<input type="password" class="form-control" name="passwordConfirm" required>
</div>
</div>
<button type="submit" class="btn btn-primary w-100" name="submit_form">Sign Up</button>
</form>
<p class="text-center mt-3">Already have an account? <a href="login.php">Log In</a></p>
</div>
</div>
</div>
</div>
</body>
</html>
7. Secure Login Form (login.php)
The login form validates the user's credentials against the hashed password stored in the database. It also includes the CSRF protection token.
login.php
<?php
require_once("autoload.php");
if($getUser->is_loggedin()) {
header("Location: account.php");
exit();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Secure Login - Techno Smarter</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-5">
<div class="card shadow-sm p-4">
<h3 class="text-center mb-4">User Login</h3>
<?php
if(isset($_GET['register']) && $_GET['register'] == 'success') {
echo '<div class="alert alert-success">Registration successful! Please log in.</div>';
}
if(isset($_POST['submit_form'])) {
// CSRF Validation
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die('<div class="alert alert-danger">CSRF Validation Failed!</div>');
}
$login_var = $_POST['login_var'];
$password = $_POST['password'];
if($getUser->login($login_var, $password)) {
header("Location: account.php");
exit();
} else {
echo '<div class="alert alert-danger">Invalid Username or Password!</div>';
}
}
?>
<form action="" method="POST">
<!-- CSRF Token Hidden Field -->
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
<div class="mb-3">
<label>Username or Email</label>
<input type="text" name="login_var" class="form-control" required>
</div>
<div class="mb-3">
<label>Password</label>
<input type="password" name="password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary w-100" name="submit_form">Login</button>
</form>
<p class="text-center mt-3">Don't have an account? <a href="signup.php">Sign up</a></p>
</div>
</div>
</div>
</div>
</body>
</html>
8. Protected User Dashboard (account.php & logout.php)
Once logged in securely, the user is redirected to their dashboard. This page fetches their details securely using their active session ID.
account.php
<?php
require_once("autoload.php");
// Restrict access to logged-in users only
if(!$getUser->is_loggedin()) {
header("Location: login.php");
exit();
}
// Fetch User Data
$userData = $getUser->fetch_user($_SESSION['userid']);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Dashboard</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card shadow-sm p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4>Welcome, <?php echo htmlspecialchars($userData['fname']); ?>! ????</h4>
<a href="logout.php" class="btn btn-danger btn-sm">Logout</a>
</div>
<hr>
<p><strong>Username:</strong> <?php echo htmlspecialchars($userData['username']); ?></p>
<p><strong>Email Address:</strong> <?php echo htmlspecialchars($userData['email']); ?></p>
<p><strong>Member Since:</strong> <?php echo $getUser->easy_date($userData['created_date']); ?></p>
</div>
</div>
</div>
</div>
</body>
</html>
logout.php
<?php
require_once("autoload.php");
$getUser->logout();
header("Location: login.php");
exit();
?>
This is now a fully production-ready, modern, and highly secure PHP 8+ Login & Registration System. It protects your application against the most common OWASP vulnerabilities like SQLi, CSRF, and Session Hijacking.
Recommended Posts:-