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.txtchecken bij elke webapp - Zoek naar
Disallowentries die admin- of gevoelige paden onthullen - Kijk ook naar
/sitemap.xmlvoor 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.txtbevat 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
4. Lab 3 — User Role via Cookie Parameter [Common]
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?
- Probeer
/admin→ geblokkeerd GET /+ headerX-Original-URL: /invalid→ "not found" van back-end? Header wordt verwerkt- Header
X-Original-URL: /admin→ admin panel toegankelijk
Vergelijkbare headers
X-Rewrite-URL, X-Forwarded-URL
Fix
- Front-end moet
X-Original-URLheader 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 Allowedteruggeven
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?
- Log in als admin, voer de volledige flow uit, intercept alle requests in Burp
- Log in als gewone user, kopieer de session cookie naar de stap-2-request
- 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,Originof andere client-stuurbare headers Referermag 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