Erfahre im kostenlosen eBook wie du mit OAuth2, API-Keys, JWT und einem Gateway APIs schützen kannst.
Dieser Artikel ist zuerst im Java Magazin 2.2026 unter dem Titel „Kein Griff in fremde Warenkörbe“ erschienen.
APIs haben eigene, unterschiedlich ausgeprägte Fehler- und Bedrohungslagen. So speichern sie oft keinen Sitzungszustand auf dem Server, sondern vertrauen auf vom Client übermittelte Parameter (z. B. Objekt-IDs), um zu entscheiden, auf welche Daten zugegriffen wird. Das erleichtert Skalierung, aber eröffnet Angreifern neue Möglichkeiten, fehlende Autorisierung auszunutzen.
Ein Paradebeispiel dafür ist Broken Object Level Authorization (BOLA). Hinter dem sperrigen Begriff verbirgt sich ein alltägliches Risiko: fehlende Prüfungen auf Objektebene, die es angemeldeten Angreifern erlauben, durch ID-Manipulation auf fremde Daten zuzugreifen. Greift ein Nutzer auf ein Objekt zu, für das seine Rechte nicht überprüft (oder nicht vorhanden) sind, entsteht eine kritische Sicherheitslücke. Kein Wunder also, dass BOLA in den „OWASP API Security Top 10“ auf Platz 1 steht (Abb. 1).

