Skip to content

Broken Access Control

Bron: PortSwigger Web Security Academy Auteur: Johan Beysen | Fox & Fish Cybersecurity


1. Overzicht — Alle Labs

Lab Frequentie Techniek
1 — Unprotected admin (robots.txt) Common Informatielekken via robots.txt
2 — Unprotected admin (unpredictable URL) Common Hardcoded pad in JS broncode
3 — Role via cookie parameter Common Client-side role enforcement
4 — Role via JSON body Common Mass assignment / parameter tampering
5 — X-Original-URL bypass Zeldzaam Header-based URL override
6 — Method-based bypass Common HTTP methode als enige check
7 — IDOR met voorspelbaar ID Zeer common Simpele parameter swap
8 — IDOR met onvoorspelbaar ID Zeer common GUID lekken via andere pagina
9 — IDOR met data leakage in redirect Common 302 body niet leeggemaakt
10 — Password disclosure via IDOR Common Gevoelige data zichtbaar voor andere user
11 — Insecure direct object references Zeer common Incrementeel bestandsnummer
12 — Multi-step zonder check op stap 2 Common Partial access control
13 — Referer-based access control Common Client-stuurbare header als auth

2. Lab 1 — Unprotected Admin Functionality [Common]

Wat gebeurt er?

Het admin panel is volledig onbeveiligd — iedereen die de URL kent kan erin. De URL wordt gelekt via robots.txt:

GET /robots.txt
User-agent: *
Disallow: /administrator-panel  ← pad gelekt!

Ironisch

robots.txt is publiek toegankelijk. Door een pad te "verbergen" voor zoekmachines maak je het juist zichtbaar voor aanvallers.

Hoe Herkennen?

  • Altijd /robots.txt checken bij elke webapp
  • Zoek naar Disallow entries die admin- of gevoelige paden onthullen
  • Kijk ook naar /sitemap.xml voor vergelijkbare lekken

Hoe Testen?

curl https://target.com/robots.txt

Probeer elk Disallow pad direct. Geen authenticatie? Dan is het raak.

Fix

  • Authenticatie en autorisatie op de admin route zelf afdwingen
  • robots.txt bevat GEEN security — gebruik het niet om paden te "verbergen"
  • Admin panel achter VPN of IP-whitelist plaatsen

3. Lab 2 — Unprotected Admin Functionality (Unpredictable URL) [Common]

Wat gebeurt er?

Het admin panel heeft een onvoorspelbaar pad, maar dat pad staat hardcoded in de JavaScript broncode:

var isAdmin = false;
if (isAdmin) {
    var adminPanelTag = document.createElement('a');
    adminPanelTag.setAttribute('href', '/admin-7hf93k');  // ← pad gelekt!
}

Zelfs als de link niet zichtbaar is in de UI (isAdmin = false), staat het pad gewoon in de broncode.

Hoe Herkennen?

  • View source (Ctrl+U) of Developer Tools → Sources tab
  • Zoek op: admin, panel, dashboard, hidden, secret
  • Burp Spider of Crawl pikt dit automatisch op

Fix

  • Nooit gevoelige paden of logica in client-side JS zetten
  • Admin functionaliteit server-side beveiligen, niet "verbergen"
  • JS minification/obfuscation is GEEN beveiliging

Wat gebeurt er?

Na inloggen zet de server een cookie Admin=false. De applicatie vertrouwt blind op die waarde.

Set-Cookie: Admin=false  ← door server gestuurd
→ Verander naar: Admin=true
→ Toegang tot admin panel verkregen

Kernprobleem

Access control mag NOOIT gebaseerd zijn op client-stuurbare waarden. De client kan alles sturen wat hij wil — cookies, headers, body parameters.

Hoe Herkennen?

  • Bekijk cookies na inloggen (DevTools → Application → Cookies)
  • Zoek naar: role=, admin=, isAdmin=, userType=, level=
  • Verdachte boolean of numerieke waarden in cookies

Fix

  • Rol/rechten ALLEEN server-side opslaan (in sessie op de server, niet in cookie)
  • Session ID in cookie, rechten ophalen uit database op basis van die sessie
  • Nooit gebruikersrol in een client-stuurbare waarde bewaren

5. Lab 4 — Mass Assignment via JSON Body [Common]

Wat gebeurt er?

Bij het updaten van het e-mailadres accepteert de server extra JSON-velden die hij niet verwacht:

// Normale request:
{"email": "wiener@normal.com"}

// Aanvalsrequest:
{"email": "wiener@normal.com", "roleid": 2}

// Response:
{"username": "wiener", "email": "...", "roleid": 2}   gelukt!

De server mapped de JSON input direct naar een object/model zonder whitelist van toegestane velden. Dit heet mass assignment of auto-binding.

Hoe Herkennen?

  • Bekijk API responses: bevatten ze meer velden dan verwacht? (roleid, isAdmin, verified...)
  • Die extra velden in de response → probeer ze ook te sturen in requests
  • Zoek naar PUT/PATCH/POST endpoints die objecten updaten

