0%

Hoofdstuk 4

Sessies die standhouden

Een sessie-id is een sleutel

Na een login wordt het wachtwoord niet opnieuw gestuurd. De browser stuurt een met een , en wie die id stuurt, is voor zover de server kan zien ana. In with Python maakte je de id met secrets.token_urlsafe(16), dat is 128 willekeurige bits, dus niemand raadt er een. Dit hoofdstuk gaat over alle andere manieren om een werkende sessie-id in handen te krijgen.

Wat er nieuw is in het project van dit hoofdstuk

Het project is je server van het eind van hoofdstuk 3 met één toegevoegde . Hier is wat de nieuwe code doet en tot welke fout ze uitnodigt.

POST /prefs onthoudt een paginagrootte voor een lezer die niet is ingelogd. Om iets te onthouden heeft de server een sessie nodig, dus deze route deelt er al vóór de login een uit: een bezoekerssessie, waarvan user None is.

    elif segments == ["prefs"]:
        if method == "POST":
            response = prefs_error(request)
            if response is None:
                page_size = json_body(request)["page_size"]
                response = json_response(200, {"page_size": page_size})
                session = current_session(request, state)
                if session is None:
                    session_id = start_session(state, None, page_size=page_size)
                    response["headers"]["set-cookie"] = session_cookie(session_id)
                else:
                    session["page_size"] = page_size

Een sessie is nu {"user": ..., "page_size": ...}, en GET /me meldt beide: {"user": null, "page_size": 5} voor een bezoeker die 5 koos. Met de functie kwamen twee hulpfuncties mee: session_id_from(request) leest de id uit de cookie, en current_session(request, state) geeft de sessie terug die die id aanduidt. sample_state() heeft ook een vermelding "clock" voor les 3, en drie functies zijn stubs die je in les 2, 3 en 5 schrijft.

De logintak moest ook veranderen, omdat een bezoeker die inlogt al een sessie heeft. Na een juist wachtwoord voert hij nu deze vier regels uit:

                session_id = session_id_from(request)
                if session_id is None or session_id not in state["sessions"]:
                    session_id = start_session(state, None)
                state["sessions"][session_id]["user"] = user

Als het verzoek een cookie meebrengt die een sessie aanduidt, houdt de login die sessie en schrijft hij de gebruiker erin. Zo overleeft de paginagrootte de login.

Lees de vier regels nog eens en vraag je af wie de sessie-id koos. De server koos hem niet op dit moment. De id kwam binnen in het verzoek, en de server controleerde alleen dat hij de id kent. Een id die aan een bezoeker was uitgedeeld, wordt dan vertrouwd met een ingelogde gebruiker.

Welke van de vier meegeleverde regels in de logintak geeft een ingelogde gebruiker aan een id die de server niet koos?

De aanval, stap voor stap

Er doen drie partijen mee. De id is ingekort tot K7f...Qw.

attacker              victim's browser        server
   |                       |                   |
1  |-- POST /prefs --------------------------->|  new session K7f...Qw:
   |<-- set-cookie: session=K7f...Qw ----------|  user None, page_size 5
2  |-- plants K7f...Qw --->|                   |
3  |                       |-- POST /login --->|
   |                       |   cookie K7f...Qw |
   |                       |   ana's password  |
4  |                       |                   |  its user is now "ana"
5  |-- GET /me, cookie: session=K7f...Qw ----->|
   |<-- {"user": "ana", "page_size": 5} -------|
  1. De aanvaller stuurt POST /prefs en krijgt een sessie-id.

  2. De aanvaller krijgt die id in de browser van het slachtoffer. Het volgende deel zegt hoe.

  3. Het slachtoffer logt in met haar eigen wachtwoord. Haar browser stuurt de geplante cookie mee.

  4. De server vindt de sessie die de cookie noemt en schrijft ana erin.

  5. De aanvaller stuurt GET /me met de id die hij sinds stap 1 heeft, en de server antwoordt als ana.

Dezelfde id reist mee in stap 1, 3 en 5. De aanvaller raadde nooit een id en stal er nooit een. De server promoveerde een die de aanvaller al had. Deze aanval heet sessiefixatie (session fixation): de aanvaller legt de id eerst vast, en de login van het slachtoffer maakt hem waardevol.

Dit blok speelt de aanval na op een gewone . De id is met opzet leesbaar; een echte is willekeurig.

Try it

Het drukt the victim's cookie now holds: id-the-attacker-knows en the attacker asks who that is: ana af.

Wat drukt dit af?

sessions = {"abc": {"user": None}}
sessions["abc"]["user"] = "ana"
print(len(sessions), sessions["abc"]["user"])

Hoe komt de id in haar browser?

Stap 2 heeft twee gebruikelijke routes.

  • Een naburige host. Stel dat de leeslijst op reading.example staat en een notitie-app op notes.reading.example. Een pagina op de notitiehost kan een cookie zetten voor het hele domein reading.example, en een browser stuurt die cookie dan ook naar de leeslijst. Les 4 sluit deze route in de cookie zelf.

  • Een gedeelde computer. De aanvaller bezoekt eerst de leeslijst, noteert de id, en laat de cookie achter. De volgende persoon aan die computer logt ermee in.

Niets op deze site is een browser, dus de cursus beschrijft stap 2 en laat hem niet zien: een echte browser is wat de geplante cookie zou meedragen. Elke andere stap draait in de volgende les tegen je server.

Een server die de sessie-id ook uit de accepteerde, zou een derde route toevoegen: een link met de id erin. Deze server leest de id alleen uit de cookie.

Vier manieren om een sessie-id kwijt te raken

Hoe de id verloren gaatWat het betekentWaar het wordt afgehandeld
GeradenDe aanvaller probeert id’s tot er een werktGesloten in HTTP with Python door een id uit secrets
VastgelegdDe aanvaller kent de id voordat hij iets waard isDeze aanval; les 2
Onderweg of uit een pagina gelezenEr wordt een kopie gemaakt terwijl de cookie een netwerk zonder HTTPS oversteekt, of door een script in een paginaLes 4
Nooit ingetrokkenEen kopie blijft werken omdat de server de sessie nooit beëindigtBevinding F3; les 3

Een sessie-id werkt nog een jaar nadat zijn gebruiker hem voor het laatst gebruikte. Welke van de vier manieren is dat?

De reparatie, in één zin

Als verandert waarmee een sessie vertrouwd wordt, geef haar dan een nieuwe id. Een login is zo’n verandering, en de oude id, die iemand anders misschien kent, moet niets meer aanduiden.

Try it

Het drukt the victim's new id is 22 characters the attacker never saw en the attacker asks who the old id is: None af. De paginagrootte is naar de nieuwe sessie verhuisd, en de oude id duidt niets aan.

De aanvaller heeft het wachtwoord van ana nooit gezien, en ook niet de cookie die haar login teruggaf. Wat wist hij wel?

Er is in deze les niets aan je server veranderd. De volgende les voert de aanval erop uit en sluit hem daar.