Direct naar inhoud

Cross-site request forgery (CSRF)

CWE-352OWASP A01:2021Bijgewerkt 29 augustus 20265 min leestijd

Cross-site request forgery (CSRF) is een kwetsbaarheid waarbij een aanvaller de browser van een ingelogde gebruiker ongemerkt een verzoek laat versturen. De site voert dat verzoek uit alsof het van de gebruiker zelf komt, en zo worden acties als een wachtwoordwijziging of een overboeking uitgevoerd zonder toestemming van het slachtoffer.

Cross-site request forgery, kortweg CSRF, draait niet om het stelen van gegevens maar om het misbruiken van vertrouwen: uw eigen gebruiker voert, zonder het te weten, een actie uit die de aanvaller heeft opgesteld. In deze uitleg leest u wat CSRF is, hoe zo’n aanval verloopt, wat een aanvaller ermee kan bereiken en hoe u uw applicatie ertegen beschermt.

Wat is cross-site request forgery?

Cross-site request forgery (CSRF) is een kwetsbaarheid waarbij een aanvaller de browser van een ingelogde gebruiker misbruikt om een ongewenste, statewijzigende actie uit te voeren op een website waar die gebruiker is aangemeld. Het slachtoffer merkt er niets van: één bezoek aan een geprepareerde pagina is genoeg om op de achtergrond een verzoek af te vuren dat de doelsite als volkomen legitiem beschouwt.

De kern zit in een gewoonte van de browser. Stelt u zich een bank voor die elke opdracht uitvoert zolang die in een envelop met uw persoonlijke zegel zit. Een oplichter kan dat zegel niet namaken, maar hij kan u wél een kant-en-klare opdrachtbrief toeschuiven en u verleiden die in uw eigen, al verzegelde envelop te stoppen. De envelop is hier uw sessiecookie: de browser plakt hem automatisch op elk verzoek naar de bank, ook op een verzoek dat door een vreemde pagina in gang is gezet. De site ziet een geldig zegel en gehoorzaamt.

Hoe werkt een CSRF-aanval?

CSRF loert overal waar een applicatie een gevoelige actie uitvoert op basis van niets meer dan de sessiecookie: een e-mailadres wijzigen, een wachtwoord resetten, geld overmaken of rechten toekennen. Omdat de browser die cookie ongevraagd meestuurt, hoeft de aanvaller de inloggegevens helemaal niet te kennen.

Kwetsbaar:

// Wijzigt het e-mailadres, vertrouwt enkel op de sessiecookie
app.post('/account/email', (req, res) => {
  if (!req.session.userId) return res.status(401).send('Niet ingelogd');
  updateEmail(req.session.userId, req.body.email);
  res.send('E-mailadres bijgewerkt');
});

Dit endpoint controleert alléén of er een geldige sessie is en voert daarna klakkeloos de wijziging door. Nergens wordt gecontroleerd of het verzoek ook echt vanaf de eigen website is verstuurd. De aanvaller hoeft dus alleen maar een pagina te bouwen die dit verzoek namens het slachtoffer verstuurt, en die pagina onder de aandacht van een ingelogde gebruiker te brengen.

<!-- Staat op een site van de aanvaller en verstuurt zichzelf meteen -->
<form action="https://bank.example/account/email" method="POST">
  <input type="hidden" name="email" value="aanvaller@kwaad.example">
</form>
<script>document.forms[0].submit()</script>

Zodra het slachtoffer deze pagina opent (via een phishingmail, een advertentie of een verborgen frame) verstuurt de browser het formulier, inclusief de sessiecookie van de bank. Het e-mailadres van het account wordt gewijzigd naar dat van de aanvaller, die vervolgens een wachtwoordreset aanvraagt en het account overneemt. Er komt geen enkel gestolen wachtwoord aan te pas.

Veilig:

import { randomBytes, timingSafeEqual } from 'node:crypto';

// Bij het tonen van het formulier: genereer een token en bewaar het in de sessie
app.get('/account/email', (req, res) => {
  req.session.csrfToken = randomBytes(32).toString('hex');
  res.render('email-form', { csrfToken: req.session.csrfToken });
});

app.post('/account/email', (req, res) => {
  if (!req.session.userId) return res.status(401).send('Niet ingelogd');

  const expected = req.session.csrfToken ?? '';
  const received = req.body.csrfToken ?? '';
  const ok = expected.length === received.length
    && timingSafeEqual(Buffer.from(expected), Buffer.from(received));
  if (!ok) return res.status(403).send('Ongeldig CSRF-token');

  updateEmail(req.session.userId, req.body.email);
  res.send('E-mailadres bijgewerkt');
});

De veilige variant voegt een geheim toe dat de aanvaller onmogelijk kan raden. Bij het opvragen van het formulier krijgt de gebruiker een willekeurig token, dat de server ook in de sessie bewaart. Bij het versturen moet datzelfde token meekomen, en de server vergelijkt beide met een timingveilige controle. De pagina van de aanvaller kent dit token niet (hij kan de respons met het formulier immers niet uitlezen) en zijn verzoek strandt op een 403. Zet daarbovenop de sessiecookie op SameSite=Lax of Strict, zodat de browser die cookie sowieso niet meer meestuurt bij een verzoek dat vanaf een vreemde site vertrekt.

