Ga naar hoofdinhoud

Cookie of sessie?

Je kunt nu allebei: een cookie met de naam erin, en een sessie waarbij alleen een sessie-id bij de bezoeker ligt. Ze lossen hetzelfde probleem op — je server herkent een terugkerende bezoeker niet — maar ze doen het op een andere plek. Deze les gaat over kiezen.

Wat ze allebei oplossen

Elk verzoek staat op zichzelf. Vraag je twee pagina's achter elkaar op, dan ziet je server twee losse verzoeken zonder enig verband. Er is niets in HTTP dat zegt "dit is dezelfde persoon als daarnet".

Een cookie is het enige wat dat gat dicht. De server geeft de browser een stukje tekst mee, en de browser stuurt dat vanaf dan bij élk verzoek terug. Dat geldt voor allebei de aanpakken: een sessie is ook een cookie. Het verschil zit niet in de techniek, maar in wát er in die cookie staat.

Het verschil in één zin

Bij een cookie staat het antwoord zelf bij de bezoeker. Bij een sessie staat er alleen een nummertje, en het antwoord ligt bij jou.

Herkend worden met een sessie
  1. Browser: De browser stuurt de cookie mee. Bij elk verzoek aan jouw server gaat sessie_id automatisch mee, ongevraagd. · Onthouden met een cookie

  2. Server: FastAPI geeft de cookie door. De parameter met Cookie(default="") vangt hem op, net als Form bij een formulier. · Onthouden met een cookie

  3. Server: De server zoekt de sessie op. Het sessie-id is een sleutel in sessies.db; daar staan de echte gegevens. · Sessies

  4. Server: Nu pas weet je wie er is. De naam komt uit jouw database, niet uit de cookie — dus die klopt. · Sessies

  5. Server: Het antwoord gaat terug. Was er nog geen sessie, dan zet set_cookie het nieuwe sessie-id erop. · Sessies

  6. Browser: De browser bewaart de cookie. En stuurt hem bij het volgende verzoek weer mee — het rondje begint opnieuw.

Zet je de naam in de cookie, dan slaat de server stap drie en vier over: hij leest de naam direct uit wat de browser stuurde. Sneller, maar hij gelooft op zijn woord wat er binnenkomt.

Naast elkaar

Cookie met de gegevens erinSessie
Waar staan de gegevensBij de bezoekerOp jouw server
Wat reist er meeAlles wat je bewaardeAlleen het sessie-id
Kan de bezoeker het wijzigenJaNee
Mag je het vertrouwenNeeJa
Blijft het na een herstart van je serverJaAlleen als je het opslaat
Hoeveel past erinOngeveer 4 kBZoveel als je database aankan
Wat kost hetNiets extra'sEen extra opzoeking per verzoek

De hoofdvraag

Wat gebeurt er als de bezoeker deze waarde zelf verandert?

  • Niets ergs — dan mag het in de cookie. De bezoeker verandert de eigen naam in het formulier, of zet het donkere thema aan na eerst het lichte te hebben gekozen. Daar heeft niemand last van, ook de bezoeker zelf niet.
  • Dan kom je ergens waar je niet hoort — dan moet het een sessie zijn. Berichten van iemand anders verwijderen, betalen zonder te betalen, bij andermans gegevens komen.

Dat is dezelfde vraag als in Server of browser?, en het antwoord komt uit dezelfde regel: alles wat op de computer van de bezoeker staat, is van de bezoeker.

Waarom niet altijd een sessie

Een sessie is veiliger, dus waarom zou je ooit een gewone cookie nemen?

Omdat een sessie werk kost dat je niet altijd nodig hebt. Elk verzoek betekent een extra opzoeking in je database, en je moet die sessies ergens laten. Ze blijven groeien: elke bezoeker die één keer langskomt laat een rij achter, ook zonder ooit terug te komen. Op een echte site ruim je die na verloop van tijd op.

Voor een voorkeur die niemand kan schaden — welke taal, welk thema, of het uitklapmenu open moet staan — is dat allemaal onnodig. Zet het in een cookie en klaar.

Testen

Je hebt allebei al draaien. Kijk nog één keer in het tabblad App naar je cookies en vergelijk wat je ziet: bij de cookie-aanpak stond je naam er leesbaar in, bij de sessie een rij tekens die niets verraadt.

Opdrachten

Opdracht 1: Predict - Wie wint?

Twee sites bewaren allebei je naam. Site A zet naam=Jan in een cookie. Site B zet sessie_id=9f2c... in een cookie en de naam in een database.

