2.2Sessions, cookies & simple authentication
The essentials in 30 seconds
- HTTP forgets everything: each request stands alone — cookies and sessions add “memory” across pages (who is logged in, cart, preferences).
- Sessions for sensitive data, cookies for preferences: trusted data on the SERVER ($_SESSION, not modifiable) — never put sensitive data in a modifiable cookie.
- Hashed passwords, never in plain text: password_hash at registration, password_verify at login, regenerated session ID — no exception.
Running mini-case — Riad keeps his members logged in
Riad builds on his form (2.1): the association's members must stay logged in from page to page to see their personal area. Since HTTP is stateless, he adds memory: a cookie for the language preference (not sensitive), a session for the identity (sensitive, server side). At login he checks the password with password_verify against a hash, regenerates the session ID, stores the identifier in $_SESSION; each protected page checks the session. In 2.3, he will factor the check into a file included everywhere; in 2.4, he will date the last login.
Sequel to thread 2.1: Riad's form becomes a member area with memory — and continues in 2.3 and 2.4.
1. Giving HTTP a memory: cookies and sessions
The HTTP protocol is stateless: each request is INDEPENDENT, the server “remembers” nothing between two pages. To remember state (who is logged in, a cart, preferences), two mechanisms. Cookies: small data items stored in the BROWSER, sent back to the server with each request — created with setcookie(), read in $_COOKIE. Client side, so VISIBLE and MODIFIABLE by the user: reserved for non-sensitive data (language, theme), with security options (HttpOnly against theft via XSS, Secure for HTTPS only, SameSite against CSRF). Sessions: the state is stored on the SERVER (the user cannot modify it); the browser only keeps a session identifier. In PHP: session_start() at the very top of the script (before any output), then storage and reading via $_SESSION ($_SESSION['user_id'] = 42;). Sensitive data lives in the session — never in plain text in a cookie. Riad applies this: language in a cookie, identity in a session.
2. Simple but safe authentication
Sessions are used notably for authentication. The cycle: at LOGIN, check the identifier and the password, then store the identity in the session ($_SESSION['user_id']); on each PROTECTED page, check the session (otherwise redirect to login); at LOGOUT, destroy the session (session_destroy()). Three protections are non-negotiable. First, passwords are stored HASHED with password_hash() and checked with password_verify() — never in plain text, never with a weak hash: a database leak would otherwise expose every account. Next, regenerate the identifier at login (session_regenerate_id()) against session fixation, with secure session cookies and HTTPS. Finally, control ACCESS: check not only that the user is logged in (authentication), but that they have the RIGHT to access the resource (authorization) — otherwise one member reads another's data by changing an id in the URL.
3. The traps that compromise accounts
Authentication is a high-stakes area: one mistake compromises real accounts. Traps to ban: storing a password in plain text; forgetting to regenerate the session ID; leaving session cookies without HttpOnly/Secure/SameSite; working without HTTPS; checking login but not authorization; revealing through over-precise error messages which emails exist; not limiting attempts (brute force); building queries by concatenation instead of prepared statements (SQL injection, level 3). Riad ticked off each point before opening the member area. And in a REAL PROJECT, prefer the PROVEN authentication systems of frameworks, which handle these subtleties correctly: understanding the mechanisms (as here) is essential, implementing them rigorously is even more so.
🔎 Concrete example — 🔎 Concrete example — Riad's member login
Session + hashed password, as with Riad:
session_start();
if (password_verify($_POST['mdp'] ?? '', $user['hash'])) {
session_regenerate_id(true);
$_SESSION['user_id'] = (int) $user['id'];
}
// protected page:
// if (!isset($_SESSION['user_id'])) { header('Location: connexion.php'); exit; }
Reading: checked against the hash, regenerated ID against fixation, identity on the server.
Lesson: also check authorization (the RIGHT of access), not just login.
Section vocabulary
- Stateless HTTP
- Each HTTP request stands alone; the server does not remember previous ones; cookies and sessions add memory across pages.
Simple example: Riad must add memory to keep his members logged in.
What it is not: a server that spontaneously recognizes its visitors.
Reflex: state across pages = plan a cookie or a session.
- Cookies ($_COOKIE)
- Small data items stored in the BROWSER, sent back with each request (setcookie / $_COOKIE); client side so modifiable — HttpOnly, Secure, SameSite options.
Simple example: Riad remembers the chosen language in a cookie.
What it is not: safe storage for an identity or a role.
Reflex: cookie = non-sensitive preferences only.
- Sessions ($_SESSION)
- State stored on the SERVER, not modifiable by the user, the browser keeping only an identifier; session_start() then $_SESSION — the secure mechanism.
Simple example: Riad's members' identity lives in $_SESSION['user_id'].
What it is not: data the user can edit in their browser.
Reflex: trusted data = session.
- Session authentication
- Login: check identifier + password then store the identity in the session; protected pages: check the session; logout: destroy the session.
Simple example: Riad protects the member area with isset($_SESSION['user_id']).
What it is not: a page that trusts a modifiable cookie.
Reflex: each sensitive page = session check.
- Session security
- HASHED passwords (password_hash/verify, never in plain text), regenerated ID at login, secure cookies, HTTPS, and access control.
Simple example: Riad hashes at registration and checks with password_verify.
What it is not: a password stored in plain text or md5.
Reflex: new authentication = full security checklist.
In practice — In practice — Handling state and simple authentication
- Start a session (session_start() at the top of the script, before any output) and store/read data via $_SESSION.
- Create a cookie with setcookie() (HttpOnly/Secure options) and read it via $_COOKIE; understand that it is stored on the client (modifiable).
- Build authentication: at login, check the password (password_verify against a hash), store $_SESSION['user_id'], regenerate the session ID.
- Protect the pages (check the session, redirect otherwise) and handle logout (session_destroy); remember: HASHED passwords, protected session, HTTPS.
Ready-to-say script — justifying the session to the association manager (30 s)
“For the member area, the identity lives in the session, on the server: members cannot modify it. The cookie carries only the language. Passwords are hashed, never in plain text, and I regenerate the identifier at each login. If someone forges their cookie, they gain no access.”
Say it while showing a modified cookie that grants no access; the demo convinces.
Copy-paste template — login and protected page
' . htmlspecialchars('Invalid credentials.', ENT_QUOTES, 'UTF-8') . '';
// --- protected page (espace.php) ---
// session_start();
// if (!isset($_SESSION['user_id'])) { header('Location: connexion.php'); exit; }
// --- logout ---
// $_SESSION = []; session_destroy();
Language preference
Riad remembers French or Arabic in a long-lived cookie: not sensitive, perfect for the client.
Sensitive cart
The association shop's cart contents live in the session: members cannot change the prices.
Clean logout
One click empties $_SESSION and destroys the session: on a shared computer, no successor recovers the account.
Towards the framework
In a real project, Riad will entrust authentication to the framework's proven system — same principles, subtleties handled.
✓ Best practices
- Use SESSIONS for sensitive state (identity, cart).
- Hash passwords with password_hash / password_verify, never in plain text.
- Regenerate the session ID at login + secure cookies + HTTPS.
✗ Common pitfalls
- Storing trusted data in a cookie (modifiable by the user).
- Storing a password in plain text or with md5/sha1.
- Checking only login and forgetting authorization (access to other people's data).
Key takeaways
- HTTP is STATELESS (each request stands alone); COOKIES (browser side, $_COOKIE) and SESSIONS (server side, $_SESSION) add “memory” across pages.
- SESSIONS = the SECURE mechanism: data on the server (not modifiable), the browser keeps only an IDENTIFIER. Cookies = client side (modifiable, for non-sensitive data).
- AUTHENTICATION: login (check password, store identity in session) → protected pages (check the session) → logout (destroy).
- SECURITY (high stakes): HASHED passwords (password_hash/verify, NEVER in plain text), regenerate the ID at login, secure cookies (HttpOnly/Secure/SameSite), HTTPS, access control (authorization).
48h challenge — apply it tomorrow
Build a minimal login: a hash created with password_hash, check with password_verify, session_regenerate_id, storing the identity in the session, a protected page that redirects otherwise, and a logout. Then forge your cookie and confirm that access stays denied.
- I can explain the cookie/session difference and say where each data item lives.
- I can build a login with hash, regenerated session and protected page.
- I have checked that a forged cookie grants no access.
Frequently asked questions
Cookies or sessions: what is the difference, and which one for what?
Both give HTTP some “memory”, but differently. COOKIES: data in the BROWSER (client side), sent back with each request — VISIBLE and MODIFIABLE by the user, small size, possible long persistence; never sensitive data in plain text (HttpOnly, Secure, SameSite options). SESSIONS: data on the SERVER, the browser keeping only an IDENTIFIER; the user CANNOT modify the data; larger capacity; usually lasts for the visit. Sessions use a cookie (for the identifier) but the DATA stays on the server — that is the key difference. Use SESSIONS for SENSITIVE state (authentication, cart) — the default choice; COOKIES for NON-sensitive preferences and long persistence (language, theme). Secure both (cookie options, identifier regeneration, HTTPS).
How do I build correct authentication, and which traps should I avoid?
Steps: (1) REGISTRATION — validate, HASH with password_hash(), store the HASH (never the plain text). (2) LOGIN — fetch the user by email (PREPARED query), check with password_verify(), then session_regenerate_id() and store the identity in the session. (3) PROTECTED PAGES — check the session (isset($_SESSION['user_id'])), otherwise redirect. (4) AUTHORIZATION — check the RIGHT to THIS resource, not just login. (5) LOGOUT — empty then destroy the session. Serious traps: passwords in PLAIN TEXT or badly hashed (the worst), non-regenerated ID (fixation), insecure cookies, no HTTPS, forgotten authorization, error messages revealing emails, unlimited attempts (brute force), concatenated queries (SQL injection). In a REAL PROJECT, prefer the PROVEN authentication systems of frameworks.
Where is SESSION data stored?