Direct naar inhoud

Session fixation

CWE-384OWASP A07:2021Bijgewerkt 31 augustus 20266 min leestijd

Session fixation is een kwetsbaarheid waarbij een aanvaller het slachtoffer vooraf een session id opdringt die bij het inloggen niet wordt vernieuwd. Zijn eigen kopie van die id hoort daarna bij een geauthenticeerde sessie, waardoor hij meelift op het account. De structurele oplossing is de session id vernieuwen bij elke wijziging van rechten en de oude sessie meteen weggooien.

Bij het inloggen verandert in veel applicaties maar één ding: de inhoud van de sessie. De lange reeks tekens in de cookie blijft precies zoals hij was, terwijl de betekenis ervan verandert van “anonieme bezoeker” naar “ingelogde gebruiker”. Wie die reeks al kende voordat u inlogde, beschikt daarna over uw account. Dat is session fixation, en de oplossing kost meestal één regel code.

Wat is session fixation?

Session fixation is een kwetsbaarheid waarbij een aanvaller vooraf vastlegt welke sessie-identificatie het slachtoffer gebruikt, waarna de applicatie die identificatie bij het inloggen niet vernieuwt. De session id, voluit session identifier, is het enige dat de server nog nodig heeft om te weten wie u bent. Wordt hij niet vervangen op het moment dat de sessie van waarde verandert, dan verandert de aanvaller mee: zijn kopie van de id hoort opeens bij een ingelogd account.

Een alledaagse vergelijking: de garderobe van een theater. Normaal krijgt u een nieuw nummertje wanneer u uw jas afgeeft. Stel nu dat iemand u bij de ingang een eigen nummertje in de hand drukt, waarvan hij een tweede exemplaar in zijn zak heeft, en dat de garderobe dat nummer gewoon overneemt. Uw jas hangt dan aan een haakje waarvan twee mensen het nummer kennen, en de eerste die zich meldt, krijgt hem mee.

Het verschil met session hijacking zit in de volgorde. Bij hijacking bemachtigt de aanvaller een sessie die al geauthenticeerd is, bijvoorbeeld door een cookie te stelen. Bij fixation kent hij de id al voordat er iemand inlogt en laat hij het slachtoffer het authenticatiewerk doen. Hij hoeft daarna niets meer te onderscheppen, wat de aanval stil maakt en lastig te herkennen in logbestanden.

Zo’n id planten kan langs meerdere wegen. Een applicatie die een sessie-id uit de URL accepteert, neemt elke waarde over die een aanvaller in een link zet. Een cookie kan ook vanaf een subdomein van de aanvaller worden gezet, want cookies zijn niet per subdomein gescheiden zoals de origin dat wel is. En elke cross-site scripting op het domein volstaat om de cookie rechtstreeks te schrijven.

Hoe werkt een session fixation-aanval?

Neem een inlogscherm dat de sessie start, het wachtwoord controleert en daarna de gebruiker in de sessie zet. De ontwikkelaar denkt daarmee klaar te zijn, maar vergeet de identificatie zelf te vervangen.

Kwetsbaar:

<?php
session_start();

$user = find_user($_POST['email']);

if ($user && password_verify($_POST['password'], $user['hash'])) {
    // De sessie-id blijft ongewijzigd, alleen de inhoud verandert
    $_SESSION['user_id']  = $user['id'];
    $_SESSION['is_admin'] = $user['is_admin'];

    header('Location: /account');
    exit;
}

De aanvaller begint met het kiezen van een waarde en zorgt dat de browser van het slachtoffer die waarde meestuurt. Het verzoek waarmee het slachtoffer op het inlogscherm belandt, ziet er dan zo uit:

GET /login HTTP/1.1
Host: app.example.com
Cookie: PHPSESSID=b7f1c0a94e2d4c8f9a1e6b3d0c5f8a27

