Session stays valid after logout
CWE-613OWASP A07:2021Updated September 4, 20265 min read
In many applications the logout button only removes the cookie in the browser, while the session continues to exist on the server. Anyone who already had the token, through a shared computer, an interception or a leaked log line, can carry on using it. Logging out must destroy the session on the server, not merely take it away from the user.
Logging out feels like a completed action: the button is pressed, the login screen appears, the session is over. For the user that picture is accurate. For the server it only holds if something was actually cleaned up there. This article explains why that difference is larger than it looks and how to test it.
What goes wrong at logout?
A session consists of two parts: a value in the user’s browser and a matching record on the server. Logging out should remove that second part. When the session stays valid after logout, only the first part has been taken away: the cookie is cleared from the browser, but the server still accepts the value.
It resembles handing in an access badge at reception, after which nobody deactivates the badge in the system. The employee who hands it in properly notices nothing, since they cannot get in any more without it. But a copy of that same badge, made at some earlier point, still opens every door. Handing it in was a gesture, not a measure.
That is precisely the heart of it: logging out is the only action with which a user can withdraw their own access. If that action works only in their own browser, nothing has in fact been withdrawn.
How is a session that survives logout exploited?
The difference between a cosmetic and a genuine logout is clearly visible in code.
Vulnerable:
app.post('/logout', (req, res) => {
res.clearCookie('sid'); // only the browser is cleaned up
res.redirect('/login');
});
The user sees the login screen and assumes the session is over. The session record on the server, however, still exists, with the same identifier and the same rights. Anyone who recorded that value earlier gets in with it without difficulty:
GET /account/details HTTP/1.1
Host: portal.example
Cookie: sid=8f42c19ade7b3f5c
HTTP/1.1 200 OK
The server sees a valid session and delivers the data. The fact that the rightful user logged out half an hour earlier is recorded nowhere.
Safe:
app.post('/logout', (req, res) => {
const sid = req.sessionID;
req.session.destroy(async (err) => { // gone from the session store
if (err) return res.status(500).send('Logout failed');
await auditlog.write('logged out', { sid });
res.clearCookie('sid', { // same attributes as when set
httpOnly: true,
secure: true,
sameSite: 'strict',
path: '/',
});
res.set('Cache-Control', 'no-store');
res.redirect('/login');
});
});
The server-side record now disappears first. The token no longer refers to anything and is rejected by the session middleware, no matter who sends it. Clearing the cookie happens with exactly the same attributes it was set with; if the path differs, for instance, the old cookie simply stays in the browser. Cache-Control: no-store finally prevents the previous page from reappearing from the cache through the back button.
What is the impact of a session that survives logout?
The severity is usually rated medium, because a condition applies: the attacker must already hold the session token. That makes this a vulnerability which extends other problems rather than opening one itself.
That condition is less limiting in practice than it sounds. Session tokens end up in more places than expected: in the browser history of a shared computer, in proxy and server logs, in error reports, through network interception on a public network, or through cross-site scripting. In all those cases, logging out is the action with which the victim should be able to limit the damage, and that is exactly the action which then does not work.
The scenario that goes wrong most often is entirely everyday: someone logs in on a computer that is not theirs, uses the logout button properly and leaves. The next user needs only the back button, or a cookie restored from history, to end up inside the account. What the user regarded as closing the door turns out to be an open one.
How do you detect a session that survives logout?
The test is direct and needs little tooling. A tester logs in, records the value of the session cookie, presses logout, and then sends a request to a protected route with the old token in it. If a 200 with content follows instead of a rejection, the finding is demonstrated.
Then come the variants that often do break. Is the session also destroyed on a password change? Do other sessions of the same user stay valid, and is that a deliberate choice? For applications using JSON Web Tokens, testers check whether any revocation mechanism exists at all, because without server-side storage the token stays valid until expiry. They also check whether the logout route itself is protected against abuse and whether the cookie is removed with the right attributes. AssistSec additionally covers the back button and the browser cache during the test, because a correctly destroyed session can still reveal sensitive pages when those are rebuilt from cache.
How do you prevent a session that survives logout?
- Destroy the session in the server-side store on logout and do not rely on removing the cookie.
- Remove the cookie with exactly the same attributes (
path,domain,secure,sameSite) it was set with. - Revoke all active sessions on a password change, a change of rights, or the blocking of an account.
- Use a
POSTrequest with CSRF protection for logout, so nobody can log another user out unbidden. - Send
Cache-Control: no-storeon pages with sensitive content, so the back button does not restore them. - With JSON Web Tokens, use short lifetimes and a revocation list, or keep session state on the server.
- Offer users an overview of active sessions and devices, with the option to end them individually.
- Record logout events in an audit log, so use of a token after logout is recognisable afterwards.
Sources
Frequently asked questions
Is removing the cookie not enough?
No, because that is an instruction to one browser. The token itself stays valid, and anyone who already knows the value can keep sending it. Logging out must delete the session from the server-side store, so that the value no longer means anything anywhere.
Why is this harder with JWT?
A JWT is verified on its signature and not looked up in a database, so the server has no place to invalidate it. You then need a revocation list or a short lifetime with refresh tokens; otherwise the token stays valid until its expiry, however often the user logs out.
Should logging out end all sessions?
Not necessarily. Users often work on several devices and expect logging out on the laptop not to close their phone. On a password change or a suspicion of abuse, all sessions should indeed be destroyed. Also offer an overview in which the user can end sessions individually.
What exactly should logging out do?
Destroy the session on the server, remove the cookie with an expired date and the same attributes as when it was set, and send the user to a page that does not come from the cache. Do this through a POST request with CSRF protection, so nobody can log another person out unbidden.
Related articles
- VulnerabilitiesCWE-287A07:2021Broken authenticationBroken authentication explained: how attackers take over accounts through brute force, leaked passwords and predictable session tokens, and how to stop them.
- VulnerabilitiesCWE-613A07:2021JWT stays valid after logoutA JWT is valid until its expiry and takes no notice of logging out. Learn how to revoke tokens without losing the benefits.
- VulnerabilitiesCWE-1004A07:2021Unprotected authentication cookieA session cookie without HttpOnly, Secure and SameSite is readable, interceptable and abusable. Learn what each attribute actually covers.
- VulnerabilitiesCWE-384A07:2021Session fixationSession fixation explained: how an attacker plants a session id in advance, why logging in keeps it valid, and how rotating the id prevents it.
- VulnerabilitiesCWE-613A07:2021Sessions do not expireA session without an expiry stays usable forever. Learn why that makes stolen tokens valuable and how to set a sensible timeout.