After a user signs in, a web application needs to remember them on every request. That is the job of the session, usually carried by a cookie. Session design determines how long access lasts, how it ends, and how exposed it is to theft.
Kinds of session state
| Kind | What the cookie holds | Advantages | Drawbacks |
|---|---|---|---|
| Server-side session | A random session identifier; data lives in a server store. | Immediate revocation; small cookie; data hidden from the client. | Requires a shared store across instances. |
| Stateless (client-side) session | Signed and usually encrypted session data, for example a JWT or encrypted blob. | No server store; scales easily. | Hard to revoke before expiry; larger cookies; key management. |
Server-side sessions are the safer default. Stateless sessions need short lifetimes and a revocation strategy, as with self-contained tokens.
Cookie attributes
| Attribute | Recommendation | Why |
|---|---|---|
Secure | Always. | Sent only over HTTPS. |
HttpOnly | Always for session cookies. | Not readable by JavaScript, limiting XSS theft. |
SameSite | Lax or Strict; None only when cross-site use is required (and then Secure is mandatory). | Limits cross-site sending and CSRF. |
Domain | Omit. | Host-only cookies are not sent to sibling subdomains. |
Path | / | Paths are not a security boundary. |
Max-Age / Expires | Omit for a browser-session cookie; set for “remember me”. | Controls persistence on the device. |
__Host- prefix | Use for session cookies. | Browser enforces Secure, Path=/, and no Domain. |
Set-Cookie: __Host-session=Xk3f9…; Secure; HttpOnly; SameSite=Lax; Path=/
Session lifecycle
- Generate identifiers with a cryptographically secure random generator and at least 64 bits of entropy.
- Regenerate the session identifier at login and at privilege changes to prevent session fixation.
- Idle timeout: end sessions after inactivity, shorter for sensitive applications.
- Absolute timeout: end sessions after a maximum lifetime regardless of activity.
- Re-authenticate for sensitive actions, such as changing email or payment details.
- Logout invalidates the session server-side, clears the cookie, and, where relevant, ends the identity provider session.
- Show active sessions to users and let them revoke them.
CSRF
Browsers attach cookies automatically, so another site can trigger requests with the user’s session. Defenses:
SameSite=LaxorStricton session cookies.- Anti-CSRF tokens (synchronizer or double-submit) for state-changing requests.
- For JSON APIs, require a custom header that forces a CORS preflight.
- Never change state with
GET.
Where should a browser keep tokens?
| Storage | XSS can read it? | CSRF risk? | Notes |
|---|---|---|---|
HttpOnly cookie | No | Yes, mitigate | Best for session identifiers; used by the BFF pattern. |
| JavaScript memory | Yes, while the page runs | No | Lost on reload; good for short-lived access tokens. |
sessionStorage / localStorage | Yes, persistently | No | Avoid for tokens; any XSS can exfiltrate them. |
Sessions in a federated world
A user signed in through SSO has at least two sessions: one at the identity provider and one at each application. Their lifetimes are independent. Ending the application session does not end the identity provider session, and vice versa, unless logout is propagated. See OpenID Connect in depth and sessions, tokens, and revocation.
Browsers increasingly block third-party cookies. Patterns that rely on cookies inside cross-site iframes, such as silent token renewal and front-channel logout, become unreliable. Prefer same-site architectures and back-channel mechanisms.
Session cookies versus access tokens
| Session cookie | Access token | |
|---|---|---|
| Audience | The application that set it. | A resource server (API). |
| Sent by | The browser, automatically. | The client, explicitly. |
| Typical lifetime | Minutes to days, with idle timeout. | Minutes. |
| Threats | CSRF, theft via malware. | Leakage, replay, XSS when held by JavaScript. |