Een CSRF-token beschermt niet tegen een aanvaller die al scripts kan uitvoeren op uw site. Via cross-site scripting (XSS) leest hij het token gewoon uit en omzeilt hij de hele verdediging. Behandel XSS en CSRF daarom als twee kwetsbaarheden die u allebei moet dichten.

Wat is de impact van CSRF?

De ernst van CSRF is doorgaans medium, en dat heeft een duidelijke reden. Door de same-origin policy kan de aanvaller de respons niet uitlezen; hij stuurt zijn verzoek blind en heeft bovendien een ingelogd slachtoffer nodig dat hij naar zijn pagina weet te lokken. CSRF steelt dus geen gegevens, maar dwingt acties af.

Toch kan die ene afgedwongen actie zwaar wegen. Wordt het e-mailadres of wachtwoord gewijzigd, dan volgt vaak een volledige accountovername. Andere klassieke doelwitten zijn een overboeking, het aanpassen van rechten, het toevoegen van een beheerder of het wijzigen van instellingen die de beveiliging verzwakken. In een beheerdersinterface kan één succesvol CSRF-verzoek de configuratie van een heel systeem omzetten. De schade voor het bedrijf loopt daarmee van een gekaapt klantaccount tot reputatieschade en financieel verlies, precies waarom u ook een “middelzware” kwetsbaarheid als deze niet mag laten liggen.

Hoe spoor je CSRF op?

Bij een test loopt een pentester elke statewijzigende actie na (alles wat iets aanmaakt, wijzigt of verwijdert) en kijkt of het verzoek beschermd is met een onvoorspelbaar, per sessie uniek token. Vervolgens wordt dat token weggelaten, hergebruikt uit een andere sessie of licht aangepast om te zien of de server het verzoek dan alsnog uitvoert. Ook de SameSite-instelling van de sessiecookie en de controle op de Origin- en Referer-headers worden nagelopen. Endpoints die state wijzigen via een GET-verzoek zijn een klassiek alarmsignaal, want die zijn triviaal te misbruiken. Geautomatiseerde scanners melden het ontbreken van een token, maar beoordelen zelden of de token-controle ook echt sluitend is. AssistSec neemt CSRF standaard mee in een penetratietest en toetst juist die randgevallen waarin een token wel aanwezig lijkt maar niet correct wordt gevalideerd.

Hoe voorkom je CSRF?

  • Bescherm elke statewijzigende actie met het synchronizer-tokenpatroon: een onvoorspelbaar token per sessie dat de server bij elk verzoek verifieert.
  • Zet sessiecookies op SameSite=Lax of SameSite=Strict, aangevuld met Secure en HttpOnly.
  • Voer nooit een wijziging uit via een GET-verzoek; reserveer GET voor het ophalen van gegevens.
  • Controleer bij gevoelige acties de Origin- en Referer-headers als extra laag.
  • Vraag voor kritieke handelingen (wachtwoord- of e-mailwijziging, betalingen) opnieuw om het wachtwoord of een tweede factor.
  • Leun op de ingebouwde CSRF-bescherming van uw framework in plaats van zelf iets te bouwen, en houd die ingeschakeld.
  • Dicht cross-site scripting consequent, want XSS omzeilt elke CSRF-token die u instelt.

Bronnen

Veelgestelde vragen

Wat is het verschil tussen CSRF en XSS?

Bij XSS injecteert een aanvaller kwaadaardige scripts die in de browser van het slachtoffer draaien en data kunnen uitlezen. Bij CSRF draait er geen script op de doelsite; de aanvaller laat de browser alleen een verzoek versturen en misbruikt de meegestuurde sessiecookie. XSS is bovendien vaak een manier om CSRF-beveiliging te omzeilen.

Beschermt HTTPS tegen CSRF?

Nee. HTTPS versleutelt het verkeer, maar een CSRF-verzoek is op zichzelf een geldig, correct opgebouwd verzoek. De versleuteling verandert niets aan het feit dat de browser de sessiecookie automatisch meestuurt. U hebt een CSRF-token of een strikte SameSite-cookie nodig.

Is de SameSite-cookie alleen voldoende tegen CSRF?

SameSite=Lax of Strict blokkeert de meeste klassieke aanvallen en is een sterke basislaag, maar dek niet elk scenario af: denk aan subdomeinen, oudere browsers of GET-verzoeken die state wijzigen. Combineer SameSite daarom met een CSRF-token voor gevoelige acties.

Kan een CSRF-aanval de respons uitlezen?

Nee. Door de same-origin policy kan de aanvaller de respons van het slachtoffer niet lezen. Hij stuurt het verzoek blind. Daarom is CSRF vooral gevaarlijk voor acties die iets wijzigen, niet voor het stelen van gegevens.

Verwante artikelen

Druk op / om te zoeken · Esc