Je opent bij allebei de ontwikkelaarstools en verandert de cookie in de naam van iemand anders.

Vraag: bij welke site sta je daarna als die ander te boek?

Tip

Bij welke van de twee komt de naam die de pagina toont uit iets wat jij zojuist hebt getypt?

Antwoord

Bij site A. Die leest de naam rechtstreeks uit de cookie, dus jouw wijziging is meteen waar.

Bij site B verandert er niets: je hebt een sessie-id gewijzigd dat nu nergens meer op past, dus de server behandelt je als een nieuwe bezoeker met een leeg formulier. Je komt niet in de sessie van die ander terecht — daarvoor zou je het sessie-id van die ander moeten raden, en dat zijn 32 willekeurige tekens.

Opdracht 2: Run - Kijk wat er over de lijn gaat

Open het tabblad Netwerk zoals in Kijken wat de browser doet, herlaad je gastenboek en klik het bovenste verzoek aan. Zoek bij Headers, onder Verzoekheaders, de regel die met Cookie: begint.

Antwoord

Daar staat precies wat de browser meestuurde, bijvoorbeeld Cookie: sessie_id=9f2c4a7e10b8d3f5a6c1e0b4d7928f3a.

Dit gebeurt bij elk verzoek, ook bij het ophalen van je CSS-bestand en je afbeeldingen. Daarom is die 4 kB-grens een echte grens: alles wat je in een cookie zet, reist de hele dag heen en weer over het netwerk.

Opdracht 3: Investigate - Twee browsers

Open je gastenboek in een gewoon venster en in een privévenster, en plaats in allebei een bericht met een andere naam.

Antwoord

Je krijgt twee sessies. Een privévenster begint met een lege cookiejar, dus er gaat geen sessie_id mee en je server maakt een nieuwe aan.

Dat laat zien waar de identiteit echt zit: niet bij de persoon achter het scherm en niet bij de computer, maar bij één browservenster met één cookie. Log je op een gedeelde computer in, dan zit die cookie er na jou nog steeds — daarom bestaat er een uitlogknop.

Geef je gastenboek een knop waarmee de bezoeker kiest of berichten van nieuw naar oud of van oud naar nieuw staan, en onthoud die keuze in een gewone cookie.

Tip

Dit is een voorkeur die niemand kan schaden, dus een cookie volstaat — geen sessie nodig. Lees hem uit met Cookie(default="nieuw") en draai je lijst om met list(reversed(...)) als de waarde oud is.

Antwoord
@app.get("/volgorde/{keuze}")
async def volgorde(keuze: str):
antwoord = RedirectResponse(url="/berichten", status_code=303)
antwoord.set_cookie(key="volgorde", value=keuze, max_age=60 * 60 * 24 * 30)
return antwoord

@app.get("/berichten")
async def berichten(request: Request, volgorde: str = Cookie(default="nieuw")):
with SqliteDict("gastenboek.db") as db:
alle_berichten = list(db.items())
if volgorde == "oud":
alle_berichten = list(reversed(alle_berichten))
return templates.TemplateResponse(request, "berichten.html", {"berichten": alle_berichten})

In je template:

<a href="/volgorde/nieuw">Nieuwste eerst</a>
<a href="/volgorde/oud">Oudste eerst</a>

Verandert een bezoeker deze cookie in banaan, dan valt de lijst terug op de standaardvolgorde. Vervelend voor die bezoeker, verder gebeurt er niets — en dat is precies waarom een cookie hier genoeg is.

Opdracht 5: Make - Uitloggen

Maak een endpoint /uitloggen dat de sessie op de server weggooit én de cookie bij de bezoeker weghaalt.

Tip

Twee dingen dus: del sessies[sessie_id] op de server en delete_cookie op het antwoord. Vang met if sessie_id in sessies op dat de sessie er misschien niet meer is.

Antwoord
@app.get("/uitloggen")
async def uitloggen(sessie_id: str = Cookie(default="")):
with SqliteDict("sessies.db") as sessies:
if sessie_id in sessies:
del sessies[sessie_id]
sessies.commit()

antwoord = RedirectResponse(url="/gastenboek", status_code=303)
antwoord.delete_cookie("sessie_id")
return antwoord

Allebei is nodig. Haal je alleen de cookie weg, dan blijft de sessie op je server staan; iemand die het oude sessie-id nog heeft, is gewoon weer binnen. Gooi je alleen de sessie weg, dan houdt de bezoeker een cookie die nergens meer op past — dat werkt, maar je server maakt bij het volgende bezoek een tweede sessie aan naast de eerste.