Fix

  • Explicit whitelist van toegestane velden bij model binding
  • Laravel: $fillable | Rails: strong parameters | Spring: @JsonIgnore
  • Nooit de volledige request body blindlings mappen naar een database object

6. Lab 5 — X-Original-URL Bypass [Zeldzaam]

Wat gebeurt er?

De front-end proxy blokkeert /admin op basis van de URL. Maar de back-end ondersteunt de X-Original-URL header:

GET / HTTP/1.1
X-Original-URL: /admin

Front-end ziet / → geen blokkering. Back-end ziet header → serveert /admin.

Voor delete-actie:

GET /?username=carlos HTTP/1.1
X-Original-URL: /admin/delete

Hoe Testen?

  1. Probeer /admin → geblokkeerd
  2. GET / + header X-Original-URL: /invalid → "not found" van back-end? Header wordt verwerkt
  3. Header X-Original-URL: /admin → admin panel toegankelijk

Vergelijkbare headers

X-Rewrite-URL, X-Forwarded-URL

Fix

  • Front-end moet X-Original-URL header strippen uit inkomende externe requests
  • Back-end access control implementeren, niet enkel op proxy

7. Lab 6 — Method-Based Access Control Bypass [Common]

Wat gebeurt er?

De access control check zit enkel op POST-requests:

POST /admin/promote  → 403 (check aanwezig)
POSTX /admin/promote → "missing param" (check geskipt, route bestaat)
GET /admin/promote?username=wiener → 200 OK (actie uitgevoerd!)

HTTP methodes zijn semantische labels — geen technische beveiliging. Als de check enkel op POST zit, is elke andere methode een blinde vlek.

Hoe Testen?

# Burp Repeater: verander methode handmatig of via rechtermuisklik
# Tool: ffuf
ffuf -u https://target.com/admin/promote -X FUZZ -w methods.txt

Burp tip

"Change request method" in Burp Repeater verplaatst automatisch POST body-parameters naar de query string bij conversie naar GET.

Fix

  • Access control binden aan de route, niet aan de methode
  • Onbekende methodes automatisch 405 Method Not Allowed teruggeven

8. Lab 7 — IDOR Voorspelbaar ID [Zeer Common]

Wat is IDOR?

Insecure Direct Object Reference (IDOR): de applicatie gebruikt een gebruiker-gecontroleerde waarde om direct een object op te halen, zonder te controleren of die gebruiker daar toegang toe heeft.

Wat gebeurt er?

GET /my-account?id=wiener  ← jouw account
GET /my-account?id=carlos  ← carlos zijn account + API key

Hoe Testen?

  • Maak 2 accounts aan
  • Log in als account A, pas id-parameter aan naar ID van account B
  • Burp Intruder: brute force numerieke IDs

Fix

  • Controleer server-side of de ingelogde gebruiker eigenaar is van het gevraagde object
  • Gebruik indirect references (mapping table) in plaats van directe DB-IDs

9. Lab 8 — IDOR met GUIDs [Zeer Common]

Wat gebeurt er?

De applicatie gebruikt GUIDs als "extra beveiliging" — maar die lekken via blogposts:

Blog post URL: /blogs?userId=fe89a329-d98f-4a26-b...  ← carlos zijn GUID
Account URL:   /my-account?id=fe89a329-d98f-4a26-b...  ← zelfde GUID!

Kernprincipe

Een onvoorspelbaar ID is GEEN access control. Het is security through obscurity. Als het ID ergens anders lekt, is de "beveiliging" volledig nutteloos.

Hoe Herkennen?

  • GUIDs of lange hashes als user identifier
  • Zoek of die IDs ergens anders in de applicatie voorkomen: blog auteurs, forum posts, reacties

Fix

  • GUID ≠ access control: autorisatiecheck blijft verplicht
  • Nooit user-specifieke IDs lekken in publiek toegankelijke content

10. Lab 9 — IDOR Data Leakage in Redirect [Common]

Wat gebeurt er?

De server stuurt een 302 redirect als je een ID opvraagt dat niet van jou is. Maar de response body bevat de gevoelige data al — vóór de redirect uitgevoerd wordt.

HTTP/1.1 302 Found
Location: /home

<body>            ← browser negeert dit, Burp wel!
API key: abc123xyz...
</body>

Browser vs Burp

Tool Gedrag bij 302 redirect
Browser Negeert body, volgt redirect, jij ziet nooit de data
Burp Repeater Toont volledige raw response incl. body

Fix

  • Response body leegmaken vóór de redirect verstuurd wordt
  • Of beter: data pas ophalen na succesvolle autorisatiecheck

11. Lab 10 — Password Disclosure via IDOR [Common]

Wat gebeurt er?

De profielpagina toont het wachtwoord in een invoerveld:

GET /my-account?id=wiener        ← eigen pagina, eigen wachtwoord zichtbaar
GET /my-account?id=administrator ← admin pagina, admin wachtwoord zichtbaar!

