Tokens spelen een belangrijke rol in API-beveiliging. Zonder goede authenticatie wil je geen API beschikbaar maken voor gebruikers, applicaties of externe systemen.
Maar in de praktijk ontstaat er vaak een misverstand.
Zodra een API werkt met JWT’s, API-keys, OAuth-tokens of sessietokens, voelt de basis veilig. Een gebruiker logt in, krijgt een token en stuurt dat token mee bij requests naar de API. De API controleert of het token geldig is en verwerkt de aanvraag.
Technisch klopt dat.
Maar daarmee is nog niet bepaald wat die gebruiker mag doen.
Veel API-kwetsbaarheden ontstaan niet doordat tokens ontbreken, maar doordat organisaties te veel vertrouwen op het bestaan van een geldig token. De API controleert dan wel of iemand is een geldig token heeft, maar niet altijd of die gebruiker toegang mag hebben tot het specifieke object, de specifieke actie of de specifieke data.
Daar zit vaak het echte risico.
Een token zegt wie iemand is, niet automatisch wat iemand mag
Een token helpt een API om vast te stellen wie een gebruiker of systeem is. Dat is authenticatie.
Maar API-beveiliging stopt daar niet.
Na authenticatie moet de API bij gevoelige acties nog steeds controleren wat die gebruiker mag doen. Dat is autorisatie.
Die controle moet veel specifieker zijn dan alleen:
“Is dit token geldig?”
De betere vraag is:
“Mag deze gebruiker met dit token deze actie uitvoeren op dit specifieke object?”
Dat verschil is cruciaal.
Een gebruiker mag misschien zijn eigen profiel bekijken, maar niet dat van een andere klant. Een medewerker mag misschien orders zien van zijn eigen afdeling, maar niet van alle afdelingen. Een partnerapplicatie mag misschien één dataset ophalen, maar niet alle interne records.
Een geldig token is dus pas het begin. De API moet daarna nog steeds bepalen waar dat token recht op geeft.
Authenticatie en autorisatie worden vaak verward
In veel API’s gaat het mis omdat authenticatie en autorisatie door elkaar lopen.
Authenticatie beantwoordt de vraag:
“Wie ben je?”
Autorisatie beantwoordt de vraag:
“Wat mag je doen?”
Een API kan technisch gezien correct authenticeren en toch kwetsbaar zijn. Dat gebeurt wanneer de API een request accepteert zodra het token geldig is, zonder daarna voldoende te controleren of de gebruiker bij de gevraagde data of functie mag.
In de interface lijkt alles dan veilig. Een gebruiker ziet alleen zijn eigen gegevens. Knoppen voor andere functionaliteiten zijn niet zichtbaar. Menu-items zijn verborgen. De applicatie voelt netjes afgeschermd.
Maar een aanvaller kijkt niet alleen naar de interface of frontend.
Die kijkt naar de API-requests daaronder. Welke endpoints worden aangeroepen? Welke ID’s worden gebruikt? Welke objecten komen terug? Wat gebeurt er als een waarde wordt aangepast?
Als de API alleen vertrouwt op de frontend en het token, ontstaat ruimte voor misbruik.
BOLA: Broken Object Level Authorization
Een van de bekendste API-risico’s is BOLA: Broken Object Level Authorization.
BOLA betekent dat een gebruiker toegang krijgt tot een object waar hij geen toegang toe hoort te hebben. Niet omdat hij niet is geauthenticeerd, maar omdat de API onvoldoende controleert of het object ook echt bij die gebruiker hoort.
Een eenvoudig voorbeeld:
/api/invoices/12345
Een gebruiker vraagt via de API zijn eigen factuur op. De API controleert of het token geldig is en geeft de factuur terug.
Maar wat gebeurt er als dezelfde gebruiker het ID aanpast?
/api/invoices/12346
Als de API opnieuw alleen controleert of het token geldig is, maar niet of factuur 12346 bij deze gebruiker hoort, ontstaat een autorisatieprobleem.
De gebruiker is geauthenticeerd.
Het token is geldig.
Het request is technisch correct.
Maar de toegang is niet toegestaan.
Dat maakt BOLA zo verraderlijk. Alles lijkt te werken zoals ontworpen, terwijl de API in werkelijkheid data vrijgeeft waar een gebruiker geen toegang toe zou mogen hebben.
Waarom BOLA zo vaak voorkomt
BOLA komt vaak voor omdat objecten in API’s nu eenmaal direct worden aangesproken.
Facturen, projecten, tickets, gebruikers, bestellingen, documenten en klantrecords hebben vaak een ID. Dat ID wordt gebruikt in routes, parameters of request bodies.
Dat is op zichzelf normaal.
Het probleem ontstaat wanneer de API ervan uitgaat dat een gebruiker alleen objecten opvraagt die via de interface beschikbaar zijn. Voor gewone gebruikers klopt dat meestal. Voor aanvallers niet.
Een aanvaller kan ID’s aanpassen, requests herhalen of endpoints direct aanroepen. Als de API de objecttoegang niet bij elke request controleert, kan een simpele wijziging al genoeg zijn om buiten de eigen rechten te komen.
Daarom is BOLA meestal geen exotische kwetsbaarheid. Het is vaak een ontbrekende controle op precies het punt waar de API toegang geeft tot data.
Objectniveau-autorisatie is lastiger dan het lijkt
Objectniveau-autorisatie klinkt eenvoudig:
Controleer of de gebruiker toegang heeft tot het object.
In de praktijk is het vaak ingewikkelder.
Want toegang hangt niet altijd alleen af van de gebruiker. Het kan ook afhangen van de organisatie, afdeling, rol, status, licentie, contractvorm, workflow of relatie tussen objecten.
Een gebruiker mag een document misschien bekijken zolang het concept is, maar niet nadat het is goedgekeurd. Een medewerker mag een order aanpassen zolang die nog niet verwerkt is. Een partner mag data ophalen voor één klantgroep, maar niet voor een andere. Een beheerder mag instellingen wijzigen binnen één tenant, maar niet platformbreed.
Bij moderne SaaS-applicaties, klantportalen en B2B-platformen lopen dit soort regels snel door elkaar.
Daarom ontstaan autorisatiefouten vaak niet in eenvoudige demo-situaties, maar bij groei. Nieuwe rollen, nieuwe endpoints, nieuwe uitzonderingen en nieuwe integraties maken de logica steeds complexer.
Als autorisatie dan niet centraal en consequent wordt afgedwongen, ontstaan verschillen tussen endpoints.
Het ene endpoint controleert correct.
Het andere endpoint doet dat net anders.
Een ouder endpoint controleert alleen of iemand is ingelogd.
Een nieuw endpoint vertrouwt op een rol, maar niet op objecteigenaarschap.
Voor een aanvaller zijn juist die inconsistenties interessant.
In het artikel over waarom API beveiliging vaak faalt gaan we breder in op hoe zulke inconsistenties tot aanvalspaden leiden.
Rollen en scopes zijn nuttig, maar niet genoeg
OAuth-scopes, rollen en claims kunnen helpen om API-toegang beter af te bakenen. Ze maken duidelijk waarvoor een token bedoeld is en welke globale rechten een gebruiker of applicatie heeft.
Maar ze lossen objectniveau-autorisatie niet automatisch op.
Een scope zoals:
invoices:read
kan betekenen dat iemand facturen mag lezen. Maar welke facturen?
Alle facturen?
Alleen facturen van de eigen organisatie?
Alleen facturen van klanten waarvoor de gebruiker verantwoordelijk is?
Alleen facturen met een bepaalde status?
Die nuance zit meestal niet volledig in het token zelf. De API moet die controle uitvoeren op basis van de context.
Hetzelfde geldt voor rollen.
Een rol zoals “admin” of “manager” lijkt duidelijk, maar in veel applicaties is de vraag: admin waarvan? Manager van welk team? Beheerder binnen welke tenant?
Zonder die context kunnen rollen te breed worden toegepast.
Daarom is een goed tokenmodel belangrijk, maar niet voldoende. De API moet per endpoint, actie en object blijven controleren of de toegang klopt.
API-keys zijn vaak te grofmazig
Naast gebruikerstokens werken veel API’s met API-keys of statische tokens. Die worden vaak gebruikt voor integraties met externe diensten, partners of interne systemen.
Dat kan praktisch zijn, maar brengt risico’s met zich mee.
Een API-key zegt meestal vooral dat een client toegang heeft tot de API. Vaak is minder duidelijk namens wie die toegang wordt gebruikt, welke gebruiker erbij hoort en welke objecten precies toegestaan zijn.
Daardoor worden API-keys in de praktijk soms te breed ingezet.
Een partner krijgt één key voor een integratie, maar die key geeft toegang tot meer endpoints dan nodig. Een interne service gebruikt een key die ooit voor testdoeleinden is aangemaakt. Een mobiele app bevat een key die bedoeld was als technische toegangspoort, maar geen fijnmazige autorisatie afdwingt.
Wanneer zo’n key uitlekt of wordt misbruikt, is de impact vaak groter dan verwacht.
Het probleem is dan niet alleen dat de key bestaat, maar dat de autorisatie achter die key te ruim is ingericht.
De frontend is geen autorisatielaag
Een veelgemaakte fout is vertrouwen op de frontend.
De applicatie toont alleen toegestane knoppen. De gebruiker kan alleen zijn eigen records selecteren. Bepaalde velden zijn uitgeschakeld. Sommige pagina’s zijn niet bereikbaar via het menu.
Voor normale gebruikers werkt dat.
Maar de API moet niet vertrouwen op wat de frontend wel of niet laat zien.
Een aanvaller kan requests direct aanpassen. Een verborgen knop betekent niets als het achterliggende endpoint gewoon bereikbaar is. Een uitgeschakeld veld zegt weinig als de API de meegestuurde waarde alsnog verwerkt. Een filter in de frontend helpt niet als de API zonder server-side controle meer data teruggeeft.
De frontend bepaalt wat prettig en logisch is voor de gebruiker.
De API moet bepalen wat toegestaan is.
Als die scheiding ontbreekt, ontstaan autorisatierisico’s die vaak pas zichtbaar worden wanneer iemand de requests handmatig onderzoekt.
Waarom vulnerability scans dit meestal missen
Autorisatieproblemen zoals BOLA zijn lastig te vinden met een standaard vulnerability scan.
Een vulnerability scan kan bekende kwetsbaarheden herkennen. Verouderde software, ontbrekende headers, zwakke TLS-instellingen of bepaalde configuratiefouten kunnen goed zichtbaar zijn.
Maar een vulnerability scan begrijpt meestal niet welke gebruiker toegang hoort te hebben tot welk object.
Dat is precies het probleem bij API-autorisatie.
Een endpoint kan netjes reageren. Het token is geldig. De HTTP-status is 200. Er is geen technische foutmelding. Toch kan de response data bevatten die niet voor deze gebruiker bedoeld is.
Om dat te beoordelen moet je context begrijpen.
Welke gebruiker doet het request?
Van wie is het object?
Welke rol hoort toegang te hebben?
Welke tenant, organisatie of klant hoort hierbij?
Wat gebeurt er als hetzelfde request met een ander account wordt uitgevoerd?
Dat zijn vragen die verder gaan dan automatische detectie.
Daarom komen API-autorisatieproblemen vaak pas naar voren tijdens handmatig onderzoek.
In het artikel over het verschil tussen een vulnerability scan en een pentest leggen we breder uit waarom geautomatiseerde controles en handmatig onderzoek een andere rol hebben
Praktijkvoorbeeld: geldig token, verkeerde toegang
Stel dat een SaaS-applicatie projectgegevens ophaalt via dit endpoint:
/api/projects/{projectId}
Een gebruiker logt in, krijgt een geldig JWT-token en opent zijn eigen project. De API controleert het token en geeft de projectgegevens terug.
Tot zover lijkt alles correct.
Maar vervolgens past de gebruiker het project-ID aan naar een ander nummer. De API controleert opnieuw het token, ziet dat de gebruiker is geauthenticeerd en geeft ook dat project terug.
Wat ontbreekt, is de belangrijkste controle:
Hoort dit project bij deze gebruiker of organisatie?
Als die controle ontbreekt, kan een gewone gebruiker mogelijk projecten van andere klanten bekijken. Afhankelijk van de data kan dat leiden tot toegang tot klantgegevens, interne notities, documenten, financiële informatie of andere gevoelige data.
De kwetsbaarheid zit dan niet in het JWT-token zelf.
Het token werkt precies zoals bedoeld.
De fout zit in de autorisatie rond het object.
Waarom dit bij API’s extra impact kan hebben
Autorisatiefouten in API’s kunnen snel grote impact hebben, omdat API’s goed te automatiseren zijn. Meer hierover lees je in het artikel over waarom API’s een geliefd doelwit zijn voor aanvallers.
Als één object-ID werkt, kan een aanvaller meer ID’s proberen. Als één endpoint te veel data teruggeeft, kan dezelfde request worden herhaald. Als één rol te breed is ingericht, kan die rol op meerdere plekken misbruikt worden.
Dat maakt API-autorisatie anders dan veel zichtbare frontendproblemen.
Een fout die bij handmatig gebruik klein lijkt, kan via scripts of geautomatiseerde requests snel opschalen.
Daarom is het belangrijk om niet alleen te checken of een individuele request onterecht werkt, maar ook wat er gebeurt als die kwetsbaarheid op grotere schaal wordt misbruikt.
Kan iemand tientallen records ophalen?
Honderden?
Alle klanten binnen een tenant?
Data uit meerdere organisaties?
Die schaalbaarheid maakt API-autorisatie zo belangrijk.
Wat een API pentest zichtbaar maakt
Tijdens een API pentest wordt niet alleen onderzocht of authenticatie goed is ingeregeld. Er wordt ook gekeken in hoeverre autorisatie consequent werkt.
Daarbij kijkt de tester naar verschillende gebruikersrollen, objecten, endpoints en acties. Requests worden vergeleken tussen accounts. Object-ID’s worden aangepast. Rollen worden naast elkaar getest. Endpoints worden direct aangeroepen, ook als ze niet zichtbaar zijn in de interface.
Het doel is niet om het tokenmechanisme los te beoordelen, maar om te begrijpen wat iemand met een geldig token kan doen.
Dat is het realistische scenario.
Veel aanvallers beginnen namelijk niet zonder toegang. Ze beginnen met een normaal account, een gelekt token, een partnerintegratie of een beperkte API-key. De vraag is dan hoe ver zij vanaf dat punt kunnen komen.
Een goede API pentest maakt zichtbaar waar authenticatie stopt en waar autorisatie tekortschiet.
In het artikel over wat er tijdens een pentest gebeurt lees je hoe zo’n gecontroleerd onderzoek in de praktijk verloopt.
Hoe API-autorisatie sterker wordt
Sterke API-autorisatie vraagt om consequente controles op meerdere niveaus.
De API moet niet alleen controleren of een token geldig is, maar ook of de gebruiker toegang heeft tot de specifieke functionaliteit en het specifieke object.
Dat betekent onder andere:
- controle op objectniveau bij elke gevoelige request;
- duidelijke scheiding tussen authenticatie en autorisatie;
- server-side controles in plaats van vertrouwen op de frontend;
- rollen en scopes die niet ruimer zijn dan nodig;
- tenant-, organisatie- en eigenaarschapscontroles waar dat relevant is;
- consistente autorisatieregels over alle endpoints en services;
- logging van verdachte toegangspogingen of afwijkend API-gebruik.
Vooral consistentie is belangrijk.
Een API is zo sterk als de zwakste endpoint waar autorisatie ontbreekt.
API-beveiliging draait om wat iemand mag doen
Tokens blijven hoe dan ook belangrijk. Zonder goede authenticatie is API-beveiliging niet serieus te nemen.
Maar tokens zijn niet het eindpunt.
Een API moet na authenticatie bij elke relevante request opnieuw bepalen wat de gebruiker mag doen. Vooral bij objecten, rollen, tenants, acties en gevoelige data.
Daar gaat het in de praktijk vaak mis.
Niet omdat teams geen security toepassen, maar omdat autorisatie complex wordt zodra applicaties groeien. Nieuwe endpoints, nieuwe rollen, nieuwe integraties en nieuwe uitzonderingen maken het steeds moeilijker om overal dezelfde controles af te dwingen.
Wil je weten of jouw API niet alleen controleert of een gebruiker geauthenticeerd is, maar ook consequent nagaat welke handelingen die gebruiker mag uitvoeren? Dan kan een gerichte API pentest helpen om autorisatierisico’s rond endpoints, objecten en rollen concreet in kaart te brengen.