A session token is effectively a temporary password — anyone who has it can act as the logged-in user, which means how it's stored, transmitted, and expired matters as much as how it's generated.
Cookie flags that actually matter
If using cookies for session storage, three flags do most of the protective work:
- HttpOnly — prevents JavaScript from reading the cookie, which blocks most cookie theft via XSS, since the attacker's injected script simply can't access it.
- Secure — ensures the cookie is only sent over HTTPS, preventing exposure over an unencrypted connection.
- SameSite — restricts when the cookie is sent with cross-site requests, mitigating CSRF risk.
StrictorLaxare the common safe choices, depending on how much cross-site linking your application needs to support.
Why localStorage is a weaker choice for session tokens
Anything stored in localStorage is readable by any JavaScript running on the page — including an attacker's script injected via an XSS vulnerability elsewhere in the application. A cookie with HttpOnly set doesn't have this exposure.
Session expiration and rotation
- Set a reasonable expiration. An indefinitely valid session token means a stolen one is valid indefinitely too.
- Rotate the session ID after login. If a session ID existed before authentication (a common pattern for tracking anonymous visitors), issue a new one after successful login to prevent session fixation attacks.
- Invalidate sessions server-side on logout. A client-side-only logout (just clearing the cookie) leaves the session valid server-side if the token is somehow retained or replayed.
Detecting anomalies
Track session usage patterns (IP changes mid-session, impossible travel between requests) as a signal for potential session hijacking, and consider requiring re-authentication for sensitive actions even within an otherwise valid session.
Refresh tokens, used carefully
Short-lived access tokens paired with a longer-lived refresh token reduce the exposure window of a stolen access token, but the refresh token itself becomes a high-value target — store and transmit it with at least the same care as the primary session token.