Zolang de server geen strikte controle doet, maakt hij bij dit verzoek een nieuwe sessie aan onder de aangeleverde naam in plaats van een eigen id uit te geven. Het slachtoffer vult zijn wachtwoord in, de controle slaagt en $_SESSION['user_id'] wordt gevuld. De id in de cookie is echter nog steeds b7f1c0a94e2d4c8f9a1e6b3d0c5f8a27, en die kende de aanvaller al. Hij zet dezelfde cookie in zijn eigen browser, laadt /account en is binnen zonder ooit een wachtwoord te hebben gezien.

De oplossing is de identificatie te vervangen op het moment dat de sessie van betekenis verandert. In PHP doet session_regenerate_id(true) dat: er komt een nieuwe id en de oude sessie wordt verwijderd. Daarnaast weigert de instelling session.use_strict_mode een id die de server niet zelf heeft uitgegeven, waardoor het planten van een waarde al bij het eerste verzoek mislukt.

Veilig:

<?php
// php.ini: session.use_strict_mode = 1 en session.use_only_cookies = 1
session_start();

$user = find_user($_POST['email']);

if ($user && password_verify($_POST['password'], $user['hash'])) {
    // Nieuwe id, oude sessie direct verwijderd
    session_regenerate_id(true);

    $_SESSION['user_id']  = $user['id'];
    $_SESSION['is_admin'] = $user['is_admin'];

    header('Location: /account');
    exit;
}

De aanvaller houdt nu een id over die na het inloggen naar niets meer verwijst. Zijn voorbereiding is bovendien onbruikbaar voor een tweede poging, omdat de server een verzonnen waarde in strikte modus meteen weggooit.

Roep session_regenerate_id altijd aan met true. Zonder dat argument krijgt de gebruiker wel een nieuwe id, maar blijft de oude sessie aan serverzijde bestaan, inclusief de zojuist ingevulde gebruikersgegevens. De aanvaller kan dan gewoon verder met de id die hij had gepland, en de kwetsbaarheid is alleen beter verstopt.

Wat is de impact van session fixation?

Een geslaagde session fixation levert een volledige overname van het account op, met precies de rechten van het slachtoffer. Bij een gewone gebruiker betekent dat inzage in persoonsgegevens, bestellingen of dossiers, en de mogelijkheid om namens die persoon te handelen. Treft het een beheerdersaccount, dan reikt de schade tot het beheer van andere gebruikers en de configuratie van de applicatie.

De ernst loopt van medium tot hoog, en die spreiding is reëel. De aanvaller heeft twee dingen nodig: een manier om de id in de browser van het slachtoffer te krijgen, en een slachtoffer dat daarna daadwerkelijk inlogt. Ontbreekt de eerste voorwaarde, bijvoorbeeld omdat u geen subdomeinen deelt, geen sessie-ids in URL’s accepteert en geen cross-site scripting heeft, dan blijft het risico beperkt. Draait de applicatie op een gedeeld domein met veel subdomeinen, of gaat het om een portaal waar beheerders regelmatig inloggen, dan is dezelfde fout aanzienlijk ernstiger.

Zakelijk vertaalt zich dat naar onterechte transacties, toegang tot persoonsgegevens met meldplicht en fraude die op naam van een echte gebruiker staat. Dat laatste weegt zwaar in een geschil: de logbestanden tonen een normale inlog en daarna normaal gedrag, want er is technisch gezien niets ongebruikelijks gebeurd.

Hoe spoort u session fixation op?

De test is kort en vraagt geen bijzonder gereedschap. Noteer de waarde van de sessiecookie voordat u inlogt, log in en vergelijk de waarde daarna. Blijft hij gelijk, dan vernieuwt de applicatie de identificatie niet en is de kwetsbaarheid aanwezig. Doe dezelfde vergelijking rond een tweede factor, een rolwissel en een wachtwoordwijziging, want veel applicaties vernieuwen alleen bij de eerste inlogstap.

Controleer daarnaast of de server een zelfverzonnen waarde accepteert: zet een cookie met een id die nooit is uitgegeven, vraag een pagina op en kijk of u een geldige sessie krijgt. Kijk ook of de sessie-id ergens in een URL opduikt, in redirects, in Referer-headers of in deelbare links, en of de cookie beperkt is tot de host waar hij thuishoort.

