Direct naar inhoud

Broken access control

CWE-284OWASP A01:2021Bijgewerkt 31 augustus 20266 min leestijd

Broken access control is de kwetsbaarheidsklasse waarbij een gebruiker gegevens of functies bereikt die buiten zijn rechten vallen, omdat de applicatie die grens niet serverzijdig afdwingt. Horizontale escalatie geeft toegang tot gegevens van een andere gebruiker, verticale escalatie tot hogere rechten. De oplossing is standaard weigeren en bij elk endpoint expliciet controleren wie iets mag met welk object.

Vrijwel elke applicatie kent gebruikers met verschillende rechten: een klant ziet zijn eigen facturen, een medewerker ziet die van iedereen, een beheerder wijzigt rollen. Broken access control is de verzamelnaam voor alle situaties waarin die scheiding niet wordt afgedwongen op de enige plek die telt, namelijk de server. Hieronder leest u hoe die fout ontstaat, wat een aanvaller ermee bereikt en hoe u hem structureel voorkomt.

Wat is broken access control?

Broken access control, in het Nederlands gebrekkige toegangscontrole, is de kwetsbaarheidsklasse waarbij een gebruiker gegevens kan opvragen of handelingen kan uitvoeren die buiten zijn rechten vallen, omdat de applicatie die grens niet of niet op de juiste plek afdwingt. De klasse staat sinds 2021 op de eerste plaats van de OWASP Top 10 en vormt de koepel boven een aantal bekendere namen: IDOR (insecure direct object reference), privilege escalation en forced browsing zijn er alle drie verschijningsvormen van.

Vergelijk het met een hotel waarin elke sleutelkaart iedere deur op de gang opent. Bij de receptie hoort u welke kamer van u is, maar het slot zelf controleert alleen of u een kaart hebt, niet welke. Zolang iedereen zich aan de bordjes houdt, valt er niets op.

Toegangscontrole faalt in twee richtingen. Bij horizontale escalatie blijft de aanvaller op zijn eigen niveau, maar bereikt hij gegevens van een andere gebruiker met dezelfde rol: hij verhoogt het factuurnummer in de URL en krijgt de factuur van een andere klant te zien. Dat is het klassieke IDOR-geval. Bij verticale escalatie stijgt hij in rechten. Een gewone gebruiker roept een beheerdersfunctie aan, keurt zijn eigen aanvraag goed of kent zichzelf een rol toe die hij nooit had mogen krijgen.

Daarnaast bestaat forced browsing: een aanvaller vraagt rechtstreeks een pad of endpoint op dat nergens in de interface staat, bijvoorbeeld een exportfunctie onder een beheerderspad of een oude API-versie die nooit is uitgezet. Wie de knop niet ziet, kan de bijbehorende route nog steeds aanroepen. Een interface die menu-items verbergt, doet aan presentatie; dat is geen toegangscontrole.

Hoe werkt een aanval op toegangscontrole?

Neem een facturatieportaal. Klanten loggen in en zien hun eigen facturen, beheerders kunnen rollen wijzigen. De ontwikkelaar controleert netjes of iemand is ingelogd, maar niet waar die iemand recht op heeft.

Kwetsbaar:

// requireLogin controleert alleen of er een geldige sessie bestaat
app.get("/api/invoices/:id", requireLogin, async (req, res) => {
  const invoice = await db.invoices.findById(req.params.id);
  res.json(invoice);
});

// De knop staat alleen in de beheerdersinterface, dus de route lijkt veilig
app.post("/api/users/:id/role", requireLogin, async (req, res) => {
  await db.users.update(req.params.id, { role: req.body.role });
  res.sendStatus(204);
});

De aanvaller heeft een gewoon klantaccount. Hij opent zijn eigen factuur, ziet nummer 10431 in de URL staan en probeert het nummer ernaast:

GET /api/invoices/10432 HTTP/1.1
Host: portaal.example.com
Cookie: session=eyJhbGciOi

