Login and user registration are the foundation of every web application that requires user accounts. Whether you are building an online store, a blog, or a web service, you must provide a secure registration and login process. In this article, we will cover the entire process — from the form to secure password storage and session management.
User Registration
Registration is the process where a user creates a new account. The core components of registration are:
- HTML form: User enters a username, email, and password
- Server-side validation: Server checks whether the data is valid and whether the user already exists
- Password hashing: The password is never stored in plain text — it is always hashed
- Database storage: User data (without the password) and the password hash are stored in the database
HTML Registration Form
Let's start with a simple registration form. We will use the POST method, as it is more secure for sending sensitive data:
<!-- register.html -->
<form action="register.php" method="POST">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<button type="submit">Register</button>
</form>
PHP Registration Processing
When the user submits the form, the data is received by register.php. Three key things happen here: validation, checking for existing users, and password hashing.
<?php
// register.php
// Check if the form was submitted
if ($_SERVER["REQUEST_METHOD"] === "POST") {
// Retrieve and sanitize input data
$username = htmlspecialchars($_POST["username"]);
$email = filter_var($_POST["email"], FILTER_VALIDATE_EMAIL);
$password = $_POST["password"];
// Validation
if (!$username || !$email || strlen($password) < 8) {
die("Please enter valid data. Password must be at least 8 characters.");
}
// Hash the password (bcrypt)
$password_hash = password_hash($password, PASSWORD_DEFAULT);
// Prepare SQL query (PDO with prepared statements)
$stmt = $pdo->prepare("
INSERT INTO users (username, email, password_hash)
VALUES (:username, :email, :password_hash)
");
// Execute with bound parameters (prevents SQL injection)
$stmt->execute([
":username" => $username,
":email" => $email,
":password_hash" => $password_hash
]);
echo "Registration successful! You can now log in.";
}
?>
Here are two key security functions:
| Function | Purpose | When to use |
|---|---|---|
| password_hash() | Creates a secure password hash (bcrypt) | During new user registration |
| htmlspecialchars() | Prevents XSS attacks (Cross-Site Scripting) | Whenever outputting user input |
| filter_var() | Validates email address format | During email validation |
| PDO prepared statements | Prevents SQL injection attacks | For every database query with user data |
User Login
Login is the process where a user accesses their existing account. The server checks whether the entered password matches the stored hash.
<!-- login.html -->
<form action="login.php" method="POST">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<button type="submit">Log in</button>
</form>
PHP Login Processing
<?php
// login.php
session_start();
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$email = filter_var($_POST["email"], FILTER_VALIDATE_EMAIL);
$password = $_POST["password"];
if (!$email || !$password) {
die("Please enter valid data.");
}
// Find user by email
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
$stmt->execute([":email" => $email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// Verify password
if ($user && password_verify($password, $user["password_hash"])) {
// Password is correct — create session
$_SESSION["user_id"] = $user["id"];
$_SESSION["username"] = $user["username"];
echo "Welcome, " . htmlspecialchars($user["username"]) . "!";
header("Location: dashboard.php");
exit;
} else {
// Incorrect password or user does not exist
echo "Incorrect email or password.";
}
}
?>
Important: the error message should always be the same, regardless of whether the user does not exist or the password is incorrect. This prevents an attacker from determining whether an email is registered.
Session Management
Once the user is logged in, the server needs to remember who they are. This is achieved with sessions. A session starts with session_start() at the top of every protected page:
<?php
// dashboard.php — protected page
session_start();
// Check if user is logged in
if (!isset($_SESSION["user_id"])) {
header("Location: login.html");
exit;
}
echo "Welcome to the dashboard, "
. htmlspecialchars($_SESSION["username"]) . "!";
?>
Logging out the user:
<?php
// logout.php
session_start();
session_unset(); // Clear all session variables
session_destroy(); // Destroy the session
header("Location: login.html");
exit;
?>
SQL Users Table
Here is an example SQL table for storing users:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Security Recommendations
- Never store passwords in plain text: Always use
password_hash()with the bcrypt algorithm - Use PDO with prepared statements: This is the only safe way to interact with a database
- Validate on the server side: HTML validation (required, type="email") is not enough — always validate in PHP too
- Use HTTPS: Without HTTPS, all data including passwords is transmitted in plain text
- Limit login attempts: Prevent brute-force attacks by limiting attempts (e.g., 5 attempts in 10 minutes)
- Never output user data without htmlspecialchars(): Prevent XSS attacks
- Require strong passwords: Demand a minimum of 8 characters, combining letters, numbers, and special characters
- Consider two-factor authentication (2FA): An additional layer of security for sensitive applications
Common Mistakes
| Mistake | Why it's dangerous | Correct solution |
|---|---|---|
| Storing passwords in plain text | If the database is breached, all passwords are compromised | password_hash() with bcrypt |
| Using md5() or sha1() for passwords | These algorithms are not secure for passwords | password_hash() (uses bcrypt) |
| SQL queries with string concatenation | Vulnerable to SQL injection | PDO prepared statements with bound parameters |
| Outputting user input without sanitization | Vulnerable to XSS attacks | htmlspecialchars() |
| Different errors for "user not found" and "wrong password" | Allows attackers to determine if an email is registered | Always the same message: "Incorrect email or password" |
Conclusion
Implementing a secure login and registration is not complex if you follow basic security principles: password hashing, using prepared SQL statements, server-side validation, and protection against XSS attacks. These components are the foundation of every secure web application.
For a detailed example of forms, validation, and password protection in PHP, check out the tutorial: PHP Forms with Secure Processing and Password Protection.