Hoe deze controle werkt
Scanaris leest de Set-Cookie-headers die je server stuurt bij het laden van de homepage en bekijkt de attributen van elke cookie. Het controleert drie dingen: dat hij Secure draagt (alleen vereist als de site via HTTPS gaat), dat hij HttpOnly draagt en dat hij SameSite declareert. Suggereert de cookienaam een sessie of authenticatie (session, auth, token, sid en dergelijke), dan heeft de bevinding gemiddelde ernst; anders laag. Er is één speciaal geval met een eigen gemiddelde ernst: SameSite=None zonder Secure, dat moderne browsers ronduit weigeren, waardoor de cookie niet werkt en bovendien een configuratie verraadt die voor cross-site-gebruik bedoeld is. Alleen de cookies in dat eerste antwoord worden gezien; die na het inloggen worden aangemaakt controleer je beter met het commando hieronder of met de geauthenticeerde scan.
Wat elk attribuut betekent
- Secure: de cookie reist alleen over HTTPS. Zonder stuurt een HTTP-verzoek (een oude link, een publiek netwerk) hem onversleuteld en leest iedereen op het netwerk hem.
- HttpOnly: JavaScript kan hem niet lezen. Het is het verschil tussen een XSS die de sessie steelt en een die dat niet kan.
- SameSite: wanneer hij vanaf een andere site wordt meegestuurd.
Laxstuurt hem bij normale navigaties maar niet bij verzoeken die een andere website start;Strictnooit van buitenaf;Nonealtijd, en vereistSecure. Het is de belangrijkste verdediging tegen CSRF.
Wat een aanvaller doet met een sessiecookie zonder deze attributen
Zonder HttpOnly leest elk geïnjecteerd script document.cookie, stuurt de waarde naar zijn server en logt in met jouw sessie. Zonder SameSite kan een vreemde pagina een formulier naar je site sturen met de cookie erbij en namens de gebruiker een bestelling plaatsen of een e-mailadres wijzigen. Zonder Secure volstaat het dat de gebruiker op een gedeeld netwerk een http://-link naar je domein opent. In alle drie de gevallen is het resultaat hetzelfde: iemand handelt als jouw gebruiker.
Hoe je het oplost, afhankelijk van je framework
// Express met express-session
app.use(session({
cookie: { secure: true, httpOnly: true, sameSite: 'lax' }
}));
// Next.js (Route Handler of Server Action)
cookies().set('session', value, { httpOnly: true, secure: true, sameSite: 'lax', path: '/' });
// Nuxt (server/api)
setCookie(event, 'session', value, { httpOnly: true, secure: true, sameSite: 'lax', path: '/' });
Met Supabase in een app met een server bewaart de bibliotheek @supabase/ssr de sessie in cookies met deze attributen; in een pure SPA gaat de sessie naar localStorage, wat de controle tokens in storage meet. In Django, Rails of Laravel worden de attributen in de sessie-instellingen geconfigureerd en zijn ze standaard redelijk; controleer dat niemand ze heeft verlaagd om een ontwikkelprobleem "op te lossen".
Controleer of het is opgelost
curl -sI https://jouwdomein.com | grep -i "set-cookie"
curl -sI -X POST https://jouwdomein.com/api/login -d "..." | grep -i "set-cookie"
Elke sessiecookie moet Secure; HttpOnly; SameSite=Lax (of Strict) tonen. Is je login een formulier, bekijk dan het antwoord in de browsertools, tabblad Netwerk, antwoordheaders.
Veelgestelde vragen
Mijn cookie wordt door JavaScript aangemaakt (analytics, voorkeuren). Hij kan niet HttpOnly zijn.
Klopt, en dat is geen probleem: die cookies geven nergens toegang toe. De controle markeert hem met lage ernst voor het ontbrekende attribuut; wat telt is dat de sessiecookies het wél hebben.
Ik heb SameSite=None nodig omdat mijn app in een iframe op een ander domein draait.
Dan is het legitiem, altijd met Secure. Zorg dat de andere CSRF-verdedigingen (tokens in formulieren, controle van de Origin) actief zijn, want SameSite beschermt je niet meer.
Lax of Strict?
Lax voor de meeste gevallen: het behoudt de sessie wanneer de gebruiker via een externe link binnenkomt. Strict als de cookie toegang geeft tot iets heel gevoeligs en je accepteert dat de gebruiker opnieuw moet inloggen als hij van buiten komt.