De server zoekt de factuur op, vindt hem en stuurt hem terug. Nergens is gecontroleerd of factuur 10432 bij deze gebruiker hoort. Met een script loopt de aanvaller het hele nummerbereik af en heeft hij binnen een uur de complete facturatiehistorie met namen, adressen en bedragen.

De tweede route gaat verder. Die is nergens gelinkt in de klantinterface, maar staat wel in de JavaScript-bundel die iedere bezoeker downloadt. De aanvaller stelt het verzoek zelf samen:

POST /api/users/8891/role HTTP/1.1
Host: portaal.example.com
Content-Type: application/json
Cookie: session=eyJhbGciOi

{"role":"admin"}

De applicatie stelt alleen vast dat er een sessie is en voert de wijziging uit. Dat is verticale escalatie in één verzoek.

De structurele oplossing bestaat uit twee delen. Ten eerste weigert u standaard: een route zonder expliciete autorisatiebeslissing komt de middleware niet voorbij. Ten tweede controleert u niet alleen de rol, maar ook de relatie tussen de gebruiker en het concrete object.

Veilig:

// Deny by default: elke route moet expliciet een recht claimen
app.use(requireLogin);
app.use(denyUnlessAuthorized); // weigert alles zonder authorize()

app.get("/api/invoices/:id", authorize("invoice:read"), async (req, res) => {
  const invoice = await db.invoices.findById(req.params.id);

  // Objectniveau: hoort deze factuur bij deze gebruiker?
  if (!invoice || invoice.ownerId !== req.user.id) {
    return res.sendStatus(404);
  }
  res.json(invoice);
});

// authorize leest de rechten uit de serverzijdige sessie, nooit uit het verzoek
app.post("/api/users/:id/role", authorize("user:manage"), async (req, res) => {
  await db.users.update(req.params.id, { role: req.body.role });
  res.sendStatus(204);
});

Nu levert het opvragen van factuur 10432 een 404 op en wordt de rolwijziging geweigerd voor iedereen zonder het recht user:manage. Cruciaal is waar authorize zijn informatie vandaan haalt: uit de serverzijdige sessie of een ondertekend token, nooit uit een header, een verborgen veld of een parameter met een gebruikersnummer. Alles wat de client meestuurt, kan de client veranderen.

Een autorisatiecheck die op een padprefix werkt, is fragiel. Varianten met hoofdletters, een dubbele schuine streep, een afsluitende punt of een URL-gecodeerd teken glippen langs zo’n tekstvergelijking, terwijl de achterliggende router ze wel gewoon herkent. Koppel de beslissing daarom aan de route of de resource in de applicatie zelf, en niet aan een filter op de URL in een proxy.

Wat is de impact van broken access control?

De impact loopt van hoog tot kritiek en hangt af van wat er achter de ontbrekende controle ligt. Aan de lichte kant staat het uitlezen van gegevens van andere gebruikers: persoonsgegevens, facturen, dossiers, berichten. Dat is vrijwel altijd een datalek met een meldplicht, en een oplopende reeks nummers levert de hele database op in plaats van één record.

Aan de zware kant staat verticale escalatie. Een aanvaller die zichzelf beheerder maakt, kan gegevens wijzigen of verwijderen, andere accounts overnemen, exports draaien en vaak ook functies bereiken die het systeem verder openbreken. Fraude via de bedrijfslogica hoort in dezelfde categorie: een korting toekennen of een limiet aanpassen die aan een andere rol is voorbehouden. Omdat een gewoon HTTP-verzoek volstaat, is de drempel laag en zijn de sporen in uw logboeken nauwelijks van normaal gedrag te onderscheiden.

Hoe spoort u broken access control op?

Deze klasse laat zich niet betrouwbaar automatiseren, omdat een scanner niet weet wie wat mag zien. Het onderzoek begint daarom met een rechtenmatrix: welke rollen bestaan er, wat mag elke rol, en welke endpoints horen daarbij.

De praktische werkwijze gebruikt minimaal twee accounts. U legt de verzoeken van het account met de meeste rechten vast, speelt ze opnieuw af met de sessie van het account met de minste rechten en kijkt of het antwoord verandert. Vervolgens vervangt u identificatiegegevens in URL’s, in JSON-velden en in headers door die van de andere gebruiker. Aanvullend probeert u dezelfde route met een andere HTTP-methode of vraagt u een pad op dat alleen in de JavaScript-bundel of in oude API-documentatie staat.

