CSRF maakt misbruik van een automatisme van de browser: bij elk verzoek naar een site stuurt hij de cookies van die site mee, ook de sessiecookie. Een externe pagina kan dus een verzoek naar jouw site laten vertrekken, en de server ontvangt het met de inloggegevens van de ingelogde persoon. Niets onderscheidt het van een bewuste klik, tenzij de site controleert of het verzoek echt uit zijn eigen formulieren komt. MITRE registreert de zwakte als CWE-352; je ziet ook de afkorting XSRF.
Hoe een CSRF-aanval werkt
De aanvaller maakt een pagina met een verzoek naar de doelsite: een onzichtbare afbeelding waarvan de URL een actie start, of een verborgen formulier dat JavaScript automatisch verstuurt. Daarna hoeft hij het slachtoffer alleen nog naar die pagina te lokken, met een link in een e-mail, een reactie of een bericht. Is het slachtoffer op dat moment ingelogd op de doelsite, dan vertrekt het verzoek met zijn sessie.

Zoals OWASP benadrukt, ziet de aanvaller het antwoord van de server niet. CSRF richt zich dus op verzoeken die iets veranderen: het e-mailadres van een account wijzigen, een instelling aanpassen, een gebruiker aanmaken, inhoud verwijderen. In de beheeromgeving van een WordPress-site zijn zulke acties vaak genoeg om de controle over te nemen.
De rol van SameSite-cookies
Het cookie-attribuut SameSite beperkt het meesturen van cookies vanaf een andere site. Met Strict gaat de cookie alleen mee met verzoeken die van dezelfde site komen; met Lax gaat hij ook mee bij een navigatie op topniveau met een veilige methode, zoals een gewone GET-link. MDN vermeldt dat sommige browsers Lax als standaard toepassen als het attribuut ontbreekt. Dat is een nuttige maar gedeeltelijke bescherming: een actie die via een GET-link wordt gestart, komt er nog door, en niet alle browsers gedragen zich hetzelfde. Het komt dus bovenop anti-CSRF-tokens, zonder ze te vervangen.
Voor WordPress: nonces en rechtencontrole
WordPress beschermt zijn formulieren en actielinks met nonces: tokens die gekoppeld zijn aan de gebruiker, zijn sessie en de gevraagde actie, en die een externe site niet kan raden. Ze zijn standaard 24 uur geldig, verdeeld over twee periodes van 12 uur. Een plugin die eigen acties toevoegt, moet ze ook gebruiken:
// In het formulier
wp_nonce_field( 'mijnplugin_opslaan', 'mijnplugin_nonce' );
// Bij de verwerking
check_admin_referer( 'mijnplugin_opslaan', 'mijnplugin_nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'Toegang geweigerd' );
}
wp_nonce_field()enwp_nonce_url()voegen het token toe aan een formulier of een URL;check_admin_referer()controleert het in de beheeromgeving,check_ajax_referer()bij een AJAX-verzoek,wp_verify_nonce()elders;current_user_can()blijft verplicht: de WordPress-documentatie benadrukt dat een nonce niet dient voor authenticatie of toegangscontrole.
Hoe je je als eigenaar beschermt
Vergeten nonces komen in plugins nog vaak voor: CSRF hoort bij de soorten lekken die regelmatig worden gepubliceerd, zoals blijkt uit de cijfers over kwetsbaarheden in WordPress-plugins. Als eigenaar zijn de reflexen eenvoudig: bijwerken, geen onbekende links openen tijdens een beheersessie, uitloggen na je werk en niet te veel beheerdersaccounts aanmaken. Noch WP Masquerade noch een instelling in het Dashboard repareert een ontbrekende nonce in een plugin: alleen een update van die plugin doet dat.
Veelgemaakte fouten
- De nonce controleren maar de rechten niet: een ingelogde abonnee die een geldige nonce ophaalt uit een pagina die hij mag zien, kan dan een actie aanroepen die voor beheerders bedoeld is. Zo ontstaan lekken van het type IDOR of gebrekkige toegangscontrole.
- Gegevens wijzigen via een gewoon GET-verzoek, dat zelfs met cookies op
SameSite=Laxdoorkomt. - Vergeten dat een XSS-lek de bescherming tenietdoet: een script dat in de pagina draait, leest de nonces die erin staan.
- CSRF verwarren met clickjacking: daarbij laat de aanvaller het slachtoffer klikken op je echte pagina, getoond in een onzichtbaar frame, en dat houdt een nonce niet tegen.