Sessie blijft geldig na uitloggen
CWE-613OWASP A07:2021Bijgewerkt 3 september 20265 min leestijd
Bij veel applicaties verwijdert de uitlogknop alleen de cookie in de browser, terwijl de sessie aan serverzijde blijft bestaan. Wie het token al had, via een gedeelde computer, een onderschepping of een gelekte logregel, kan het daarna gewoon blijven gebruiken. Uitloggen moet de sessie op de server vernietigen, niet alleen bij de gebruiker weghalen.
Uitloggen voelt als een afgeronde handeling: de knop is ingedrukt, het inlogscherm verschijnt, de sessie is voorbij. Voor de gebruiker klopt dat beeld. Voor de server geldt het alleen als daar ook werkelijk iets is opgeruimd. In dit artikel leest u waarom dat verschil groter is dan het lijkt en hoe u het test.
Wat gaat er mis bij het uitloggen?
Een sessie bestaat uit twee delen: een waarde in de browser van de gebruiker en een bijbehorende registratie op de server. Bij het uitloggen hoort dat tweede deel te verdwijnen. Blijft de sessie geldig na uitloggen, dan is alleen het eerste deel weggehaald: de cookie is uit de browser gewist, maar de server accepteert de waarde nog steeds.
Het lijkt op het inleveren van een toegangspas bij de receptie, waarna niemand de pas in het systeem deactiveert. De medewerker die netjes inlevert merkt niets; hij komt er immers niet meer mee binnen omdat hij hem niet meer heeft. Maar een kopie van diezelfde pas, gemaakt op enig moment daarvoor, opent alle deuren nog steeds. Het inleveren was een gebaar, geen maatregel.
Precies daar zit de kern: uitloggen is de enige handeling waarmee een gebruiker zelf zijn toegang kan intrekken. Werkt die handeling alleen in zijn eigen browser, dan is er in werkelijkheid niets ingetrokken.
Hoe wordt een sessie die na uitloggen geldig blijft misbruikt?
Het verschil tussen een cosmetische en een echte uitlogfunctie is in code goed zichtbaar.
Kwetsbaar:
app.post('/uitloggen', (req, res) => {
res.clearCookie('sid'); // alleen de browser wordt opgeruimd
res.redirect('/inloggen');
});
De gebruiker ziet het inlogscherm en gaat ervan uit dat de sessie voorbij is. De sessierecord op de server bestaat echter nog, met dezelfde identificatie en dezelfde rechten. Wie die waarde eerder heeft vastgelegd, komt er zonder meer mee binnen:
GET /account/gegevens HTTP/1.1
Host: portaal.example
Cookie: sid=8f42c19ade7b3f5c
HTTP/1.1 200 OK
De server ziet een geldige sessie en levert de gegevens. Dat de rechtmatige gebruiker een half uur eerder heeft uitgelogd, is nergens vastgelegd.
Veilig:
app.post('/uitloggen', (req, res) => {
const sid = req.sessionID;
req.session.destroy(async (err) => { // weg uit de sessieopslag
if (err) return res.status(500).send('Uitloggen mislukt');
await auditlog.schrijf('uitgelogd', { sid });
res.clearCookie('sid', { // zelfde attributen als bij het zetten
httpOnly: true,
secure: true,
sameSite: 'strict',
path: '/',
});
res.set('Cache-Control', 'no-store');
res.redirect('/inloggen');
});
});
Nu verdwijnt de registratie aan serverzijde als eerste. Het token verwijst daarna nergens meer naar en wordt door de sessiemiddleware afgewezen, ongeacht wie het meestuurt. Het opruimen van de cookie gebeurt met exact dezelfde attributen als waarmee hij is gezet, wijkt bijvoorbeeld het path af, dan blijft de oude cookie gewoon in de browser staan. Cache-Control: no-store voorkomt ten slotte dat de vorige pagina met de terugknop weer uit de cache tevoorschijn komt.
Wat is de impact van een sessie die na uitloggen geldig blijft?
De ernst wordt doorgaans als middelzwaar beoordeeld, omdat er een voorwaarde geldt: de aanvaller moet het sessietoken al hebben. Dat maakt dit een kwetsbaarheid die andere problemen verlengt in plaats van er zelf een te openen.
Die voorwaarde is in de praktijk minder beperkend dan ze klinkt. Sessietokens komen op meer plekken terecht dan verwacht: in de browsergeschiedenis van een gedeelde computer, in proxy- en serverlogbestanden, in foutrapportages, via een netwerkonderschepping op een openbaar netwerk of via cross-site scripting. In al die gevallen is uitloggen de handeling waarmee het slachtoffer de schade zou moeten kunnen beperken, en juist die handeling werkt dan niet.
Het scenario waar het het vaakst misgaat is alledaags: iemand logt in op een computer die niet van hem is, gebruikt netjes de uitlogknop en vertrekt. De volgende gebruiker heeft alleen de terugknop nodig, of een uit de geschiedenis herstelde cookie, om in het account te belanden. Wat de gebruiker als afsluiting beschouwde, blijkt een open deur.
Hoe spoor je een sessie die na uitloggen geldig blijft op?
De test is direct en vraagt weinig gereedschap. Een tester logt in, legt de waarde van het sessiecookie vast, drukt op uitloggen en stuurt daarna een verzoek naar een beschermde route met het oude token erin. Volgt er een 200 met inhoud in plaats van een afwijzing, dan is de bevinding aangetoond.
Daarna volgen de varianten die vaak wél stukgaan. Wordt de sessie ook vernietigd bij een wachtwoordwijziging? Blijven andere sessies van dezelfde gebruiker geldig, en is dat een bewuste keuze? Bij toepassingen met JSON Web Tokens wordt gekeken of er überhaupt een intrekkingsmechanisme bestaat, want zonder opslag aan serverzijde blijft het token geldig tot de vervaldatum. Ook wordt gecontroleerd of de uitlogroute zelf tegen misbruik is beschermd en of de cookie met de juiste attributen wordt verwijderd. AssistSec neemt daarnaast de terugknop en de browsercache mee in de test, omdat een correct vernietigde sessie nog steeds gevoelige pagina’s kan prijsgeven wanneer die uit de cache worden herbouwd.
Hoe voorkom je een sessie die na uitloggen geldig blijft?
- Vernietig bij het uitloggen de sessie in de opslag aan serverzijde en vertrouw niet op het verwijderen van de cookie.
- Verwijder de cookie met exact dezelfde attributen (
path,domain,secure,sameSite) als waarmee hij is gezet. - Trek alle actieve sessies in bij een wachtwoordwijziging, een rechtenwijziging of het blokkeren van een account.
- Gebruik voor uitloggen een
POST-verzoek met CSRF-bescherming, zodat niemand een ander ongevraagd kan uitloggen. - Stuur
Cache-Control: no-storemee op pagina’s met gevoelige inhoud, zodat de terugknop ze niet herstelt. - Werk bij JSON Web Tokens met korte geldigheidsduren en een intrekkingslijst, of houd een sessiestatus aan serverzijde bij.
- Bied gebruikers een overzicht van actieve sessies en apparaten, met de mogelijkheid ze afzonderlijk te beëindigen.
- Leg uitlogmomenten vast in een auditlog, zodat gebruik van een token ná uitloggen achteraf herkenbaar is.
Bronnen
Veelgestelde vragen
Is het niet genoeg om de cookie te verwijderen?
Nee, want dat is een instructie aan één browser. Het token zelf blijft geldig, en iedereen die de waarde al kent kan hem blijven meesturen. Uitloggen moet de sessie in de opslag aan serverzijde verwijderen, zodat de waarde nergens meer iets betekent.
Waarom is dit bij JWT lastiger?
Een JWT wordt gecontroleerd op zijn handtekening en niet opgezocht in een database, dus de server heeft geen plek waar hij hem ongeldig kan maken. U hebt dan een intrekkingslijst of een korte geldigheidsduur met refresh-tokens nodig; anders blijft het token geldig tot de vervaldatum, hoe vaak de gebruiker ook uitlogt.
Moet uitloggen alle sessies beëindigen?
Niet noodzakelijk. Gebruikers werken vaak op meerdere apparaten en verwachten dat uitloggen op de laptop hun telefoon niet afsluit. Bij een wachtwoordwijziging of een vermoeden van misbruik moeten juist wél alle sessies sneuvelen. Bied daarnaast een overzicht waarin de gebruiker sessies afzonderlijk kan beëindigen.
Wat moet uitloggen precies doen?
De sessie aan serverzijde vernietigen, de cookie verwijderen met een verlopen vervaldatum en dezelfde attributen als bij het zetten, en de gebruiker naar een pagina sturen die niet uit de cache komt. Doe dat via een POST-verzoek met CSRF-bescherming, zodat niemand een ander ongevraagd kan uitloggen.
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:2021JWT blijft geldig na uitloggenEen JWT is geldig tot zijn vervaldatum en trekt zich niets aan van uitloggen. Lees hoe u tokens toch kunt intrekken zonder de voordelen te verliezen.
- 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.
- KwetsbaarhedenCWE-613A07:2021Sessies verlopen nietEen sessie zonder vervaltijd blijft eindeloos bruikbaar. Lees waarom dat gestolen tokens waardevol maakt en hoe u een verstandige timeout instelt.