De Engelse naam, Insecure Direct Object Reference, betekent zoiets als "onveilige directe verwijzing naar een object". Het "object" kan een bestelling, een factuur, een profiel, een bestand of een bericht zijn; de "verwijzing" is het ID dat het aanduidt in de URL, een formulier of een API-verzoek. Volgens OWASP komt het lek voort uit een ontbrekende toegangscontrole: de applicatie controleert wel dat de gebruiker is ingelogd, maar niet dat hij dit specifieke object mag bekijken.
Hoe een IDOR-lek werkt
Voor de aanval is geen speciaal gereedschap nodig. Een ingelogde klant bekijkt zijn factuur op het adres /mijn-account/factuur/?id=1057. Hij vervangt 1057 door 1042 en de site toont de factuur van een andere klant, met naam en adres. Hetzelfde scenario zie je in een API-route (/wp-json/winkel/v1/bestellingen/1042), het downloaden van een bijlage of een verwijderactie.

Het is de meest voorkomende vorm van horizontale privilege-escalatie: de aanvaller krijgt geen hogere rang, maar komt bij de gegevens van een gebruiker op hetzelfde niveau. MITRE deelt het in onder CWE-639 ("authorization bypass through user-controlled key") en OWASP API Security spreekt van BOLA (Broken Object Level Authorization). In de OWASP Top 10 valt het onder categorie A01, gebrekkige toegangscontrole, die het bekijken of wijzigen van andermans account via zijn ID expliciet noemt.
Hoe je je ertegen beschermt
- Elk object controleren: vergelijk bij elk verzoek aan de serverkant de eigenaar van het gevraagde object met de ingelogde gebruiker.
- Alleen zoeken in wat van de gebruiker is: een query als "de bestellingen van de huidige gebruiker met ID X" geeft nooit die van een ander terug.
- Het ID niet tonen als dat niet nodig is: bij "mijn profiel" volgt de gebruiker uit de sessie.
- Niet vertrouwen op willekeurige ID's: ze maken raden moeilijker, maar OWASP benadrukt dat ze de toegangscontrole niet vervangen.
Voor WordPress
WordPress heeft "meta"-capabilities die het recht op een specifiek object controleren. current_user_can( 'edit_post', $post_id ) vraagt niet alleen "mag deze gebruiker berichten bewerken?", maar "mag hij dit bericht bewerken?", rekening houdend met de auteur en de status:
$post_id = absint( $_POST['post_id'] ?? 0 );
if ( ! current_user_can( 'edit_post', $post_id ) ) {
wp_send_json_error( null, 403 );
}
Voor gegevens van een plugin zelf (bestellingen, reserveringen, privébestanden) moet je die controle zelf schrijven, bijvoorbeeld door de opgeslagen auteur te vergelijken met get_current_user_id(). In de REST API hoort die controle in de permission_callback van de route. Twee verwarringen komen vaak terug: een geldige nonce bewijst dat het verzoek echt van de site komt en beschermt tegen vervalste verzoeken, niet dat de gebruiker toegang tot het object heeft; en een link verbergen in de interface verhindert niet dat iemand de URL rechtstreeks aanroept.
Om een plugin te testen, maak je twee accounts met dezelfde rol aan en probeer je met het ene de objecten van het andere te openen. Lekken in de toegangscontrole horen bij de meest voorkomende in plugins, zoals de cijfers over kwetsbare plugins laten zien; ons artikel over de REST API helpt je te zien wat er op je site openbaar is, en dat over rollen en capabilities om te beperken wat elk account kan bereiken.