Session ID Generator
A session identifier is what turns a stateless HTTP request into a logged-in user. It is a bearer credential with a specific set of well-documented attacks, and the defences are mostly about the cookie rather than the value.
Length and Randomness
OWASP recommends at least 128 bits of entropy — 16 random bytes, 32 hex characters — from a cryptographically secure generator. Sequential or predictable session IDs allow an attacker to guess a valid session and assume that user's identity outright.
The Cookie Attributes Are the Real Defence
| Attribute | Effect | Set it to |
|---|---|---|
HttpOnly | JavaScript cannot read the cookie | Always |
Secure | Sent only over HTTPS | Always |
SameSite | Controls cross-site sending | Lax or Strict |
Path | Limits which paths receive it | / usually |
Domain | Omit to avoid sharing with subdomains | Omit |
Max-Age | Idle and absolute lifetime | Both, explicitly |
HttpOnly is what turns a cross-site scripting bug from "session stolen" into "script ran".
SameSite is the primary defence against cross-site request forgery.Regenerate on Privilege Change
Session fixation works by getting a victim to use a session ID the attacker already knows, then waiting for them to log in. The fix is one line: issue a new session ID on login, and again on any privilege escalation. Do not merely add data to the existing session.
Two Timeouts, Not One
An idle timeout ends a session after inactivity; an absolute timeout ends it after a fixed period regardless. Both are needed — an idle timeout alone lets a stolen session live indefinitely as long as it is used.
Server-side invalidation is what makes logout mean anything. Deleting the cookie without deleting the server record leaves a working credential in whatever captured it.
Storage
Sessions in a shared store — Redis, a database — allow revocation, "log out everywhere", and horizontal scaling. Signed-cookie sessions avoid the store at the cost of losing all three. For anything with an account and a password, the store is usually worth it.
The Avalanche Effect
A one-character change produces a completely different digest — not a similar one. That property is what makes a hash useful as a fingerprint:
| Input | MD5 | CRC32 |
|---|---|---|
hello | 5d41402abc4b2a76b9719d911017c592 | 3610a686 |
hello. | d94c10e437d18531e122ed0b45badd2a | 0a39d4f1 |
Hello | 8b1a9953c4611296a827abf8c47804d7 | f7d18982 |
hello and Hello differ by one bit of one byte, and share no part of their output.
RIPEMD-160 of hello is 108f07b8382412612c048d07d13f814118445acd, and of Hello is
d44426aca8ae0a69cdbc4021c64fa5ad68ca32fe — same story.Digest Length and Collision Resistance
| Algorithm | Output | Birthday bound | Status |
|---|---|---|---|
| CRC32 | 32 bits | ~77,000 values | Checksum only |
| MD5 | 128 bits | 2⁶⁴ in theory | Broken — collisions in seconds |
| SHA-1 | 160 bits | 2⁸⁰ in theory | Broken — SHAttered, 2017 |
| RIPEMD-160 | 160 bits | 2⁸⁰ | No practical attack |
| SHA-256 | 256 bits | 2¹²⁸ | Current standard |
| SHA-512 | 512 bits | 2²⁵⁶ | Standard, faster on 64-bit |
Never Hash a Password With These
A general-purpose hash is designed to be fast, which is exactly wrong for passwords: speed helps the attacker. Use a deliberately slow KDF — bcrypt, scrypt or Argon2id — with a per-password salt. A GPU tries billions of SHA-256 guesses a second and a few thousand bcrypt guesses a second, and that gap is the entire defence.