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.
- Een script dat via een gat in de pagina komt, probeert het sessie-id te lezen.
- Een andere site laat de browser van een ingelogde bezoeker stiekem een bericht plaatsen.
- Iemand luistert het netwerk af om cookies te onderscheppen.
Antwoord
httponly: een script kan de cookie dan niet lezen.samesite: de cookie gaat niet mee met een verzoek vanaf een andere site. FastAPI zetlaxal standaard; zie les 4.securesamen methttps: de cookie gaat alleen versleuteld over het netwerk.
Door naar de volgende reeks: Wachtwoorden veilig opslaan.