Impact

Naast data-inzage leidt dit direct tot account takeover van het administrator account. Impact: maximaal.

Fix

  • Wachtwoorden NOOIT in responses sturen (ook niet masked)
  • Autorisatiecheck: controleer of de sessiegebruiker = eigenaar van het gevraagde account
  • Wachtwoorden alleen gehasht opslaan

12. Lab 11 — IDOR Bestanden [Zeer Common]

Wat gebeurt er?

Chatgeschiedenissen worden opgeslagen met een incrementeel bestandsnummer:

GET /download-transcript/3.txt  ← jouw bestand
GET /download-transcript/1.txt  ← bestand van iemand anders → bevat wachtwoord!

Hoe Testen?

ffuf -u https://target.com/download-transcript/FUZZ.txt -w numbers.txt

Fix

  • Bestanden opslaan met niet-voorspelbare namen (UUID)
  • Server-side controleren of de gebruiker eigenaar is van het bestand

13. Lab 12 — Multi-Step Zonder Check op Stap 2 [Common]

Wat gebeurt er?

Access control enkel op stap 1, niet op de bevestigingsstap:

Stap 1: POST /admin/promote          → 403 (check aanwezig)
Stap 2: POST /admin/promote/confirm  → 200 OK! (geen check)

Kernfout

Developers gaan ervan uit dat stap 2 enkel bereikbaar is via stap 1. Maar HTTP heeft geen "state" — elke request is onafhankelijk.

Hoe Testen?

  1. Log in als admin, voer de volledige flow uit, intercept alle requests in Burp
  2. Log in als gewone user, kopieer de session cookie naar de stap-2-request
  3. Stuur stap 2 direct, zonder stap 1 te doen

Fix

  • Access control op elke stap van een multi-step flow
  • Server-side state bijhouden: controleer of stap 1 succesvol doorlopen is

14. Lab 13 — Referer-Based Access Control [Common]

Wat gebeurt er?

De server controleert of de Referer header verwijst naar /admin:

GET /admin-roles?username=wiener&action=upgrade HTTP/1.1
Referer: https://vulnerable-site.com/admin  ← zelf toegevoegd
Cookie: session=<non-admin-session>
 200 OK, actie uitgevoerd!

De Referer header is volledig client-stuurbaar — een aanvaller kan hem vrij instellen.

Vergelijking Client-Stuurbare Headers

Header Bedoeling
Referer Aanduiden van vorige pagina
X-Original-URL URL override voor proxy
X-Forwarded-For Origineel IP bij proxy
Cookie (role=admin) Sessie-info

Fix

  • Nooit access control baseren op Referer, Origin of andere client-stuurbare headers
  • Referer mag gebruikt worden als extra logging/monitoring, NIET als beveiliging

15. Patronen & Gouden Regels

De Drie Rode Draden

1. Beveiliging gebaseerd op client-stuurbare waarden

Cookies, headers, query parameters, JSON body — de client controleert dit allemaal. Vertrouw het nooit voor access control beslissingen.

2. Access control in één laag, omzeilbaar via een andere vector

Front-end blokkeert? Probeer de back-end direct. Check enkel op POST? Probeer GET. Enkel stap 1 beveiligd? Stuur stap 2 rechtstreeks.

3. Gevoelige data lekt via onverwachte kanalen

Redirect bodies, JS broncode, robots.txt, blog auteur-URLs, API responses — data lekt overal als developers er niet actief op letten.

Frequentiesamenvatting

Lab Frequentie Kernles
1 — robots.txt leak Common robots.txt is geen beveiliging
2 — JS broncode leak Common Client-side JS verbergt niets
3 — Cookie role param Common Nooit rechten in client-stuurbare cookies
4 — Mass assignment Common Whitelist je model binding
5 — X-Original-URL Zeldzaam Headers zijn client-stuurbaar
6 — Method bypass Common Check de route, niet de methode
7 — IDOR simpel ID Zeer common Altijd autorisatiecheck op object
8 — IDOR GUID Zeer common GUID ≠ access control
9 — IDOR redirect leak Common Body leegmaken voor redirect
10 — Password via IDOR Common Wachtwoorden niet in responses
11 — IDOR bestanden Zeer common Bestanden niet direct ophaalbaar
12 — Multi-step Common Elke stap afzonderlijk beveiligen
13 — Referer check Common Referer is client-stuurbaar

Gouden Regels

Gouden regels voor secure access control

  • Vertrouw NOOIT client-gestuurde input voor autorisatiebeslissingen
  • Implementeer access control op de back-end, op elke route, elke methode, elke stap
  • Controleer altijd: is de ingelogde gebruiker eigenaar van het gevraagde object?
  • Gevoelige data pas ophalen NA succesvolle autorisatiecheck
  • Als pentester: test altijd alle methodes, alle parameters, alle stappen van een flow
  • Gebruik Burp Repeater om raw responses te bekijken, ook bij redirects

Fox & Fish Cybersecurity | Intern gebruik