Geautomatiseerde scanners melden hooguit dat de cookie niet verandert en missen de gevallen achter een tweede factor of in een impersonatiefunctie, omdat ze die flow niet doorlopen. Handmatig testen met twee gescheiden browsersessies laat het gedrag wel zien. AssistSec toetst de sessieafhandeling standaard in een penetratietest en laat per bevinding zien met welke id een sessie na het inloggen nog bruikbaar was.

Hoe voorkomt u session fixation?

  • Vernieuw de session id bij elke wijziging van rechten. Bij het inloggen, na een geslaagde tweede factor, bij een rol- of tenantwissel, bij impersonatie door een beheerder en bij een wachtwoordwijziging.
  • Verwijder de oude sessie aan serverzijde. Een nieuwe id uitgeven terwijl de oude sessie blijft leven, verplaatst het probleem alleen.
  • Accepteer alleen identificaties die u zelf heeft uitgegeven. Zet de strikte modus van uw sessiebibliotheek aan, zodat een onbekende waarde wordt geweigerd in plaats van overgenomen.
  • Houd de session id uit de URL. Uitsluitend cookies, geen sessie-ids in querystrings of paden; die lekken via logbestanden, browsergeschiedenis en gedeelde links.
  • Zet de cookievlaggen strak. HttpOnly, Secure, een passende SameSite-waarde en waar mogelijk de __Host- prefix, zodat de cookie aan één host gebonden blijft.
  • Beheers uw subdomeinen. Elk subdomein kan cookies zetten voor het hoofddomein; een vergeten of overgenomen subdomein is daarmee een startpunt voor deze aanval.
  • Beperk de levensduur en maak uitloggen echt. Een inactiviteitstermijn, een absolute maximale duur en het serverzijdig ongeldig maken van de sessie bij uitloggen verkleinen het venster waarin een geplante id nog waarde heeft.

Bronnen

Veelgestelde vragen

Wat is het verschil tussen session fixation en session hijacking?

Bij session hijacking bemachtigt een aanvaller een bestaande, al ingelogde sessie, bijvoorbeeld door een cookie te stelen via cross-site scripting of onversleuteld verkeer. Bij session fixation gebeurt het omgekeerde: de aanvaller kent de id al voordat er iemand inlogt en laat het slachtoffer die id voor hem authenticeren. Het resultaat is hetzelfde, maar de aanvaller hoeft achteraf niets te onderscheppen.

Zijn moderne frameworks standaard beschermd tegen session fixation?

Grotendeels wel. Laravel, Django, Rails, ASP.NET Core en Spring Security vernieuwen de session id bij het inloggen zodra u hun eigen authenticatiemechanisme gebruikt. Het misgaat vooral bij zelfgebouwde inlogschermen, bij een tweede inlogroute naast de standaardroute en bij step-up authenticatie of impersonatie, waar de rechten wijzigen zonder dat er formeel opnieuw wordt ingelogd.

Helpen HttpOnly- en Secure-cookies tegen session fixation?

Ze helpen, maar ze lossen het niet op. HttpOnly houdt JavaScript weg bij de cookie en Secure voorkomt verzending over gewoon HTTP, waardoor twee manieren om een id te planten of te onderscheppen wegvallen. Een aanvaller die de cookie vanaf een subdomein zet of via een URL-parameter binnenbrengt, komt er nog steeds langs. Alleen het vernieuwen van de id bij het inloggen neemt de aanval bij de wortel weg.

Wanneer moet ik de session id vernieuwen?

Bij elke wijziging van het rechtenniveau. Dat is in ieder geval het moment van inloggen, maar ook het afronden van een tweede factor, het wisselen van rol of tenant, het overnemen van een account door een beheerder en het wijzigen van een wachtwoord. Vernieuw ook na het uitloggen, en verwijder daarbij de sessie aan serverzijde in plaats van alleen de cookie te wissen.

Verwante artikelen

Druk op / om te zoeken · Esc