Automatisering helpt bij het vinden van niet-afgeschermde beheerpaden, maar de beoordeling blijft mensenwerk. AssistSec test toegangscontrole in een penetratietest met meerdere accounts per rol en toont per bevinding welk verzoek welke gegevens teruggaf.

Hoe voorkomt u broken access control?

  • Weiger standaard. Laat het framework elk verzoek afwijzen tenzij er een expliciete autorisatiebeslissing is genomen. Een nieuwe route is dan gesloten tot iemand hem bewust openzet, in plaats van andersom.
  • Autoriseer serverzijdig bij elk entreepunt. Elke route, elke API, elke achtergrondtaak en elke GraphQL-resolver. Controles in de frontend zijn gebruiksgemak, geen beveiliging.
  • Controleer ook het object, niet alleen de rol. Stel bij elk verzoek de vraag of deze gebruiker recht heeft op dit specifieke record, en bouw die eigendomscontrole in de query in plaats van erna.
  • Vertrouw geen identiteit of rol uit het verzoek. Haal de gebruiker en zijn rechten uit de serverzijdige sessie of uit een geverifieerd token, nooit uit een parameter, een cookieveld of een verborgen invoerveld.
  • Centraliseer de beslissing. Eén autorisatiecomponent die overal wordt aangeroepen, is te reviewen en te testen; controles die verspreid door de code staan, ontbreken vroeg of laat ergens.
  • Log geweigerde pogingen en test ze. Laat mislukte autorisaties een alarm opleveren bij herhaling, en neem toegangscontrole op in uw geautomatiseerde tests met een testgeval per rol.

Bronnen

Veelgestelde vragen

Wat is het verschil tussen horizontale en verticale escalatie?

Bij horizontale escalatie blijft de aanvaller op zijn eigen rechtenniveau, maar bereikt hij gegevens van een andere gebruiker met dezelfde rol, bijvoorbeeld de factuur van een andere klant. Bij verticale escalatie stijgt hij in rechten en voert hij een handeling uit die aan een hogere rol is voorbehouden, zoals het toekennen van een beheerdersrol. Beide vallen onder broken access control en beide worden op dezelfde plek voorkomen: in een serverzijdige controle per verzoek.

Is IDOR hetzelfde als broken access control?

IDOR is een specifieke verschijningsvorm, broken access control is de klasse eromheen. Bij IDOR verwijst een parameter rechtstreeks naar een object, bijvoorbeeld een nummer in de URL, en ontbreekt de controle of die gebruiker bij dat object hoort. Privilege escalation en forced browsing horen in dezelfde klasse maar volgen een andere route.

Waarom is authenticatie niet genoeg?

Authenticatie stelt vast wie iemand is, autorisatie bepaalt wat die persoon mag. Een applicatie die alleen controleert of er een geldige sessie bestaat, laat iedere ingelogde gebruiker bij alles wat de applicatie kan. In de praktijk is dat de meest voorkomende oorzaak van broken access control.

Helpt het om een endpoint niet in het menu te tonen?

Nee. Een verborgen knop of een menu-item dat alleen voor beheerders wordt gerenderd, is presentatie en geen beveiliging. Het pad staat vaak gewoon in de JavaScript-bundel, in de API-documentatie of in een oude versie van de applicatie, en een aanvaller roept de route rechtstreeks aan. Dit heet forced browsing.

Kan een scanner broken access control vinden?

Maar gedeeltelijk. Een scanner ziet dat een verzoek een antwoord met code 200 oplevert, maar weet niet welke gebruiker welk object hoort te zien; die kennis zit in uw bedrijfslogica. Geautomatiseerd testen vindt vooral open beheerpaden en ontbrekende authenticatie, terwijl handmatig testen met meerdere accounts de rest blootlegt.

Verwante artikelen

Druk op / om te zoeken · Esc