Broken Access Control ist seit Jahren die Nummer 1 der OWASP Top 10, in der Edition 2021 ebenso wie in der Edition 2025. Die Schwachstelle klingt harmlos, hat aber gewaltige Auswirkungen: Ein Angreifer ändert eine einzige Zahl in einer URL und liest plötzlich fremde Kontoauszüge, Bestelldaten oder Krankenakten. Kein Exploit-Framework, kein Buffer Overflow, nur ein manipulierter Parameter. Genau diese Einfachheit macht fehlerhafte Zugriffskontrolle so gefährlich und so weit verbreitet.
Insecure Direct Object Reference, kurz IDOR, ist der bekannteste Unterfall davon. Dieser Artikel ordnet beide Begriffe sauber ein, erklärt den Unterschied zwischen Authentifizierung und Autorisierung, zeigt die formale CWE-Taxonomie (CWE-284, CWE-862, CWE-863, CWE-639) im Zusammenspiel mit den praktischen OWASP- und PortSwigger-Kategorien und liefert verwundbare wie sichere Codebeispiele. Ein eigener Abschnitt widmet sich JWT- und Session-bezogenen Zugriffskontrollfehlern, die in den meisten Erklärungen fehlen, obwohl sie in der Praxis besonders häufig übersehen werden.
Der Beitrag setzt die OWASP-Reihe des Blogs fort und ergänzt bestehende Grundlagenartikel zu SQL-Injection, Directory Traversal und den OWASP Top 10. Er richtet sich an Entwickler, Pentester und Security-Verantwortliche, die verstehen wollen, warum Zugriffskontrolle so oft bricht und wie man sie strukturell richtig baut.
Was ist Broken Access Control?
Zugriffskontrolle (Access Control) ist der Mechanismus, der durchsetzt, dass Nutzer nur das tun können, was ihnen erlaubt ist. OWASP formuliert es knapp: "Access control enforces policy such that users cannot act outside of their intended permissions." Bricht dieser Mechanismus, spricht man von Broken Access Control. Die typischen Folgen: unautorisierte Offenlegung von Informationen, unautorisierte Änderung oder Löschung von Daten oder die Ausführung einer Geschäftsfunktion außerhalb der Grenzen des Nutzers.
Entscheidend ist ein Prinzip, das in der Praxis erstaunlich oft verletzt wird: Zugriffskontrolle wirkt nur, wenn sie in vertrauenswürdigem, serverseitigem Code durchgesetzt wird, wo der Angreifer die Prüfung oder ihre Metadaten nicht selbst manipulieren kann. Alles, was im Browser, im Frontend-JavaScript oder in einem versteckten Formularfeld "geprüft" wird, ist wertlos, denn genau diese Ebene kontrolliert der Angreifer vollständig.
Warum Zugriffskontrolle so oft bricht
Broken Access Control ist selten das Ergebnis eines einzelnen groben Fehlers. Sie entsteht meist aus Struktur- und Wachstumsproblemen:
- Unstrukturiertes Wachstum: Anwendungen, deren Umfang über viele Versionen gewachsen ist, hatten selten von Anfang an ein durchdachtes Zugriffskontrollschema. Berechtigungen wurden nachträglich und uneinheitlich ergänzt.
- Dezentrale Autorisierungslogik: Wenn die Prüfung "darf dieser Nutzer das?" über die ganze Codebasis verstreut ist statt zentral an einer Stelle, wird sie unübersichtlich und lückenhaft. Jeder neue Endpunkt ist eine neue Gelegenheit, die Prüfung zu vergessen.
- System-Evolution: Neue Funktionen nutzen bestehende Datenmodelle wieder, Autorisierungslogik wird an einer Stelle eingefügt, aber nicht überall konsequent. Refactoring kann frühere Ownership-Checks unbeabsichtigt umgehen.
- Besonders kritisch: administrative Remote-Interfaces, die mächtige Funktionen bereitstellen und deshalb besonders sorgfältig abgesichert sein müssen.
Authentifizierung ist nicht Autorisierung
Der wichtigste Denkfehler hinter fast jeder Zugriffskontroll-Schwachstelle lautet: "Der Nutzer ist eingeloggt, also darf er das." Das ist falsch. Zwei getrennte Fragen müssen beantwortet werden:
- Authentifizierung (AuthN): "Wer bist du?" Der Server verifiziert die Identität, etwa über Passwort, Multi-Faktor-Authentifizierung, Session-Cookie oder Token.
- Autorisierung (AuthZ): "Darfst du genau das?" Der Server verifiziert, ob diese authentifizierte Identität berechtigt ist, genau diese Ressource zu sehen oder genau diese Aktion auszuführen.
IDOR und Broken Access Control sind im Kern immer AuthZ-Fehler: Der Server prüft, ob jemand eingeloggt ist, versäumt aber die Prüfung, ob dieser Jemand auch für die konkret angeforderte Ressource berechtigt ist. Ein eingeloggter Nutzer bleibt eben nur für seine eigenen Daten autorisiert, nicht automatisch für die aller anderen.
Die Begriffshierarchie: BAC, IDOR und die CWE-Taxonomie
In der Praxis werden "Broken Access Control", "IDOR", "BOLA" und diverse CWE-Nummern oft durcheinandergeworfen. Eine saubere Hierarchie hilft:
- Broken Access Control (OWASP A01) ist der Oberbegriff für alle Arten fehlerhafter Zugriffskontrolle.
- CWE-284 (Improper Access Control) ist die formale, abstrakte "Pillar"-Kategorie ganz oben in der CWE-Hierarchie. MITRE rät ausdrücklich davon ab, CWE-284 direkt für konkretes Mapping zu verwenden, weil sie zu allgemein ist. Man soll spezifischere Nachfahren nutzen.
- CWE-862 (Missing Authorization) beschreibt den Fall, dass gar keine Autorisierungsprüfung stattfindet.
- CWE-863 (Incorrect Authorization) beschreibt den Fall, dass eine Prüfung existiert, aber fehlerhaft ist und umgangen werden kann.
- CWE-639 (Authorization Bypass Through User-Controlled Key) ist die formale Bezeichnung für das, was in der Praxis meist "IDOR" heißt: Ein Nutzer greift auf fremde Daten zu, indem er einen kontrollierbaren Schlüsselwert (Key) verändert, ohne dass geprüft wird, ob er auf genau diesen Datensatz zugreifen darf.
Der Unterschied zwischen CWE-862 und CWE-863 ist wichtiger, als er wirkt. "Keine Prüfung vorhanden" und "fehlerhafte Prüfung vorhanden" erfordern unterschiedliche Fixes: Im ersten Fall muss eine Prüfung überhaupt erst eingebaut werden, im zweiten Fall muss die vorhandene Logik korrigiert werden. In der OWASP-API-Security-Terminologie taucht IDOR übrigens als Broken Object Level Authorization (BOLA) auf, das ist derselbe Sachverhalt, nur auf API-Objektebene benannt.
Horizontale, vertikale und kontextabhängige Zugriffskontrolle
Die PortSwigger Web Security Academy unterscheidet drei Grundtypen von Zugriffskontrolle. Diese Klassifikation ist der beste Rahmen, um Angriffe einzuordnen.
Vertikale Zugriffskontrolle
Sie beschränkt sensible Funktionen auf bestimmte Nutzertypen. Ein Admin darf Konten löschen, ein normaler Nutzer nicht. Bricht die vertikale Kontrolle, spricht man von vertikaler Rechteausweitung (Vertical Privilege Escalation): Ein normaler Nutzer erlangt Admin-Funktionen. Typische Ursachen sind ungeschützte Admin-Seiten, in robots.txt oder Frontend-JavaScript offengelegte Pfade oder parameterbasierte Rollen, die clientseitig gespeichert werden:
https://insecure-website.com/login/home.jsp?admin=true
https://insecure-website.com/login/home.jsp?role=1
Ändert der Nutzer admin=false zu admin=true, verschafft er sich unautorisierten Zugriff, weil die Rolle nicht serverseitig verifiziert wird.
Horizontale Zugriffskontrolle
Sie beschränkt den Ressourcenzugriff auf bestimmte Nutzer. Eine Banking-App zeigt nur die eigenen Transaktionen. Bricht die horizontale Kontrolle, spricht man von horizontaler Rechteausweitung: Ein Nutzer greift auf die Ressourcen eines gleichrangigen anderen Nutzers zu. Genau das ist die klassische IDOR:
https://insecure-website.com/myaccount?id=123
Der Angreifer ändert id=123 zu id=124 und sieht ein fremdes Konto. IDOR ist per Definition meist ein horizontaler Angriff, kann aber vertikal eskalieren: Übernimmt man über eine horizontale Schwachstelle das Konto eines privilegierten Nutzers, wird daraus vollständiger Admin-Zugriff (Horizontal-to-Vertical Escalation).
Kontextabhängige Zugriffskontrolle
Sie verhindert, dass Aktionen in der falschen Reihenfolge ausgeführt werden, etwa den Inhalt eines Warenkorbs nach Abschluss der Zahlung noch zu ändern. Werden Zugriffskontrollen über mehrere Prozessschritte hinweg inkonsistent angewendet, können Angreifer kontrollierte Zwischenschritte überspringen und direkt die finale Anfrage senden.
IDOR im Detail: die drei notwendigen Elemente
Das offizielle OWASP Cheat Sheet definiert IDOR sehr präzise über drei notwendige Bestandteile. Erst ihr Zusammentreffen ergibt die Schwachstelle:
- Ein Objekt, das existiert (ein Konto, ein Dokument, eine Bestellung).
- Eine Referenz darauf, die der Nutzer kontrollieren kann (eine ID, UUID oder ein Slug in URL, Formularfeld, Header oder Cookie).
- Eine fehlende objektbezogene Autorisierungsprüfung (missing object-level authorization check).
Fehlt das dritte Element, liegt IDOR vor. Die zentrale Erkenntnis von Aikido, einer der Top-Quellen zum Thema, bringt es auf den Punkt: "Die Vulnerabilität ist nicht der Identifikator selbst. Die Vulnerabilität ist die Annahme, dass der Identifikator dem authentifizierten Nutzer gehört, ohne diese Annahme auf dem Server zu revalidieren."
Wo angreifbare Identifikatoren stecken
Angreifbare Referenzen finden sich fast überall, wo Anwendungen Nutzereingaben zur Objektauswahl verwenden:
- URL-Pfad oder Query-Parameter:
/user/id/1234,?customer_number=132355,?id=16. - Request-Body-Felder: JSON- oder Formularfelder wie
user_idin einem POST/PUT-Request. - Versteckte Formularfelder:
<input type="hidden" name="user_id" value="12345">, im Browser trivial änderbar. - HTTP-Header: eigene Header wie
X-User-ID. - Cookies: ID-Werte in Cookies, die den Nutzerkontext bestimmen.
- Dateinamen:
/static/pdfs/1.pdf,/documents/annual-report.pdf, oft sequenziell und damit erratbar. - API-Response-IDs, die in nachfolgenden Requests wiederverwendet werden.
Die IDOR-Varianten
Über die Quellen hinweg lassen sich mehrere Ausprägungen unterscheiden:
- Horizontale IDOR: Zugriff auf Objekte eines gleichrangigen Nutzers.
- Vertikale IDOR: Zugriff auf privilegierte oder administrative Objekte.
- Blind IDOR: Die Serverantwort zeigt keine Daten, aber die Aktion wirkt auf ein fremdes Objekt (etwa eine unautorisierte Änderung ohne sichtbares Ergebnis). Solche Fälle sind besonders leicht zu übersehen, weil kein offensichtliches Datenleck sichtbar wird.
- Numerisch vs. UUID: Sequenzielle Integer sind trivial durchzählbar; UUIDs erschweren das Erraten, beseitigen die Schwachstelle aber nicht (siehe unten).
- Verkettete (chained) IDOR: Mehrere kleinere Schwachstellen werden zu einer Account-Übernahme kombiniert ("Bug Chaining"). Ein Beispiel: Ein Multi-Step-Approval-Workflow prüft die Ownership beim Erstellen einer Anfrage korrekt, versäumt sie aber beim späteren Wiederaufnehmen des ausstehenden Vorgangs. Ein anderer authentifizierter Nutzer ersetzt die Request-ID und greift so auf fremde Vorgänge zu, obwohl er selbst korrekt eingeloggt ist.
- Mass Assignment: Ein verwandtes Muster, bei dem Angreifer nicht vorgesehene Felder in einen Request einschleusen, etwa
isAdmin=trueodercredit=10000.
Verwundbarer vs. sicherer Code
Das folgende Muster ist der Kern fast jeder IDOR: Ein Objekt wird direkt über die vom Nutzer gelieferte ID geladen, ohne Ownership-Prüfung. Zuerst ein verwundbarer FastAPI-Endpunkt in Python:
@app.get("/users/{user_id}")
async def get_user_by_id(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
Hier fehlt jede Autorisierungsprüfung. Jeder, der die user_id in der URL ändert, erhält fremde Daten. Die sichere Variante vergleicht die authentifizierte Identität mit der angefragten ID und gibt bei Nichtübereinstimmung HTTP 403 zurück:
@app.get("/users/{user_id}")
async def get_secure_user(user_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
if current_user.id != user_id:
raise HTTPException(status_code=403, detail="Not authorized to access this user")
user = db.query(User).filter(User.id == user_id).first()
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
Das gleiche Problem in Node.js/Express, verwundbar: Der Server prüft nur, ob der Nutzer eingeloggt ist, nicht, ob ihm die Ressource gehört.
app.get("/user/id/:id", (req, res) => {
const user = db.users.find(req.params.id);
if (req.isAuthenticated()) {
// Authentifizierung allein reicht nicht!
res.render("user", { user });
}
});
Sicher wird es erst, wenn zusätzlich die Ownership geprüft wird:
app.get("/user/id/:id", (req, res) => {
const user = db.users.find(req.params.id);
// Nutzer muss authentifiziert UND autorisiert sein
// typsicherer Vergleich: session.userId ist eine Zahl, req.params.id ein String
if (req.isAuthenticated() && String(req.session.userId) === String(req.params.id)) {
res.render("user", { user });
} else {
return res.status(403).json({ message: "Forbidden" });
}
});
Der elegantere Ansatz: Ownership über das Datenmodell erzwingen
Die robusteste Lösung ist, den Ownership-Check gar nicht als separate if-Bedingung zu schreiben, sondern direkt in die Datenabfrage einzubauen. Das OWASP Cheat Sheet zeigt das an einem Ruby/ActiveRecord-Beispiel:
# verwundbar: sucht in ALLEN Projekten
@project = Project.find(params[:id])
# sicher: sucht nur in den Projekten des aktuellen Nutzers
@project = @current_user.projects.find(params[:id])
Der Unterschied ist subtil, aber entscheidend: Statt global über alle Datensätze zu suchen und danach zu prüfen, wird die Suche von vornherein auf die Datensätze eingeschränkt, die dem authentifizierten Nutzer gehören. Gibt es das Objekt für diesen Nutzer nicht, findet die Abfrage schlicht nichts. Vergisst ein Entwickler diesen Scope später, fällt das viel eher auf, als eine fehlende separate Prüfung.
Auch die formalen CWE-Beispiele von MITRE zeigen dasselbe Antimuster. Dieses C#-Beispiel (CWE-639) schützt korrekt vor SQL-Injection durch parametrisierte Abfrage, enthält aber keinerlei Autorisierungsprüfung:
conn = new SqlConnection(_ConnectionString);
conn.Open();
int16 id = System.Convert.ToInt16(invoiceID.Text);
SqlCommand query = new SqlCommand("SELECT * FROM invoices WHERE id = @id", conn);
query.Parameters.AddWithValue("@id", id);
SqlDataReader objReader = objCommand.ExecuteReader();
Obwohl die Oberfläche nur die eigenen Rechnungen anzeigt, kann ein Angreifer, der die Oberfläche umgeht und direkt eine beliebige id sendet, jede Rechnung abrufen. Das ist die wichtigste Lehre: Schutz vor SQL-Injection und Schutz vor IDOR sind zwei völlig verschiedene Dinge. Eine parametrisierte Query allein macht einen Endpunkt nicht sicher.
Warum UUIDs allein nicht reichen
Ein weit verbreiteter Irrglaube lautet: "Wir nutzen UUIDs statt fortlaufender IDs, also sind wir gegen IDOR geschützt." Das ist falsch, und gleich mehrere Primärquellen (OWASP Cheat Sheet, Hadrian, Aikido) betonen es unabhängig voneinander.
UUIDs und komplexe Identifikatoren erschweren das Erraten gültiger Werte drastisch, sie ersetzen aber keine Autorisierungsprüfung. Das OWASP Cheat Sheet ist unmissverständlich: Auch mit komplexen IDs bleiben "access control checks essential". Der Grund: Angreifer müssen IDs gar nicht raten. Sie erhalten gültige Identifikatoren auf ganz normalen Wegen, über Endpoint-Antworten, geteilte Links, Logs, die Browser-Historie oder abgefangene Requests. Sobald ein Angreifer eine gültige fremde UUID kennt und keine Ownership-Prüfung stattfindet, funktioniert die IDOR genauso wie mit einer fortlaufenden Nummer.
UUIDs sind also eine sinnvolle Defense-in-Depth-Maßnahme, die automatisiertes Durchzählen (Enumeration) verhindert. Sie sind aber niemals die eigentliche Kontrolle. Die eigentliche Kontrolle ist immer die serverseitige Prüfung "gehört dieses Objekt dem anfragenden Nutzer?".
Force Browsing und fehlende Funktionsebenen-Zugriffskontrolle
Nicht jede Zugriffskontroll-Schwachstelle ist eine IDOR. Ein zweiter großer Block ist die fehlende Zugriffskontrolle auf Funktionsebene, oft über Force Browsing ausgenutzt (formal CWE-425, Direct Request/Forced Browsing). Dabei ruft ein Angreifer schlicht eine URL direkt auf, die in der Oberfläche gar nicht verlinkt ist:
/app/getappInfo (sollte Authentifizierung erfordern)
/app/admin_getappInfo (sollte Admin-Rechte erfordern)
Fehlt die serverseitige Prüfung, erreicht ein nicht authentifizierter Nutzer geschützte Seiten oder ein normaler Nutzer die Admin-Funktionen. Verwandte Techniken zur Umgehung von URL-Restriktionen sind besonders tückisch, weil sie oft an schlecht konfiguriertem Routing scheitern:
- Nicht-standardisierte Header: Ein
X-Original-URL- oderX-Rewrite-URL-Header überschreibt bei manchen Frameworks den geprüften Pfad.
POST / HTTP/1.1
X-Original-URL: /admin/deleteUser
- URL-Matching-Diskrepanzen: unterschiedliche Groß-/Kleinschreibung (
/ADMIN/DELETEUSER), zusätzliche Dateiendungen (/admin/deleteUser.anythingbei bestimmten Spring-Konfigurationen) oder ein Trailing Slash (/admin/deleteUser/). - Referer-basierte Kontrolle: Verlässt sich eine Anwendung auf den
Referer-Header zur Autorisierung, kann der Angreifer diesen Header beliebig setzen.
Ein oft übersehener Sonderfall ist die Privilege De-Escalation: Zugriffskontrollen sind häufig nur gegen Eskalation nach oben abgesichert. Dass ein Angreifer einen hochprivilegierten Nutzer versehentlich oder gezielt herabstufen und damit Admin-Konten sabotieren kann, wird selten geprüft.
JWT- und Session-bezogene Access-Control-Fehler
JSON Web Tokens (JWT) sind heute der Standard für zustandslose Authentifizierung in APIs, und genau hier passieren Access-Control-Fehler, die in den meisten IDOR-Erklärungen fehlen. Die OWASP-A01-Kategorienliste nennt sie ausdrücklich: "Metadaten-Manipulation, z. B. Replay oder Tampering eines JWT-Access-Control-Tokens." Zwei typische Fehlerklassen sind besonders häufig.
Fehler 1: Das Token wird dekodiert, aber nicht verifiziert. Ein JWT besteht aus Header, Payload und Signatur. Die Signatur ist der einzige Schutz gegen Manipulation. Wer das Token nur dekodiert (die Payload liest), ohne die Signatur zu prüfen, vertraut Werten, die der Angreifer frei setzen kann, etwa einem role-Claim.
Fehler 2: Die Identität aus dem Token wird nicht gegen die Objekt-Ownership geprüft. Selbst ein korrekt signiertes Token sagt nur, wer der Nutzer ist, nicht, worauf er zugreifen darf. Das folgende Node.js-Beispiel (mit der Bibliothek jsonwebtoken) macht beide Fehler gleichzeitig:
const jwt = require("jsonwebtoken");
// VERWUNDBAR: vertraut dem role-Claim und prueft keine Ownership
app.get("/api/orders/:orderId", (req, res) => {
const token = req.headers.authorization?.split(" ")[1];
const payload = jwt.decode(token); // decode() prueft die Signatur NICHT
if (payload.role === "user") {
const order = db.orders.findById(req.params.orderId);
return res.json(order); // gibt JEDE Bestellung zurueck, egal wem sie gehoert
}
});
Zwei Angriffe werden dadurch möglich. Erstens kann ein Angreifer die Signaturprüfung aushebeln, weil jwt.decode() sie schlicht überspringt, und sich per manipuliertem Payload etwa role: "admin" verleihen. Zweitens, und das ist die eigentliche IDOR, gibt der Endpunkt jede beliebige orderId zurück, weil kein Abgleich zwischen dem Token-Inhaber und dem Besitzer der Bestellung stattfindet. Die sichere Variante behebt beides:
const jwt = require("jsonwebtoken");
app.get("/api/orders/:orderId", (req, res) => {
const token = req.headers.authorization?.split(" ")[1];
let payload;
try {
// verify() prueft die Signatur und lehnt "alg: none" oder manipulierte Tokens ab
payload = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ["HS256"] });
} catch (err) {
return res.status(401).json({ message: "Invalid token" });
}
// Ownership erzwingen: nur Bestellungen des Token-Inhabers laden
const order = db.orders.findOne({ id: req.params.orderId, ownerId: payload.sub });
if (!order) {
return res.status(403).json({ message: "Forbidden" });
}
return res.json(order);
});
Drei Punkte machen den Unterschied. jwt.verify() prüft die Signatur und weist mit der expliziten algorithms-Angabe auch den berüchtigten "alg: none"-Angriff sowie Algorithmus-Verwirrung ab. Die Identität wird aus dem verifizierten Token (payload.sub) abgeleitet, niemals aus der Anfrage. Und die Datenabfrage bindet das Objekt direkt an den Token-Inhaber (ownerId: payload.sub), sodass fremde Bestellungen gar nicht erst geladen werden.
Ergänzend gehört zum sicheren Umgang mit Tokens und Sessions: Stateful Session-Identifier müssen beim Logout serverseitig invalidiert werden. Bei zustandslosen JWTs sollte man kurze Lebensdauern bevorzugen, weil ein einmal ausgestelltes Token sonst bis zum Ablauf gültig bleibt, auch nach einem Logout. Sequenzielle oder erratbare Session-IDs sind eine eigene Ausprägung von CWE-639, weil sie es Angreifern erlauben, zwischen fremden Sessions zu wechseln.
Prävention: Zugriffskontrolle strukturell richtig bauen
Über alle Primärquellen hinweg kristallisiert sich ein konsistentes Muster heraus. Die folgenden Prinzipien greifen ineinander.
Deny by default
Sofern eine Ressource nicht ausdrücklich öffentlich sein soll, wird der Zugriff standardmäßig verweigert. PortSwigger formuliert es als Grundregel: "Unless a resource is intended to be publicly accessible, deny access by default." Neue Endpunkte sind damit von Haus aus geschützt, nicht offen.
Serverseitig, bei jedem Zugriff
Die Prüfung gehört in vertrauenswürdigen Server-Code und muss bei jedem Zugriff erfolgen, nicht nur beim ersten. Gerade Multi-Step-Workflows brechen, wenn die Ownership nur beim Erstellen geprüft wird und beim späteren Bearbeiten nicht mehr. Frontend-Prüfungen sind reine Bequemlichkeit für den Nutzer, niemals Sicherheit. Ein Frontend-only-Schutz scheitert sofort gegen direkte API-Requests, etwa per curl.
Ownership über das Domänenmodell erzwingen
Statt generischer CRUD-Rechte ("darf lesen") gehört die Frage "gehört dieses Objekt dem Nutzer?" ins Domänenmodell, idealerweise als gescopte Abfrage wie im Ruby-Beispiel oben. OWASP empfiehlt zusätzlich, Identifikatoren gar nicht erst zu exponieren: Den authentifizierten Nutzer aus Session- oder Token-Daten ableiten statt aus URL oder Body, und in mehrstufigen Abläufen IDs über die Session weiterreichen.
RBAC und ABAC: die passenden Modelle wählen
Für strukturierte Autorisierung, wie sie ein umfassendes Identity and Access Management bereitstellt, haben sich zwei Modelle etabliert:
- RBAC (Role-Based Access Control): Rechte hängen an Rollen, Nutzer werden Rollen zugewiesen (etwa "Nutzer", "Redakteur", "Admin"). RBAC ist einfach zu verstehen und zu auditieren und passt gut, wenn sich Berechtigungen sauber in wenige, klar abgegrenzte Rollen fassen lassen. Bei sehr feingranularen oder objektabhängigen Regeln wird RBAC schnell unübersichtlich (Rollenexplosion).
- ABAC (Attribute-Based Access Control): Entscheidungen basieren auf Attributen von Nutzer, Ressource und Kontext (etwa Abteilung, Eigentümer, Tageszeit, Mandant). ABAC ist ausdrucksstärker und eignet sich für dynamische, kontextabhängige Regeln und Multi-Tenant-Umgebungen, ist dafür aber komplexer zu implementieren und zu testen.
Als Faustregel: RBAC, wenn wenige klare Rollen ausreichen; ABAC (oder eine Kombination), sobald objektbezogene Ownership, Mandantengrenzen oder kontextabhängige Bedingungen eine Rolle spielen. Für komplexe Szenarien bietet sich zentralisierte Policy-as-Code an, etwa mit dem Open Policy Agent (OPA), damit die Autorisierungslogik an einer Stelle liegt und nicht über die Codebasis verstreut ist.
Weitere bewährte Maßnahmen
- Unratebare IDs (UUIDs) als Defense in Depth gegen Enumeration, aber nie als Ersatz für die Prüfung.
- Zentralisierte statt verstreuter Autorisierungslogik: Mechanismus einmal implementieren, überall wiederverwenden.
- Mandantengrenzen in Multi-Tenant-Systemen bei jeder Anfrage validieren.
- Rate Limiting für API- und Controller-Zugriffe, um automatisierte Enumeration einzudämmen.
- Zugriffskontrollfehler protokollieren und Administratoren bei wiederholten Fehlversuchen alarmieren.
- Directory Listing deaktivieren und Metadaten wie
.gitaus dem Web-Root fernhalten. - Funktionale Zugriffskontroll-Tests als Unit- und Integrationstests in die Pipeline aufnehmen.
Warum automatische Scanner versagen und wie man richtig testet
Ein Grund, warum Broken Access Control so hartnäckig ist: Automatische Scanner finden sie strukturell schlecht. Bei XSS oder SQL-Injection gibt es klare Signaturen (reflektierte Payloads, Fehlermeldungen). Bei Zugriffskontrolle fehlt diese Signatur. Ein Scanner "weiß nicht, ob eine gegebene Rechnung Nutzer A oder Nutzer B gehört". Eine technisch saubere Antwort (HTTP 200) wird als unbedenklich gewertet, obwohl inhaltlich fremde Daten zurückkamen. Statische Analyse leidet zusätzlich unter hohem Rauschen, weil Autorisierungslogik oft in Middleware oder geteilten Services steckt.
Zugriffskontrolle braucht deshalb menschliches Urteilsvermögen und kontextbewusstes Testen. Zwei praktikable Methoden:
Die Zwei-Browser- bzw. Zwei-Nutzer-Technik
Man legt zwei Testkonten an (Nutzer A und Nutzer B), jeweils mit eigenen Objekten. Ein Browser-Tab bleibt als Nutzer A angemeldet, ein zweiter (etwa im Inkognito-Modus) agiert als Angreifer. Dann versucht man systematisch, als Nutzer A auf die Objekte von Nutzer B zuzugreifen, und zwar bei allen Operationen: Lesen, Erstellen, Aktualisieren, Löschen, Exportieren, Administration. Wichtig sind Edge Cases wie id=0 und negative Zahlen, die Entwickler häufig übersehen. Zu prüfen sind alle Angriffsflächen: URL-Parameter, POST-Body, HTTP-Header, Cookies und API-Response-IDs, die in Folge-Requests wiederverwendet werden.
Burp Suite und die Autorize-Erweiterung
Manuell führt der Weg über Burp Suite: Ziel-Request abfangen, nach Parametern filtern, die wie Ressourcen-Identifikatoren aussehen, den Request an den Repeater schicken und den Parameter systematisch verändern. Für IDOR-Tests im großen Stil ist die Burp-Erweiterung Autorize der De-facto-Standard: Man hinterlegt die Session-Cookies eines niedrig privilegierten Nutzers, surft dann als hochprivilegierter Nutzer durch die Anwendung, und Autorize wiederholt jeden Request automatisch mit den Cookies des niedrig privilegierten Nutzers. Die Ergebnisse werden farblich markiert (grün: korrekt geblockt, rot: umgangen, orange: manuell zu prüfen). So sieht man auf einen Blick, welche Endpunkte die Autorisierung nicht durchsetzen. Ergänzende Werkzeuge sind OWASP ZAP, Nuclei-IDOR-Templates, ffuf und wfuzz zum Fuzzing sowie Postman für manuelles API-Testing.
Systematisches Testen ist eng verwandt mit der Arbeit in Bug-Bounty-Programmen, in denen IDOR zu den am häufigsten gemeldeten und oft gut vergüteten Schwachstellenklassen zählt.
Reale Vorfälle und CVEs
Broken Access Control ist keine Theorie. Einige dokumentierte Fälle, chronologisch:
- US-Verteidigungsministerium (2020): Das Sicherheitsunternehmen Silent Breach entdeckte eine IDOR-Schwachstelle, die unautorisierten Zugriff ermöglichte. Behoben wurde sie laut Wikipedia durch Einführung eines Nutzer-Session-Mechanismus.
- Parler (2021): Sequenzielle Post-Identifikatoren sollen massives Data-Scraping ermöglicht haben. Wichtig: Wikipedia selbst weist darauf hin, dass der beteiligte Forscher Teile der Medienberichterstattung zu den technischen Details bestritten hat. Der Fall ist also mit Vorsicht zu behandeln und kein unstrittiger Fakt.
- Instagram (2022): Eine IDOR in der "Interests"-Funktion erlaubte es, über eine manipulierte Nutzer-ID in POST-Daten fremde Interessen zu ändern. Berichtet wird eine Bug-Bounty-Prämie von 4.300 US-Dollar (Sekundärquelle, nicht unabhängig verifiziert).
- CVE-2023-4836 (WordPress-Plugin "User Private Files"): Erlaubte Nutzern mit minimalen Rechten, Dateien aus den Ordnern anderer Nutzer abzurufen (Quelle: MDN, ohne genannten CVSS-Score).
- TikTok (2023): Über den Parameter
aweme_idsollen sich private "Memories" anderer Nutzer haben einsehen lassen (Sekundärquelle). - CVE-2021-36539: Von MITRE als klassisches CWE-639-Beispiel geführt, eine Bildungsanwendung, die Datei-IDs nicht korrekt auf Nutzer beschränkte, sodass Angreifer sie per Brute-Force erraten konnten.
Weitere in Sekundärquellen genannte, hier aber nicht unabhängig verifizierte Fälle sind CVE-2024-46528 (KubeSphere), CVE-2024-55471 (Oqtane), CVE-2024-48899 (Moodle Course Badges) und CVE-2025-13526 (WordPress-Plugin "OneClick Chat to Order", laut Sekundärquelle CVSS 7.5, sequenzielle order_id erlaubte unautorisierten Massenabruf von Kundendaten). Diese Nummern eignen sich als Rechercheeinstieg, sollten für konkrete technische Aussagen aber gegen die offizielle NVD abgeglichen werden. Wie man CVE-Einträge und ihre CVSS-Scores richtig liest, erklärt unser CVE-Leitfaden.
Broken Access Control in den OWASP Top 10: die Zahlen
Broken Access Control führt die OWASP Top 10 an, in der Edition 2021 wie in der Edition 2025. Damit die Zahlen nicht durcheinandergeraten, hier die beiden Editionen sauber getrennt, jeweils direkt aus der offiziellen OWASP-Primärquelle:
- OWASP Top 10:2021: 34 zugeordnete CWEs, durchschnittliche Incidence-Rate 3,81 Prozent, maximale Incidence-Rate 55,97 Prozent, 318.487 Occurrences und 19.013 zugeordnete CVEs.
- OWASP Top 10:2025: 40 zugeordnete CWEs, durchschnittliche Incidence-Rate 3,74 Prozent, maximale Incidence-Rate 20,15 Prozent, 1.839.701 Occurrences und 32.654 zugeordnete CVEs.
Eine oft zitierte Aussage verdient eine Einordnung: "100 Prozent der getesteten Anwendungen wiesen eine Form von Broken Access Control auf." Dieser Satz stammt wörtlich von der OWASP-2025-Seite, misst aber etwas anderes als die Incidence-Rate. Die 100-Prozent-Aussage bedeutet: In praktisch jeder getesteten Anwendung fand sich mindestens ein Vorkommen irgendeiner der zugeordneten CWEs. Die durchschnittliche Incidence-Rate von 3,74 Prozent bezieht sich dagegen auf den Anteil der Testfälle, die ein einzelnes, spezifisches CWE-Muster aufwiesen, gemittelt über alle 40 Schwachstellenklassen. Beide Zahlen sind korrekt, sie beschreiben nur verschiedene Dinge und dürfen nicht vermischt werden.
Alle drei zentralen CWEs (CWE-639, CWE-862 und die abstrakte Pillar CWE-284) sind sowohl in der OWASP-Top-10-Kategorie A01 als auch im CWE Top 25 der gefährlichsten Software-Schwächen gelistet. CWE-639 gilt zudem mit "hoher Ausnutzungswahrscheinlichkeit". Eine vollständige Einordnung aller Kategorien bietet unser Überblick zu den OWASP Top 10.
Häufige Fragen
Was ist Broken Access Control?
Broken Access Control (fehlerhafte Zugriffskontrolle) bezeichnet den Fall, dass eine Anwendung nicht korrekt durchsetzt, worauf ein Nutzer zugreifen und was er tun darf. OWASP führt sie als Kategorie A01 an der Spitze der Top 10. Typische Folgen sind der unautorisierte Zugriff auf fremde Daten, deren Änderung oder Löschung sowie die Ausführung von Funktionen außerhalb der eigenen Berechtigung. Die Ursache ist fast immer eine fehlende oder fehlerhafte serverseitige Autorisierungsprüfung.
Was ist der Unterschied zwischen IDOR und Broken Access Control?
Broken Access Control ist der Oberbegriff für jede Art fehlerhafter Zugriffskontrolle. IDOR (Insecure Direct Object Reference) ist ein konkreter Unterfall davon: Ein Nutzer greift auf ein fremdes Objekt zu, indem er eine von ihm kontrollierbare Referenz verändert (etwa eine ID in der URL), ohne dass der Server prüft, ob ihm dieses Objekt gehört. Formal entspricht IDOR meist CWE-639. Jede IDOR ist also Broken Access Control, aber nicht jede Broken Access Control ist eine IDOR.
Was ist der Unterschied zwischen horizontaler und vertikaler Rechteausweitung?
Bei der horizontalen Rechteausweitung greift ein Nutzer auf die Ressourcen eines gleichrangigen anderen Nutzers zu, etwa auf dessen Kontoauszug, ohne selbst höhere Rechte zu erlangen. Bei der vertikalen Rechteausweitung erlangt ein normaler Nutzer Funktionen einer höheren Rolle, typischerweise Admin-Rechte. Beide können zusammenwirken: Übernimmt man horizontal das Konto eines privilegierten Nutzers, wird daraus eine vertikale Eskalation.
Wie verhindert man IDOR-Schwachstellen?
Der Kern ist immer eine serverseitige, objektbezogene Autorisierungsprüfung bei jedem Zugriff: "Gehört dieses konkrete Objekt dem anfragenden Nutzer?" Am robustesten erzwingt man das direkt in der Datenabfrage, indem man nur die Objekte des authentifizierten Nutzers durchsucht, statt global zu laden und danach zu prüfen. Ergänzend helfen deny by default, die Ableitung der Identität aus Session oder Token statt aus der Anfrage sowie automatisierte Zugriffskontroll-Tests. Frontend-Prüfungen bieten keinen Schutz.
Schützen UUIDs vor IDOR?
Nein. UUIDs erschweren nur das Erraten gültiger Identifikatoren (Enumeration), sie ersetzen aber keine Autorisierungsprüfung. Angreifer müssen IDs gar nicht raten, sie erhalten gültige Werte über Endpoint-Antworten, geteilte Links, Logs oder abgefangene Requests. Kennt ein Angreifer eine gültige fremde UUID und fehlt die Ownership-Prüfung, funktioniert die IDOR genauso wie mit einer fortlaufenden Nummer. UUIDs sind eine sinnvolle Defense-in-Depth-Maßnahme, aber niemals die eigentliche Kontrolle.
Fazit
Broken Access Control ist nicht deshalb die Nummer 1 der OWASP Top 10, weil sie besonders raffiniert wäre, sondern weil sie so leicht entsteht und so schwer automatisiert zu finden ist. Der rote Faden durch alle Ausprägungen, ob horizontale IDOR, vertikale Rechteausweitung, Force Browsing oder JWT-Fehler, ist immer derselbe: Die Anwendung verwechselt Authentifizierung mit Autorisierung und prüft nicht bei jedem Zugriff serverseitig, ob der Nutzer für genau dieses Objekt berechtigt ist.
Wer das strukturell richtig baut, folgt einigen wenigen Prinzipien: deny by default, serverseitige Prüfung bei jedem Zugriff, Ownership über das Domänenmodell erzwingen, ein passendes Modell (RBAC oder ABAC) zentralisiert implementieren und UUIDs nur als ergänzende, nicht als tragende Schicht verstehen. Getestet wird nicht mit Scannern allein, sondern mit menschlichem Urteilsvermögen, der Zwei-Nutzer-Technik und Werkzeugen wie Burp Suite und Autorize. So wird aus einer der häufigsten Schwachstellen der Webentwicklung eine beherrschbare.