Implementing a secure “Remember Me” feature requires moving away from simply extending a standard session or storing raw credentials. The industry standard for safe persistence is the Selector and Validator Pattern, originally popularized by Charles Miller and refined by Paragon Initiative. This method mitigates both database leaks and timing attacks. 🧱 The Architecture: Selector & Validator
Never store user IDs, usernames, or passwords in cookies. Instead, generate a unique, split token consisting of two cryptographically secure random strings.
Selector: A public, random string used to look up the database record. Validator: A secret, random string used to verify the user.
[ Client Browser Cookie ] | Selector | Validator | | | | (Look up) | (SHA-256 Hash) v v [ Database Row Lookup ] —-> Matches Hashed Validator? 🛠️ Step-by-Step Database & Cookie Setup 1. Database Schema
Create a dedicated rememberme_tokens table. Store only the SHA-256 hash of the validator, never the plaintext version. selector: Fixed-length string (char(16)), indexed. hashed_validator: Fixed-length string (char(64)). user_id: Integer/UUID referencing your users table. expires_at: Timestamp enforcing absolute expiration. 2. Issuing the Cookie
When the user checks “Remember Me” and logs in successfully:
Generate a 16-byte random selector using a cryptographically secure pseudorandom number generator (CSPRNG). Generate a 32-byte random validator using a CSPRNG.
Save the selector, hash(‘sha256’, validator), user_id, and expires_at into the database.
Send a combined cookie to the browser (e.g., Value = selector:validator). 3. Verifying the Cookie
When a returning user has an expired session but provides the cookie: Split the cookie value into selector and validator.
Query the database for the row matching the selector. If missing, abort.
Check if expires_at is in the past. If expired, delete the database row and abort. Hash the incoming validator string using SHA-256.
Compare it to the database hashed_validator using a constant-time string comparison function (e.g., hash_equals() in PHP or crypto.timingSafeEqual() in Node.js) to avoid timing attacks. If valid, issue a brand new session. 🛡️ Critical Security Reinforcements 🔒 Lock Down Cookie Attributes
A secure workflow is useless if the cookie is intercepted. Harden your cookie settings with these flags:
Leave a Reply