Sessies verlopen niet
CWE-613OWASP A07:2021Bijgewerkt 3 september 20265 min leestijd
Wanneer een sessie geen vervaltijd kent, blijft het bijbehorende token onbeperkt geldig. Een cookie dat maanden geleden op een gedeelde computer is achtergebleven of ooit is buitgemaakt, geeft dan nog steeds toegang. Een sessie hoort te verlopen na een periode van inactiviteit én na een absolute maximale looptijd.
Bij het inloggen wordt vaak zorgvuldig nagedacht over wachtwoorden en tweede factoren, terwijl de vraag hoe lang de daaruit volgende sessie geldig blijft onbeantwoord blijft. Toch is dat het antwoord dat bepaalt hoeveel een gestolen token waard is. Hieronder leest u waarom een sessie twee verschillende klokken nodig heeft en wat er misgaat als beide ontbreken.
Wat betekent het dat een sessie niet verloopt?
Na een geslaagde inlogpoging krijgt de gebruiker een sessie-identificatie mee, meestal in een cookie. Zolang die waarde geldig is, hoeft er niet opnieuw te worden ingelogd: het token ís het bewijs van authenticatie. Een sessie die niet verloopt is een token dat dat bewijs onbeperkt blijft leveren, ongeacht hoeveel tijd er verstrijkt of wat er intussen gebeurt.
Vergelijk het met een toegangspas die na afgifte nooit meer ongeldig wordt. Zolang de pas bij de rechtmatige eigenaar in de portemonnee zit, is er niets aan de hand. Maar een pas die twee jaar geleden in een taxi is achtergebleven, opent vandaag nog steeds dezelfde deuren. Het probleem is niet de uitgifte, maar het ontbreken van een houdbaarheidsdatum.
In de praktijk horen er twee grenzen te zijn. De eerste is een inactiviteitstimeout: gebeurt er een tijd niets, dan vervalt de sessie. De tweede is een absolute maximale looptijd: ongeacht de activiteit is de sessie na zoveel uur afgelopen. Ontbreekt die tweede grens, dan kan een aanvaller een gestolen sessie eindeloos openhouden door er af en toe een verzoek mee te sturen.
Hoe worden sessies die niet verlopen misbruikt?
Het verschil zit in de vraag of de server zelf de geldigheid bewaakt, of dat hij het token accepteert zolang het formeel klopt.
Kwetsbaar:
// De sessie wordt aangemaakt en verder nooit meer beoordeeld
app.post('/inloggen', async (req, res) => {
const gebruiker = await controleerInloggegevens(req.body);
if (!gebruiker) return res.status(401).send('Onjuiste gegevens');
req.session.userId = gebruiker.id;
res.send('Welkom');
});
function huidigeGebruiker(req) {
return req.session.userId ?? null; // geldig, altijd
}
Er wordt nergens vastgelegd wanneer de sessie is begonnen of wanneer ze voor het laatst is gebruikt. Zolang de sessierecord bestaat, is de gebruiker ingelogd. Een cookie dat op een gedeelde computer in de browser achterblijft, in een back-up van een profiel terechtkomt of ooit via een netwerkonderschepping is buitgemaakt, blijft daarmee bruikbaar tot iemand de sessietabel handmatig opschoont.
Veilig:
const INACTIVITEIT = 30 * 60 * 1000; // 30 minuten
const ABSOLUUT = 8 * 60 * 60 * 1000; // 8 uur
app.post('/inloggen', async (req, res) => {
const gebruiker = await controleerInloggegevens(req.body);
if (!gebruiker) return res.status(401).send('Onjuiste gegevens');
req.session.regenerate(() => { // nieuw id, tegen session fixation
req.session.userId = gebruiker.id;
req.session.gestartOp = Date.now();
req.session.laatstGezien = Date.now();
res.send('Welkom');
});
});
app.use((req, res, next) => {
const s = req.session;
if (!s?.userId) return next();
const nu = Date.now();
if (nu - s.laatstGezien > INACTIVITEIT || nu - s.gestartOp > ABSOLUUT) {
return s.destroy(() => res.status(401).send('Sessie verlopen'));
}
s.laatstGezien = nu; // schuift alleen de inactiviteitsklok
next();
});
Nu bewaakt de server twee dingen tegelijk. De inactiviteitsklok wordt bij elk verzoek teruggezet, zodat een actieve gebruiker niet wordt gestoord. De absolute klok blijft staan vanaf het moment van inloggen en is niet te verlengen, juist dat verschil maakt dat een aanvaller een buitgemaakte sessie niet eindeloos in leven kan houden. Zodra een van beide grenzen wordt overschreden, wordt de sessie aan serverzijde vernietigd.
maxAge op de cookie is een instructie aan de browser en zegt niets over de server: een aanvaller die het token al in handen heeft, stuurt het gewoon mee ná de opgegeven vervaldatum. Wordt de geldigheid niet aan serverzijde gecontroleerd, dan is de sessie in werkelijkheid nog steeds onbeperkt geldig.Wat is de impact van sessies die niet verlopen?
Dit is zelden een kwetsbaarheid waarmee een aanvaller binnenkomt; het is een kwetsbaarheid die bepaalt hoe lang hij binnen blijft. Daarom valt de ernst meestal laag tot middelzwaar uit, afhankelijk van wat er achter de sessie zit.
Het scenario dat er het meest toe doet is dat van de gedeelde of publieke werkplek: een balie, een schoolcomputer, een tablet in een wachtruimte. De gebruiker sluit het venster, staat op en vertrekt. De volgende persoon opent de browser en zit in het account. Dat vraagt geen enkele technische vaardigheid en gebeurt regelmatig.
Daarnaast vergroot een onbeperkte sessie de waarde van elke andere kwetsbaarheid. Een token dat via cross-site scripting, een netwerkonderschepping of een gelekte logregel is buitgemaakt, is bij een strikte timeout hooguit een half uur bruikbaar. Zonder timeout is het een permanente sleutel, die zonder wachtwoordwijziging niet meer ongeldig wordt, en zelfs een wachtwoordwijziging helpt niet als bestaande sessies daarbij niet worden ingetrokken.
Hoe spoor je sessies die niet verlopen op?
De test is eenvoudig van opzet maar kost tijd: een tester logt in, legt het sessietoken vast en probeert het na een langere periode van niets doen opnieuw te gebruiken. Werkt het dan nog, dan ontbreekt de inactiviteitstimeout.
De tweede meting is verfijnder. Door de sessie kunstmatig in leven te houden met een periodiek verzoek wordt gecontroleerd of er een absolute bovengrens bestaat. Veel applicaties hebben de eerste maatregel wel en de tweede niet. Verder wordt gekeken of de vervaltijd werkelijk aan serverzijde wordt afgedwongen: een tester verwijdert daarvoor simpelweg de vervaldatum van de cookie en kijkt of het token dan alsnog wordt geaccepteerd. Ook relevant: gelden de timeouts ook voor API-tokens en voor de mobiele applicatie, of alleen voor de webinterface? AssistSec toetst die scenario’s afzonderlijk, omdat een strikte webtimeout weinig waard is als dezelfde authenticatie via een API onbeperkt geldig blijft.
Hoe voorkom je sessies die niet verlopen?
- Stel een inactiviteitstimeout in die past bij de gevoeligheid van de applicatie, en houd die klok aan serverzijde bij.
- Voeg daarnaast een absolute maximale looptijd toe die niet door activiteit kan worden verlengd.
- Vernietig de sessie aan serverzijde bij het verlopen; laat het niet bij het verwijderen van de cookie.
- Geef de gebruiker vóór het verlopen een zichtbare waarschuwing met de mogelijkheid de sessie te verlengen.
- Behandel “onthoud mij” als een apart, intrekbaar token en niet als een sessie zonder einde.
- Trek alle bestaande sessies in bij een wachtwoordwijziging, een rechtenwijziging of het uitschakelen van een account.
- Pas dezelfde regels toe op API-tokens en mobiele clients, niet alleen op de webinterface.
- Bied gebruikers een overzicht van actieve sessies met de mogelijkheid ze op afstand te beëindigen.
Bronnen
Veelgestelde vragen
Wat is een redelijke timeout?
Dat hangt af van de gevoeligheid van de gegevens. Voor een bankomgeving of beheerpaneel is vijftien minuten inactiviteit gebruikelijk, voor een gewone zakelijke applicatie dertig minuten tot een uur. Naast die inactiviteitstimeout hoort er een absolute grens te staan, vaak acht tot twaalf uur, waarna opnieuw inloggen verplicht is.
Is het verschil tussen inactiviteit en absolute looptijd belangrijk?
Ja, ze dekken verschillende scenario's af. De inactiviteitstimeout beschermt tegen een onbeheerd achtergelaten werkplek. De absolute looptijd beperkt hoe lang een gestolen token bruikbaar blijft, ook wanneer de aanvaller het actief in gebruik houdt en de inactiviteitsklok daarmee steeds terugzet.
Mag ik gebruikers ingelogd houden met onthoud-mij?
Dat kan, maar behandel het als een aparte voorziening. Gebruik daarvoor een afzonderlijk, langlevend token dat u kunt intrekken en dat alleen recht geeft op een nieuwe sessie, niet op directe toegang. Vraag bij gevoelige handelingen binnen zo'n herstelde sessie altijd opnieuw om het wachtwoord.
Volstaat het om de cookie te laten verlopen?
Nee. Een vervaldatum op de cookie is een instructie aan de browser, en die kan een aanvaller die het token al bezit eenvoudig negeren. De geldigheid moet aan serverzijde worden bijgehouden en afgedwongen; de cookie-vervaldatum is hooguit een aanvulling voor de gewone gebruiker.
Verwante artikelen
- KwetsbaarhedenCWE-287A07:2021Broken authenticationBroken authentication uitgelegd: hoe aanvallers via brute force, gelekte wachtwoorden en voorspelbare sessietokens accounts overnemen, en wat u eraan doet.
- KwetsbaarhedenCWE-613A07:2021Authenticatiecookie met te lange geldigheidEen sessiecookie die maanden geldig blijft, maakt van elk gestolen token een langdurige sleutel. Lees hoe u onthoud-mij veilig inricht.
- KwetsbaarhedenCWE-613A07:2021Sessie blijft geldig na uitloggenUitloggen dat alleen de cookie wist, laat het token aan serverzijde intact. Lees hoe een buitgemaakte sessie dan gewoon bruikbaar blijft.
- KwetsbaarhedenCWE-1004A07:2021Onbeschermd authenticatiecookieEen sessiecookie zonder HttpOnly, Secure en SameSite is uitleesbaar, onderschepbaar en misbruikbaar. Lees wat elk attribuut precies afdekt.
- KwetsbaarhedenCWE-384A07:2021Session fixationSession fixation uitgelegd: hoe een aanvaller vooraf een session id vastlegt, waarom die na het inloggen geldig blijft en hoe sessierotatie het stopt.