Here’s a review of the typical approaches, covering security, best practices, and common pitfalls. Overall Assessment Most basic online tutorials get the concept right (token + database) but often miss critical security and UX details. A solid implementation requires careful handling of token storage, expiration, re-verification, and attack prevention. What a Good Implementation Should Include 1. Verification Table Structure CREATE TABLE email_verifications ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, token VARCHAR(64) NOT NULL UNIQUE, expires_at DATETIME NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); ✅ Good: Unique token, explicit expiry, foreign key. 2. Token Generation (Secure) // GOOD: Cryptographically random $token = bin2hex(random_bytes(32)); // 64 chars // BAD: md5(uniqid()) or rand() 3. Verification Endpoint // verify.php?token=abc123... $token = $_GET['token'] ?? ''; // Use prepared statement $stmt = $pdo->prepare("SELECT user_id, expires_at FROM email_verifications WHERE token = ?"); $stmt->execute([$token]); $record = $stmt->fetch();
// Mark user as verified $update = $pdo->prepare("UPDATE users SET email_verified = 1 WHERE id = ?"); $update->execute([$record['user_id']]); verify email php
// Delete used token $delete = $pdo->prepare("DELETE FROM email_verifications WHERE token = ?"); $delete->execute([$token]); Here’s a review of the typical approaches, covering
if (!$record) die("Invalid verification link."); What a Good Implementation Should Include 1
if (new DateTime() > new DateTime($record['expires_at'])) die("Link expired. Request a new one.");
// Good: "Verification successful! You may now log in." // Better: "Email verified! Redirecting to login..." header("Refresh: 3; url=/login.php"); ✅ Auto-delete old expired tokens via cron or during resend DELETE FROM email_verifications WHERE expires_at < NOW() ✅ Use HTTPS exclusively if (empty($_SERVER['HTTPS'])) header("Location: https://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); exit();