Aufgrund der Ähnlichkeit wird BOLA häufig als moderner Name für das klassische IDOR (Insecure Direct Object Reference) genutzt. Streng genommen liegt der Unterschied vor allem im Kontext: IDOR wurde meist im Rahmen traditioneller Webanwendungen diskutiert, BOLA bezeichnet speziell Autorisierungslücken in API-gestützten Szenarien.
Ohne weitere Umschweife schauen wir uns nun praxisnah an, wie BOLA-Lücken entstehen, wie man sie in Java/Spring APIs vermeiden kann und welche Schutzund Detection-Strategien für Entwickler, Architekten und CISOs sinnvoll sind.
Stellen wir uns folgendes vor: Ein Supermarkt-API bietet Endpunkte, um den Warenkorb eines Kunden zu verwalten – Artikel hinzufügen, entfernen etc. Jeder Kunde soll nur seinen eigenen Warenkorb bearbeiten können. Bei einer BOLA-Schwachstelle fehlt jedoch genau diese Sicherstellung auf Objektebene. Ergebnis: Ein authentifizierter Nutzer kann durch simple ID-Tricks fremde Warenkörbe einsehen oder manipulieren. Bildlich gesprochen greift Kunde 1 in den Warenkorb von Kunde 2, indem er im API Call seine ID durch die von Kunde 2 ersetzt.
Diese Attacke erfordert weder besonderes Knowhow noch spezielle Tools. Wenn Ressourcen-IDs erratbar oder fortlaufend sind (z.B. kunde/1, kunde/2 …), braucht ein Angreifer bloß diese zu verändern. Ist das API auf Objektebene nicht abgesichert, erhält er unbefugt Zugriff. BOLA ist also letztlich alter Wein in neuen Schläuchen – das altbekannte IDOR-Problem in modernen APIs. Der Unterschied: In APIs tritt es besonders häufig auf, weil hier Clients eigenständig Objektreferenzen übermitteln (statt wie in klassischen Web-Apps überwiegend serverseitig gesteuert).
Betrachten wir ein Shop-API (etwa für einen Onlinesupermarkt), das von einer Web- oder Mobile-App genutzt wird. Ein Endpunkt erlaubt es, Artikel in den Warenkorb eines Kunden zu legen (Listing 1).
Der Fehler: Wenn das Backend blind der kundenId aus dem URL vertraut, kann ein angemeldeter Nutzer seinen Request manipulieren. Benutzer Kunde 1 (ID=1) ändert z. B. den URL auf /api/kunden/2/warenkorb und fügt einen Artikel hinzu. Ohne weitere Prüfung würde das API den Artikel nun im Warenkorb von Kunde 2 ablegen: Eine Autorisierungslücke!
Diese horizontale Rechteausweitung ist ein klassischer BOLA-Fall. Sie passiert nach erfolgter Authentifizierung (der Nutzer ist angemeldet), aber ohne korrekte Autorisierung auf Objektebene. Ein analoger vertikaler Angriff (z. B. Nutzer greift auf Adminressource zu) fällt hingegen eher unter Broken Function Level Authorization und wird hier nicht betrachtet.
Solche Bugs entstehen oft aus Bequemlichkeit oder Unachtsamkeit. Man überlässt dem Client die Angabe der Objekt- oder Benutzer-ID, um sich im Servercode einen scheinbar überflüssigen Schritt zu sparen. Im Beispiel könnte ein unerfahrener Entwickler etwa das in Listing 2 Gezeigte implementiert haben.
Diese Methode nimmt die Kunden-ID direkt aus dem Pfadparameter, nutzt sie ungeprüft im Service und ignoriert den angemeldeten Nutzer (auth). Wenn warenkorbService.fuegeHinzu nicht selbst noch prüft, ob der aktuelle User zu kundenId gehört, ist das ein offenes Scheunentor. Und in der Praxis fehlt diese Prüfung leider oft. Sei es, weil sie vergessen wurde, oder weil sie bei Refactorings/Library-Updates versehentlich entfernt wurde. Schließlich funktioniert es ja scheinbar: Der reguläre Client übergibt immer die eigene ID, im normalen Betrieb fällt der Fehler nicht auf. Erst gezielte Tests (oder ein Angreifer) decken auf, dass hier jeder in jedermanns Warenkorb wirtschaften kann.
In traditionellen Webanwendungen fängt manchmal ein zentraler Mechanismus solche Fehler ab (z. B. ein Servlet-Filter oder MVC Interceptor, der Pfade auf Mitgliedschaft prüft). Bei Microservices APIs jedoch liegt die Verantwortung meist beim Service selbst. Ein API Gateway kann zwar auf Pfadmuster reagieren, weiß aber in diesem Fall noch nicht, welche Kunden- ID der anfragende User haben sollte. Deshalb gilt: Objektberechtigungen müssen in der Regel innerhalb des Service-Codes geprüft werden, möglichst nah an der Datenbankabfrage oder dem Zugriff auf das Objekt.
Besonders herausfordernd wird es, wenn mehrere Microservices dieselbe Zugriffs-Policy umsetzen müssen – etwa bei domänenübergreifenden Ressourcen. Die Logik konsistent zu halten, etwa über eine gemeinsame Library oder Policy-Definition, ist technisch anspruchsvoll und fehleranfällig. Schon kleine Abweichungen oder Versionsunterschiede können zu inkonsistentem Verhalten und Sicherheitslücken führen.
Im Folgenden sollen beispielhafte Lösungen für unser Shop-API-Beispiel gezeigt werden.
Der simpelste Fix ist, die übergebene ID gegen den eingeloggten Nutzer zu validieren. In Java/Spring zieht man z. B. die User-ID aus dem Security-Context (Session oder JWT) und vergleicht (Listing 3).
Hier wird vor der Warenkorboperation geprüft, ob die Pfad-ID mit der ID des aktuellen Nutzers übereinstimmt. Falls nicht, wird mit „404 Not Found“ abgebrochen. Dieses Muster folgt dem Prinzip: „Traue nie den vom Client gesendeten Daten!“ – egal ob im URLPfad, Query-Param oder Request Body, jede vom Client kommende ID muss als potenziell manipuliert angesehen werden. Entscheidend ist, die Serverlogik darauf aufzubauen, welche Objekte ein Benutzer besitzen oder bearbeiten darf.
Spring Security erlaubt, solche Checks deklarativ per Annotation zu lösen. Zum Beispiel lässt sich festlegen, dass der Methodenaufruf nur erlaubt ist, wenn die Pfad- ID mit der User-ID im Token übereinstimmt (Listing 4).
Der SpEL-Ausdruck in @PreAuthorize prüft zur Laufzeit, ob die übergebene ID (#id) gleich der ID des angemeldeten Nutzers (authentication.principal.id) ist. Falls false, wird die Methode gar nicht ausgeführt – Spring Security gibt automatisch „403“ zurück. Vorteil: Der Businesscode bleibt sauber, die Sicherheitslogik ist zentral konfigurierbar. Voraussetzung ist, dass der principal eine id-Property hat (bei eigenen Userobjekten i. d. R. der Fall). Alternativ ließe sich ein eigener PermissionEvaluator nutzen oder eine Service-Methode, z. B. @PreAuthorize("@userService.hatZugriff(#id)"), die intern den Besitz prüft.
Ein ergänzender Schutz besteht darin, die Identifier weniger erratbar zu machen. Im Warenkorb-Beispiel heißt das: Statt offener, sequenzieller Kunden-IDs könnte jeder Nutzer einen zufälligen Warenkorb-Key haben, z.B. /api/warenkoerbe/ASDF-XYZ-1234 anstelle von /api/kunden/2/warenkorb. Selbst wenn hier versehentlich keine Prüfung auf den Besitzer erfolgt, ist es für Angreifer deutlich schwieriger, gültige fremde IDs zu finden. Wichtig: Das ist nur zusätzliche Sicherheit, kein Ersatz für Autorisierungsprüfungen. Falls ein Angreifer doch einen gültigen Key erlangt (z. B. via Leak oder Brute Force), nützt die Random-ID allein nichts. Trotzdem: Unvorhersehbare GUIDs oder ausreichend lange, zufällige IDs erschweren Massenangriffe erheblich und sind daher Best Practice, wo es möglich ist.
Entwickler sollten gezielt Tests schreiben, um BOLA frühzeitig aufzudecken. Am besten in Form automatisierter Integrationstests: Ein Testuser A versucht auf Daten von User B zuzugreifen (oder umgekehrt) und muss dabei einen Fehler erhalten. Solche Szenarien sollten Teil der Testsuite sein. Zusätzlich kann man – bei verdächtigem Verhalten oder in regelmäßigen Audits – auch manuell prüfen, ob Endpunkte gegen ID-Tampering anfällig sind. Tools wie Postman oder Burp Suite ermöglichen es, identische Requests mit verschiedenen IDs auszuprobieren. Achtung: Manuelles Testen sollte nur ergänzend erfolgen; es ersetzt keinesfalls automatisierte Tests, sondern ist eher als „Notnagel“ oder für spezielle Penetrationstests gedacht. Im Team sollte es zum Qualitätsanspruch gehören, dass bei jedem Endpunkt mit Objekt-IDs ein entsprechender Negativtest existiert.
Fazit aus Beispiel 1: APIs müssen auf mehreren Ebenen die Türen abschließen. Auf Endpunktebene regelt z. B. Spring Security über Rollen/Authorities, ob ein Benutzer einen Pfad überhaupt aufrufen darf. Aber auf Objektebene muss jede Anfrage zusätzlich validieren, dass Benutzer X tatsächlich auf Objekt Y zugreifen darf. Fehlt diese Kontrolle, ist der „fremde Warenkorb“ offen – ein Super-GAU für Vertrauen und Sicherheit.
Nun ein komplexeres Szenario aus der Finanzwelt: Ein Banking-API bietet einen Endpunkt /api/konten/{kontoNr} an, über den ein Kunde seine Kontodetails abrufen kann. Hier gilt ebenfalls: Nur Berechtigte dürfen auf ein Konto zugreifen.
Angenommen, die Kontonummern sind vollständig numerisch und beinhalten die Kunden-ID in den letzten sechs Stellen. Zum Beispiel: Kontonummer 550000 123456, hier stellen die letzten sechs Ziffern (123456) die Kundennummer dar. Eine simple Policy-Idee wäre nun: „Erlaube den Zugriff nur, wenn die Kunden-ID im JWT mit den letzten Stellen der Kontonummer übereinstimmt.“ Anders ausgedrückt: Der Kontopfad /api/konten/550000123456 darf nur von Kunde 123456 selbst abgefragt werden, von niemandem sonst. (Abb. 2)

In Microsoft Azure könnte dazu eine API-Management- Policy, wie in Listing 5 gezeigt, realisiert werden.
In vielen Fällen funktioniert das. Aber was ist, wenn Kontovollmachten ins Spiel kommen? In der Praxis haben Kunden häufig bevollmächtigte Vertreter (z. B. eine Assistenz oder ein Familienmitglied), die ebenfalls Kontozugriff haben. Nehmen wir an, Kunde A (ID 111111) hat eine Vollmacht für das Konto von Kunde B (ID 987654). Die Kontonummer von B lautet etwa 770000 987654. Nach obiger Policy dürfte Kunde A dieses Konto nicht abrufen, da seine eigene ID (111111) nicht mit den letzten Stellen der Kontonummer (987654) übereinstimmt. Die starre Prüfung entsprechend dem Schema würde also legitime Zugriffe verwehren.
Kurz: Das Postfix-Muster „ID im Pfad muss gleich User-ID sein“ greift zu kurz, wenn Zugriffsrechte komplexer sind.
Statt mit Regex oder Namenskonventionen kann man die Autorisierung dynamischer lösen. Hier kommen JSON Web Tokens (JWT) und API Gateways ins Spiel.
Beim Log-in erhält jeder Nutzer ein JWT, das neben seiner User-ID auch alle Konten, auf die er Zugriff hat, als Claim enthält. Für Kunde A mit Vollmacht auf Bs Konto könnte ein dekodiertes JWT etwa so aussehen, wie in Listing 6 gezeigt.
In diesem Token signalisiert "berechtigteKonten", dass der Inhaber Zugriff auf zwei Konto-IDs hat: Auf das eigene Konto (660000 111111, das mit 111111 endet) und auf ein fremdes (770000 987654, das mit 987654 endet).
Das API Gateway (z. B. Membrane API Gateway) prüft bei jedem Request das JWT (Signatur, Gültigkeit etc.) und liest die Claims aus. Nun kann es auf Basis des Pfads und der Claims entscheiden, ob der Zugriff erlaubt wird. Konkret: Ist die angefragte Kontonummer im Claim berechtigteKonten enthalten? Eine beispielhafte Policy-Konfiguration in Membrane könnte so aussehen, wie in Listing 7 gezeigt.
Dieser Ausschnitt bewirkt Folgendes: Zunächst wird ein gültiges JWT vorausgesetzt (<jwtAuth> blockiert sonst schon). Dann prüft ein Groovy-Ausdruck, ob die Liste berechtigteKonten aus dem Token die angefragte Kontonummer (den Pfadteil [2] in /api/konten/{nr}) nicht enthält. Ist dem so (das ! negiert die Bedingung), wird die Anfrage mit „403“ geblockt. Ansonsten passiert sie und wird ans Backend weitergereicht.
Hinweis: Die genaue Syntax variiert je nach Gateway. Im Membrane-Beispiel oben greift exc.properties.jwt auf die JWT-Daten zu. Ähnliche Mechanismen bieten z.B. Apigee, Kong oder das AWS Gateway via custom Authorizer.
Analog könnte man diese Regel auch mit Open Policy Agent (OPA) implementieren, etwa als Rego-Snippet (Listing 8). Hier definiert die Policy, dass allow nur dann gültig ist, wenn die angefragte kontoId in der Liste der berechtigten Konten des JWTs auftaucht. Diese OPA-Logik könnte im Gateway oder im Microservice angewendet werden, um den Zugriff entsprechend zu steuern.
Vorteile dieser Methode: Die Autorisierungslogik wird zentral im Gateway durchgesetzt, noch bevor der eigentliche Service anspringt. In größeren Architekturen entlastet das die Microservices – die Standardchecks (JWT gültig? Darf Nutzer X theoretisch auf Ressource Y zugreifen?) werden am Eingang erledigt. Die Security-Policy ist unabhängig vom Service-Code verwaltbar, was dem CISO bzw. Security-Team ermöglicht, einheitliche Regeln durchzusetzen.
Die Zugriffsregeln lassen sich beispielsweise über Specification Extensions in OpenAPI nicht nur dokumentieren, sondern auch aktiv referenzieren. Durch standardisiert benannte Parameter und eigene Erweiterungen wie x-access-control können API-Gateways die entsprechende Regel automatisch erkennen und die passende Prüfung aktivieren – vorausgesetzt, sowohl die OpenAPI-Spezifikation als auch das zugehörige Regelwerk liegen dort vor. Mit Hilfe von Lintern lässt sich zudem automatisiert sicherstellen, dass OpenAPI-Dokumente solche Zugriffs-Policies konsistent referenzieren. Ein Beispiel wird in Listing 9 gezeigt.
Herausforderungen: Die Ausstellung solcher JWTs muss gut geplant sein. Der Identity Provider (z. B. OAuth2-Server) muss beim Log-in alle Berechtigungen kennen und ins Token packen. Bei sehr vielen Berechtigungen können JWTs groß werden. Alternativ könnte das Gateway benötigte Infos aus einem User- Info-Service nachladen, was aber Latenz bringt. In unserem Beispiel mit Kontovollmachten ist das JWT-Modell praktikabel, weil jeder Nutzer nur wenige Kontenberechtigungen hat.
Ein weiterer Punkt: Trotz Gateway-Sicherung sollten die Backend-Services eigene Prüfungen haben (Prinzip „Defense in Depth“). Im Konto-Backend würde man z. B. beim Datenbankzugriff stets überprüfen: Gehört diese Kontonummer zu einem Benutzer, der in der Anfrage steckt? Das kostet etwas Performance, bringt aber Sicherheit, falls jemand das Gateway umgehen sollte (z. B. interner Aufruf oder Fehlkonfiguration).
Zum Abschluss dieser Bank-API-Betrachtung sei gesagt: Nicht jede Situation erfordert ein so aufwändiges Set-up. Im Shop-Beispiel war die Lage simpler – in der Regel hat jeder nur seinen eigenen Warenkorb. Dort reicht die Codeprüfung im Service völlig aus. Den Overhead eines Claims-Systems im JWT lohnt sich vor allem bei vielen Objektberechtigungen pro Nutzer (wie bei Konten mit Vollmachten). Wichtig ist, die richtigen Daten im richtigen Schritt mitzuführen: Im Fall der Bank also Autorisierungsinformationen bereits im Token, damit nachgelagerte Komponenten gezielt filtern können. In jedem Fall müssen aber die Objekte selbst immer die letzte Verteidigungslinie haben (z.B. WHERE konto.id IN (Liste erlaubter Konten) in der Query).
Schon im Design kann man BOLA vorbeugen – etwa indem API-Spezifikationen (OpenAPI) klar festlegen, welche Endpunkte schützenswert sind. Linter wie Spectral können automatisiert prüfen, ob sensible Pfade ein Authentifizierungs- und/oder Zugriffs-Schema zugewiesen bekommen. So wird vermieden, dass ein kritischer Endpunkt versehentlich ohne Schutz veröffentlicht wird. Zwar erkennt ein solches Werkzeug in der Standardkonfiguration keine fehlende Objektprüfung, doch die Konfiguration ist erweiterbar: Beispielsweise ließe sich eine Regel definieren, die bei Pfaden mit einem Parameter wie kontoNr zwingend eine referenzierte Zugriffs-Policy (z.B. x-access-control) verlangt. Damit wird nicht nur Konsistenz sichergestellt, sondern auch die Durchsetzung konkreter Autorisierungsregeln unterstützt – direkt aus der Spezifikation heraus.
Attributbasierte Zugriffskontrolle (ABAC): In komplexen Fällen (wie dem Bankbeispiel) kann man statt starrer Rollen-/Besitzprüfungen auch Policies definieren, die auf Attributen basieren. Zum Beispiel: „Erlaube Zugriff, wenn (request.user.abteilung == konto.abteilung) ODER (kontoId in request.user. vollmachten).“ Solche Regeln ließen sich mit OPA oder XACML formulieren und zentral prüfen. ABAC erhöht Flexibilität und Nachvollziehbarkeit bei komplizierten Berechtigungskonstellationen, erfordert aber Disziplin bei der Implementierung, damit keine Lücke übersehen wird.
Logging und Monitoring: Da Prävention nie hundertprozentig ist, sollte man BOLA-Versuche schnell erkennen. Eine Idee ist, API-Logs ins Data Warehouse oder SIEM zu speisen und auf Anomalien zu prüfen. Ungewöhnliche Zugriffsmuster sind verdächtig: Wenn eine Benutzersession in kurzer Zeit sehr viele verschiedene Objekt-IDs anspricht, deutet das auf ID-Bruteforcing hin. Ein Beispiel-SQL (Pseudocode) wird in Listing 10 gezeigt.
Moderne Sicherheitslösungen markieren so etwas automatisch. Zum Beispiel stuft Cloudflare einen Client, der „drastisch viele einzigartige IDs“ abfragt, als verdächtig ein (Risk Score: BOLA Attack). Ein anderes Indiz: Parameter an unerwarteter Stelle. Taucht ein Wert mehrfach in einer Anfrage auf (z.B. KontoNr im Pfad und nochmal als Query-Param), könnte jemand versuchen, alternative Codepfade ohne Auth zu triggern (Parameter Pollution).
Spezifische Test-Tools: OWASP ZAP und ähnliche Tools können per Skript systematisch IDs in Requests austauschen und prüfen, ob unautorisierte Zugriffe gelingen. Solche halbautomatisierten Pentests sollten regelmäßig stattfinden, insbesondere bei Releases mit sicherheitsrelevanten Änderungen. Auch Bug-Bounty- Programme sind hilfreich – IDOR/BOLA ist ein beliebtes Ziel für Security-Researcher, weil es oft übersehen wird und doch leicht aufzuspüren ist (Tabelle 1).
| Anzeichen | Beschreibung/Risiko |
| fortlaufende IDs | Ressourcen-IDs sind einfach und sequenziell (z. B. Benutzer 1000,1001,1002). Risiko: Erleichtert ID-Guessing und massenhaftes Ausprobieren. |
| keine Owner-Prüfung | im Code Endpunkte nutzen vom Client gelieferte IDs, ohne den Besitzer gegen den eingeloggten User zu verifizieren. Risiko: Autorisierung komplett umgehbar (klassischer BOLA-Fall). |
| einseitige Fehlercodes | Bei Zugriff mit falschen IDs kommt immer derselbe Fehler (z. B. stets 403, nie 404). Risiko: Hinweis, dass keine objektspezifische Prüfung erfolgt – legitime, aber fremde IDs werden nicht unterschiedlich behandelt. |
| ungewöhnliche Aufrufmuster | Eine Session ruft sehr viele verschiedene IDs oder fremde Ressourcen auf. Risiko: Hinweis auf IDEnumeration- Angriff (Bruteforcing verschiedener IDs). |
| Parameter Pollution | Gleicher Wert taucht mehrfach in Anfrage auf (z. B. KontoNr im Pfad und als Query-Param). Risiko: Versuch, alternative Pfade/Logik zu nutzen, evtl. mit fehlender Auth (Parameter Pollution). |
| inkonsistente Doku | In der OpenAPI-Doku sind einige sensible Endpunkte nicht als secured markiert. Risiko: Möglicher Design- Lapse – Endpunkt könnte offengelassen worden sein (oder Doku hinkt hinterher). |
Zusammengefasst sollten Entwickler und Architekten ein besonderes Augenmerk auf die folgenden Punkte richten:
Wer für die Security verantwortlich ist, sollte besonders Folgendes im Hinterkopf behalten:
Broken Object Level Authorization mag als Begriff sperrig sein, doch die dahinterstehende Lücke ist simpel und gerade deshalb so gefährlich. Eine vergessene oder falsch implementierte Objektprüfung kann dazu führen, dass vertrauliche Daten in falsche Hände geraten oder Kundenkonten gekapert werden.
Die Beispiele mit dem Warenkorb und den Bankkonten zeigen, wie wichtig es ist, auf allen Ebenen zu kontrollieren, wer was darf. Die gute Nachricht: Mit klarem Bewusstsein und den richtigen Mitteln lässt sich BOLA effektiv verhindern. Sei es durch stringente Codeprüfungen in Spring, clevere JWT Claims plus Gateway Policies oder vorausschauende API Governance – es gibt keinen Grund, dem „Griff in den fremden Warenkorb“ tatenlos zuzusehen.
Am Ende zahlt sich Umsicht aus: Unsere Nutzer verlassen sich darauf, dass „ihr Konto“ auch ihr Konto bleibt – und nicht zum Selbstbedienungsladen für andere wird. Deshalb sollten wir als Entwickler und Sicherheitsverantwortliche stets fragen: Habe ich die Tür zum Datenobjekt fest verschlossen? Wenn ja, können wir beruhigt sagen: In meinem Supermarkt-API bleibt der Warenkorb dort, wo er hingehört, und geschützt vor neugierigen Zugriffen.
Lerne von unseren Autoren in der API Security Schulung.
Online Training
11. - 12. 6.2026
12. - 13. 10.2026
Jetzt anmelden
für 1.340,- €*
Schulungen in Bonn
Jetzt anmelden
für 1.470,- €*