Sessions do not expire
CWE-613OWASP A07:2021Updated September 4, 20265 min read
When a session has no expiry, the matching token remains valid indefinitely. A cookie left behind on a shared computer months ago, or captured at some point, still grants access. A session should expire after a period of inactivity and also after an absolute maximum lifetime.
Logging in usually gets careful thought about passwords and second factors, while the question of how long the resulting session stays valid goes unanswered. Yet that answer determines how much a stolen token is worth. A session needs two separate clocks, and when both are missing the consequences are predictable.
What does it mean for a session not to expire?
After a successful login the user receives a session identifier, usually in a cookie. As long as that value is valid, there is no need to log in again: the token is the proof of authentication. A session that does not expire is a token that keeps supplying that proof indefinitely, no matter how much time passes or what happens in the meantime.
Compare it with an access badge that is never deactivated after it is issued. As long as the badge sits in the rightful owner’s wallet, nothing is wrong. But a badge left in a taxi two years ago still opens the same doors today. The problem is not the issuing, it is the absence of an expiry date.
In practice there should be two limits. The first is an inactivity timeout: if nothing happens for a while, the session lapses. The second is an absolute maximum lifetime: regardless of activity, the session ends after so many hours. Without that second limit, an attacker can keep a stolen session alive indefinitely by sending the occasional request with it.
How are sessions that do not expire exploited?
The difference lies in whether the server guards validity itself, or accepts the token as long as it is formally correct.
Vulnerable:
// The session is created and then never judged again
app.post('/login', async (req, res) => {
const user = await checkCredentials(req.body);
if (!user) return res.status(401).send('Invalid credentials');
req.session.userId = user.id;
res.send('Welcome');
});
function currentUser(req) {
return req.session.userId ?? null; // valid, always
}
Nowhere is it recorded when the session began or when it was last used. As long as the session record exists, the user is logged in. A cookie left behind in the browser on a shared computer, included in a backup of a profile, or captured at some point through network interception, therefore stays usable until someone cleans out the session table by hand.
Safe:
const INACTIVITY = 30 * 60 * 1000; // 30 minutes
const ABSOLUTE = 8 * 60 * 60 * 1000; // 8 hours
app.post('/login', async (req, res) => {
const user = await checkCredentials(req.body);
if (!user) return res.status(401).send('Invalid credentials');
req.session.regenerate(() => { // new id, against session fixation
req.session.userId = user.id;
req.session.startedAt = Date.now();
req.session.lastSeen = Date.now();
res.send('Welcome');
});
});
app.use((req, res, next) => {
const s = req.session;
if (!s?.userId) return next();
const now = Date.now();
if (now - s.lastSeen > INACTIVITY || now - s.startedAt > ABSOLUTE) {
return s.destroy(() => res.status(401).send('Session expired'));
}
s.lastSeen = now; // only shifts the inactivity clock
next();
});
The server now guards two things at once. The inactivity clock is reset on every request, so an active user is not disturbed. The absolute clock keeps running from the moment of login and cannot be extended, and it is exactly that difference which stops an attacker from keeping a captured session alive forever. As soon as either limit is exceeded, the session is destroyed on the server.
maxAge on the cookie is an instruction to the browser and says nothing about the server: an attacker who already holds the token simply sends it after the stated expiry. If validity is not checked on the server, the session is in reality still unlimited.What is the impact of sessions that do not expire?
This is rarely a vulnerability an attacker gets in with; it is a vulnerability that determines how long they stay in. The severity therefore usually comes out low to medium, depending on what sits behind the session.
The scenario that matters most is the shared or public workstation: a service desk, a school computer, a tablet in a waiting room. The user closes the window, gets up and leaves. The next person opens the browser and is inside the account. That requires no technical skill at all and happens regularly.
Beyond that, an unlimited session increases the value of every other weakness. A token captured through cross-site scripting, network interception or a leaked log line is usable for half an hour at most under a strict timeout. Without a timeout it is a permanent key that does not lapse without a password change, and even a password change does not help if existing sessions are not revoked along with it.
How do you detect sessions that do not expire?
The test is simple in design but takes time: a tester logs in, records the session token, and tries to reuse it after a longer period of doing nothing. If it still works, the inactivity timeout is missing.
The second measurement is finer. By keeping the session alive artificially with a periodic request, the tester checks whether an absolute upper limit exists. Many applications have the first measure and not the second. They also check whether the expiry is really enforced on the server, simply by removing the cookie expiry and seeing whether the token is still accepted. Also relevant: do the timeouts apply to API tokens and to the mobile application, or only to the web interface? AssistSec tests those scenarios separately, because a strict web timeout is worth little if the same authentication stays valid indefinitely through an API.
How do you prevent sessions that do not expire?
- Set an inactivity timeout that matches the sensitivity of the application, and keep that clock on the server.
- Add an absolute maximum lifetime that activity cannot extend.
- Destroy the session on the server when it expires; do not stop at removing the cookie.
- Give the user a visible warning before expiry, with the option to extend the session.
- Treat “remember me” as a separate, revocable token rather than a session without an end.
- Revoke all existing sessions on a password change, a change of rights, or the disabling of an account.
- Apply the same rules to API tokens and mobile clients, not only to the web interface.
- Offer users an overview of active sessions with the option to end them remotely.
Sources
Frequently asked questions
What is a reasonable timeout?
That depends on how sensitive the data is. For a banking environment or an admin panel, fifteen minutes of inactivity is common; for an ordinary business application, thirty minutes to an hour. Alongside that inactivity timeout there should be an absolute limit, often eight to twelve hours, after which logging in again is mandatory.
Does the difference between inactivity and absolute lifetime matter?
Yes, they cover different scenarios. The inactivity timeout protects against an unattended workstation. The absolute lifetime limits how long a stolen token stays usable, even when the attacker keeps it in active use and thereby keeps resetting the inactivity clock.
May I keep users logged in with remember me?
You can, but treat it as a separate facility. Use a distinct, long-lived token that you can revoke and that only grants the right to a new session, not direct access. Within such a restored session, always ask for the password again before sensitive actions.
Is letting the cookie expire enough?
No. An expiry date on the cookie is an instruction to the browser, and an attacker who already holds the token can simply ignore it. Validity has to be tracked and enforced on the server; the cookie expiry is at most a convenience for the ordinary user.
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:2021Authentication cookie valid for too longA session cookie valid for months turns every stolen token into a lasting key. Learn how to build remember-me safely.
- VulnerabilitiesCWE-613A07:2021Session stays valid after logoutA logout that only clears the cookie leaves the token intact on the server. Learn how a captured session then simply keeps working.
- 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.