Ga naar hoofdinhoud

Cookies afschermen: in de praktijk

Met httponly, samesite, uitloggen en een verlooptijd op de server uit les 3 tot en met 6 is je sessie-cookie goed afgeschermd. Er zijn nog drie dingen die echte sites met hun cookies doen.

Wat jij zelf doet​

Kies hoe lang een sessie geldt​

In les 6 kreeg je sessie een eigen tot op de server. Hoe lang die is, staat in GELDIG: in deze reeks 30 dagen. Voor een gastenboek is dat prima; een banksite logt je na een kwartier zonder activiteit uit. Dat werkt anders dan jouw tot: die telt vanaf het inloggen, en schuift niet op als je iets doet. Een bank zet de eindtijd bij elk verzoek opnieuw een kwartier verder. Kies een tijd die past bij wat er met een sessie kan.

Verander alleen max_age niet en denk dat je klaar bent. Dat zegt alleen tegen de browser wanneer hij de cookie weggooit; wie het sessie-id heeft gekopieerd, trekt zich daar niets van aan. De tijd die telt, is de tot in sessies.db.

Wat grote sites nog meer doen​

Dit hoef je voor je eigen project niet te bouwen. Klap open wat je wilt lezen.

secure: alleen over https

Tot nu toe draait je server op http, en dan gaat een cookie als leesbare tekst over het netwerk. Op een echte site gebruik je https, waar alles versleuteld is. Met secure=True stuurt de browser de cookie alleen over https:

antwoord.set_cookie(
key="sessie_id",
value=sessie_id,
max_age=GELDIG,
httponly=True,
samesite="lax",
secure=True,
)

Op je eigen computer laat Chrome een secure-cookie toch door, omdat 127.0.0.1 als veilig adres telt. Online, op een adres zonder https, bewaart de browser hem niet. Zet je secure aan, zorg dan dat je echte site https heeft.

Een eigen code in elk formulier (CSRF-token)

samesite="lax" uit les 4 houdt een formulier van een andere site al tegen: de cookie gaat niet mee. Sites die niet alleen op de browser willen vertrouwen, of die samesite="none" nodig hebben, zetten daarnaast in elk formulier een eigen code, een CSRF-token. De server maakt die code als hij het formulier toont, met secrets.token_hex(16), bewaart hem in de sessie en zet hem in een verborgen veld:

<input type="hidden" name="token" value="{{ token }}">

Het endpoint dat het formulier ontvangt, krijgt een parameter token: str = Form("") en vergelijkt die eerst met de code in de sessie:

if not secrets.compare_digest(token.encode(), mijn.get("token", "").encode()):
raise HTTPException(status_code=403, detail="Dit formulier komt niet van deze site")

Een andere site kan je formulier nabouwen, maar kent de code niet: die staat alleen in jouw pagina en in jouw sessie. secrets.compare_digest vergelijkt twee codes in een vaste tijd, zodat de duur niet verraadt hoeveel tekens er kloppen; met .encode() werkt dat ook als iemand een teken als é stuurt.

Dit is geen les in de reeks. Met lax komt een formulier van een andere site zonder cookie aan, dus op je eigen server zie je geen gat. Om het wel te zien, zou je een tweede site moeten bouwen op localhost, en je cookie eerst zelf onveiliger maken met samesite="none" en secure=True. Een tweede site op 127.0.0.1 met een andere poort telt niet: voor samesite is dat dezelfde site, en daar gaat je cookie ook met lax mee (zie les 4).

Opdracht: welke instelling helpt?​

Kies bij elke situatie de cookie-instelling die het meest helpt.

  1. Een script dat via een gat in de pagina komt, probeert het sessie-id te lezen.
  2. Een andere site laat de browser van een ingelogde bezoeker stiekem een bericht plaatsen.
  3. Iemand luistert het netwerk af om cookies te onderscheppen.
Antwoord
  1. httponly: een script kan de cookie dan niet lezen.
  2. samesite: de cookie gaat niet mee met een verzoek vanaf een andere site. FastAPI zet lax al standaard; zie les 4.
  3. secure samen met https: de cookie gaat alleen versleuteld over het netwerk.

Door naar de volgende reeks: Wachtwoorden veilig opslaan.