19. Okt. 2025·8 Min. Lesezeit

Autorisierungsfehler in CRUD‑Apps: Rollen, Mandanten und Routen prüfen

Lernen Sie, Autorisierungsfehler in CRUD‑Apps zu finden und zu beheben: prüfen Sie Rollen, Mandanten‑Scoping und API‑Routen, bevor Nutzer auf fremde Daten zugreifen.

Autorisierungsfehler in CRUD‑Apps: Rollen, Mandanten und Routen prüfen

Warum sich Nutzer gegenseitig Daten sehen können

Ein Autorisierungsfehler tritt auf, wenn Ihre App die falsche Person auf etwas zugreifen lässt, obwohl sie eingeloggt ist. Das unterscheidet sich von der Authentifizierung, die nur beantwortet, wer jemand ist (Anmeldung, Passwort, Magic Link). Autorisierung beantwortet eine andere Frage: Jetzt, wo Sie angemeldet sind – was dürfen Sie tun und welche Datensätze gehören Ihnen?

Wenn die Autorisierung auch nur leicht fehlerhaft ist, sind die Folgen selten harmlos. Ein Nutzer könnte die Rechnungen eines anderen Kunden sehen, das Profil einer anderen Person bearbeiten oder Datensätze löschen, von deren Existenz er nichts wissen sollte. In CRUD‑Apps ist das besonders gefährlich, weil alles in der UI normal aussehen kann, bis jemand eine URL ändert, eine Anfrage im Browser anpasst oder einen ungeschützten API‑Endpunkt aufruft.

Das zeigt sich häufig in Dashboards, Admin‑Panels und Kundenportalen. Diese Apps wachsen schnell: neue Bildschirme, neue Endpunkte, neue Filter. Ein fehlender Mandantenfilter in einer Tabellenabfrage und plötzlich erscheinen Datensätze eines anderen Accounts.

Ein häufiges Muster hinter diesen Fehlern ist Privilege Creep. Berechtigungen weiten sich schleichend aus. Man beginnt mit „Nutzer sehen nur ihre eigenen Sachen“, fügt Support‑Tools hinzu, dann eine Admin‑Ansicht, dann einen Export‑Endpunkt. Bei jedem Schritt entsteht ein neuer Ort, an dem Prüfungen verschoben oder vergessen werden können.

Ein realistisches Beispiel: Ein Kundenportal hat einen Button „Beleg herunterladen“. Der Endpunkt nimmt eine Beleg‑ID. Wenn der Server nur prüft „Nutzer ist eingeloggt“, aber nicht „Beleg gehört zu diesem Mandanten“, kann ein Nutzer eine ID erraten oder wiederverwenden und den Beleg eines anderen herunterladen.

Diese Fehler überleben, weil eine der Grundlagen fehlt:

  • eine klare Regel für Besitz (Benutzer, Team oder Mandant)
  • konsistente Prüfungen bei jedem Lesen und Schreiben
  • Tests, die bewusst Cross‑Account‑Zugriff versuchen

Teams stoßen oft auf dieses Problem bei KI‑generierten Prototypen. Authentifizierung funktioniert, aber Autorisierungsregeln werden nicht über alle Routen und Abfragen hinweg konsistent angewendet. Dann wird aus „es hat im Demo funktioniert“ schnell ein Produktiv‑Datenleck.

Rollen, Berechtigungen und Mandanten – ein einfaches Modell

Die meisten Datenlecks passieren, weil die App sich nicht auf eine einfache Frage einigen kann: Wer ist dieser Nutzer und was darf er gerade tun?

Beginnen Sie damit, die Identitätsstücke zu benennen, die Ihre App bei jeder Anfrage tragen sollte:

  • User ID: die Person, die die Anfrage stellt.
  • Rolle: die Art des Zugriffs, den sie hat (Viewer, Editor, Admin).
  • Tenant ID: die Organisation/Workspace/Projekt, zu dem sie gehört.

Eine Rollenprüfung beantwortet: „Darfst du diese Aktion ausführen?“. Mandanten‑Scoping beantwortet: „Auf welche Datensätze darfst du zugreifen?“. Meist braucht man beides.

Zum Beispiel könnte role = editor das „Rechnungen bearbeiten“ erlauben. Ohne Mandanten‑Scoping kann aber eine Anfrage wie „update invoice by id“ eine Rechnung in einem anderen Workspace aktualisieren. Der Nutzer hatte die Berechtigung, nur nicht für diesen Mandanten.

Rollenprüfungen vs. Mandanten‑Scoping

Rollenprüfungen sind meist einfach: Darf dieser Nutzer löschen, kann er Teammitglieder einladen, hat er Zugriff auf die Abrechnungsansicht. Mandanten‑Scoping sind die Leitplanken, die jede Lese‑ und Schreiboperation innerhalb der richtigen Organisation/Workspace halten.

Kurz gemerkt:

  • Rolle entscheidet, was Sie tun können.
  • Mandant entscheidet, wo Sie es tun können.

Die „Admin“-Falle

„Admin“ ist ein Bereich, in dem viele Apps unvorsichtig werden. Es gibt einen großen Unterschied zwischen:

  • Tenant‑Admin: kann Nutzer und Einstellungen innerhalb des eigenen Workspaces verwalten.
  • Global‑Admin: kann alles über alle Mandanten hinweg sehen (selten, meist internes Personal).

Wenn Ihr Code irgendeinen admin als global behandelt, kann ein normaler Kunden‑Admin plötzlich die Daten anderer Kunden sehen. Das passiert besonders oft, wenn „admin“ als ein einzelnes Flag implementiert ist, ohne den Umfang zu klären.

Noch ein Hinweis: UI‑only‑Einschränkungen sind keine Sicherheit. Buttons ausblenden hilft der Usability, schützt aber nicht die Daten. Wenn ein API‑Endpunkt eine Anfrage akzeptiert, kann ein Nutzer ihn direkt aufrufen, selbst wenn die UI die Option nie anzeigt.

Wo Autorisierung durchgesetzt werden sollte

Autorisierung ist nicht eine einzelne Prüfung, die man irgendwo im Backend hinzufügt. Bei den meisten Lecks gab es eine Prüfung, aber sie lebte am falschen Ort oder deckte nur eine Schicht ab.

Denken Sie an Durchsetzung als Stapel (Stack). Jede Schicht verhindert eine andere Art von Fehler, besonders in schnell entwickeltem Code, in dem Handler kopiert und angepasst werden.

Denken Sie in Schichten (nicht in einem einzelnen Schutz)

Beginnen Sie an der Peripherie, wo Anfragen in Ihr System eintreten. Wenn ein Endpunkt von einer Rolle nicht aufgerufen werden darf, blocken Sie ihn, bevor Arbeit passiert. Das verhindert „Ich kann die URL direkt aufrufen“‑Probleme.

Als Nächstes erzwingen Sie den Zugriff auf das spezifische Objekt. Es reicht nicht zu wissen, dass jemand eingeloggt ist. Sie müssen beweisen, dass der Datensatz zu seinem Mandanten gehört (oder dass er einen gültigen bereichsübergreifenden Grund hat, z. B. eine interne Support‑Rolle).

Schließlich kontrollieren Sie, was sie in dem Datensatz sehen oder ändern dürfen. Viele Apps begrenzen welche Reihen zurückgegeben werden, leaken aber dennoch sensible Felder (interne Notizen) oder akzeptieren Änderungen an serververwalteten Feldern (role, plan, tenantId).

Eine praktische Platzierung von Prüfungen:

  • Routen‑Zugriff: wer darf diesen Endpunkt überhaupt aufrufen
  • Record‑Zugriff: welche konkreten Objekte dürfen gelesen oder geändert werden
  • Action‑Zugriff: was dürfen sie tun (lesen vs bearbeiten vs löschen vs exportieren)
  • Feld‑Zugriff: welche Eigenschaften werden zurückgegeben oder akzeptiert

Eine Regel, die die meisten Lecks auffängt

Gehen Sie davon aus, dass Clients untrusted sind – sogar Ihr eigenes Frontend. Wenn eine Anfrage eine ID, ein tenantId oder eine Rolle enthält, behandeln Sie das als Hinweis, nicht als Fakt. Der Server sollte Identität und Mandant aus der Session oder dem Token ableiten und diese auf jede Abfrage und jeden Schreibvorgang anwenden.

Beim Code‑Review suchen Sie nach Stellen, an denen nur eine Schicht existiert. Wenn eine Route Daten zurückgibt, fragen Sie: Haben wir Zugriff auf Routen‑, Record‑, Action‑ und Feld‑Level durchgesetzt, oder haben wir nur eine Schicht und hoffen, dass das reicht?

Beginnen Sie mit einer Berechtigungskarte, der Sie wirklich folgen können

Die meisten Autorisierungsfehler beginnen als Papierkramproblem. Niemand kann an einem Ort beantworten: „Wer darf was, an welchen Datensätzen?“ Also werden Prüfungen ad‑hoc hinzugefügt, Routen driften und Berechtigungen schleichen sich langsam ein.

Eine Berechtigungskarte ist eine einfache, klar formulierte Referenz, die Sie offenlegen können, während Sie Routen und Abfragen prüfen. Halten Sie sie so knapp, dass auch ein nicht‑technisches Teammitglied sie lesen und eine merkwürdige Regel erkennen kann.

1) Schreiben Sie die Rollen‑zu‑Aktionen‑Tabelle (keine Codewörter)

Beginnen Sie mit den Rollen, die tatsächlich in Produktion sind, nicht mit denen, die Sie planen. Ordnen Sie diese Rollen Aktionen mit einfachen Verben zu: ansehen, erstellen, bearbeiten, löschen, einladen, exportieren, Rechnung ändern.

RolleKann ansehenKann erstellenKann bearbeitenKann löschenKann Nutzer verwalten
MemberEigene ItemsJaEigene ItemsNeinNein
ManagerOrganisations‑ItemsJaOrganisations‑ItemsEingeschränktMitglieder einladen
AdminOrganisations‑ItemsJaOrganisations‑ItemsJaVollzugriff

Wenn Sie eine Regel nicht ohne Ausnahmen beschreiben können, ist das ein Zeichen, dass Sie eine zusätzliche Rolle oder ein weiteres Konzept wie „Owner“ brauchen.

2) Markieren Sie jede Mandanten‑Grenze und den Schlüssel, der sie scopet

Viele Apps haben mehr als eine Grenze: Account, Organisation, Workspace, Projekt. Schreiben Sie jede auf und wählen Sie den Scoping‑Schlüssel (z. B. org_id oder workspace_id). Listen Sie dann jede Ressource und den Mandanten‑Schlüssel, der immer vorhanden sein muss.

Konzentrieren Sie sich auf drei Fragen:

  • Was scopet jede Ressource (org_id, workspace_id, project_id)?
  • Woher kommt dieser Scope (Session, Token, URL, Request‑Body)?
  • Welche Grenzen dürfen niemals überschritten werden?

Definieren Sie abschließend „Owner“ pro Ressource. Owner ist nicht universell. Ein Kommentar gehört dem Ersteller, eine Aufgabe dem zugewiesenen Nutzer, eine Rechnung dem Account.

Ein konkretes Beispiel: Wenn „Owner“ für ein Dokument „Ersteller“ bedeutet, darf ein Manager nicht automatisch jedes Dokument bearbeiten, es sei denn, Ihre Tabelle sagt explizit, dass Manager alle Organisations‑Dokumente bearbeiten dürfen. Dieses Detail verhindert einen häufigen Fehler: Rollenprüfungen zu verwenden, um Mandanten‑Scoping zu umgehen.

Schritt‑für‑Schritt‑Audit: API‑Routen und Server‑Handler

Stop users seeing others’ data
If users can reach the wrong records, we’ll repair the checks at route, record, and query level.

Autorisierungsfehler verbergen sich oft an langweiligen Stellen: Routen, die Sie vergessen haben, der Handler, der „einfach einen Datensatz aktualisiert“, oder der Admin‑Endpunkt, der nie echte Prüfungen bekam. Ein Audit ist hauptsächlich Inventarisierung plus Disziplin.

1) Inventarisieren Sie jede Route (ja, alle)

Listen Sie jede API‑Route, die Ihre App aussetzt, inklusive interner, Admin‑ und „temporärer“ Endpunkte, die während Prototyping hinzugefügt wurden. Solche Experimente bleiben oft bestehen und sind weiterhin erreichbar.

Wählen Sie eine einzige Quelle der Wahrheit (Ihre Router‑Datei, Framework‑Routenordner oder API‑Gateway‑Konfiguration) und erstellen Sie eine einfache Tabelle. Finden Sie Routen, die nicht mehr genutzt werden? Markieren Sie sie zur Entfernung, prüfen Sie sie aber vorher.

2) Schreiben Sie für jede Route: Actor, Action, Resource, Tenant Boundary

Für jede Route formulieren Sie einen Satz in klarem Deutsch:

  • Actor: Wer ruft sie auf (angemeldeter Nutzer, Organisations‑Admin, Systemjob)?
  • Action: Was tun sie (lesen, erstellen, aktualisieren, löschen)?
  • Resource: Welches Objekt wird berührt (Rechnung, Projekt, Nutzer, Datei)?
  • Tenant Boundary: Welcher Container muss übereinstimmen (org_id, workspace_id, account_id)?

Wenn Sie die Regel nicht in einem Satz beschreiben können, ist der Code meist inkonsistent.

3) Verifizieren Sie, dass Mandanten‑Scope auf dem Server geprüft wird, nicht auf dem Client

Prüfen Sie, dass der Handler den Mandanten aus der authentifizierten Session (oder serverseitigen Token‑Claims) ableitet, nicht aus Request‑Body‑Feldern oder Query‑Parametern.

Ein gängiges Warnsignal: Die Anfrage enthält orgId und der Server vertraut dem Wert. Sicherer ist: Lesen Sie org_id aus der User‑Session und erzwingen Sie ihn in jeder Abfrage und Mutation.

4) Bestätigen Sie, dass auch Schreibvorgänge scoped sind (nicht nur Lesefunktionen)

Teams scopeen oft Listen‑Seiten, vergessen aber Updates und Deletes. Achten Sie auf Endpunkte wie:

  • PATCH /projects/:id
  • DELETE /invoices/:id
  • POST /members/:id/role

Wenn der Handler nur nach id updatet, ist das ein sofortiges Cross‑Tenant‑Risiko. Die Prüfung muss lauten: „Datensatz mit dieser id UND diesem Mandanten gehört diesem Actor.“

5) Achten Sie auf Routen, die IDs akzeptieren und Datensätze „nackt“ fetchen

Jede Route, die eine id nimmt, ist ein Hotspot. Das gefährliche Muster ist:

  • holen nach id
  • locker prüfen (oder gar nicht)
  • zurückgeben oder mutieren

Stattdessen sollte die Autorisierung als Teil des Lookups erzwungen werden. Gehört der Datensatz nicht zum Mandanten des Actors (oder hat der Actor keine Berechtigung), sollte das Lookup fehlschlagen.

Schritt‑für‑Schritt‑Audit: Datenbankabfragen und ORM‑Filter

Autorisierung kann im Controller korrekt aussehen und dann stillschweigend in der Datenbank‑Schicht kaputtgehen. Wenn eine Abfrage Datensätze eines anderen Mandanten zurückgeben kann, wird die App sie irgendwann zeigen — vielleicht in Suche, Exporten oder Randfällen, die Sie nicht getestet haben.

Finden Sie zuerst jede Stelle, an der Ihre App „viele Reihen“ liest (Listen, Suche, Admin‑Tabellen, Hintergrundjobs). Fragen Sie bei jeder Abfrage: Wo wird der Mandanten‑Filter angewandt und kann er umgangen werden?

1) Listen‑Abfragen auditieren (viele Reihen)

Öffnen Sie jeden Listen‑Endpunkt und verfolgen Sie ihn bis zum ORM‑Aufruf. Mandanten‑Constraints müssen Teil der Datenbankabfrage sein, nicht später im Speicher gefiltert.

Eine Checkliste, die die meisten Lecks findet:

  • Mandanten‑Scope ist in der DB‑Abfrage (nicht nach dem Holen gefiltert).
  • Pagination verwendet dieselbe scoped Abfrage (Count und Datenabfrage stimmen überein).
  • Suchbegriffe werden mit Mandanten‑Scope per AND kombiniert, nicht per OR.
  • Sortier‑ und Cursor‑Logik darf nicht auf eine ungescoped Basisabfrage zurückfallen.
  • „Include related data“ lädt keine cross‑tenant Kinder nach.

2) Detail‑Abfragen auditieren (einzelne Reihe)

Detail‑Endpunkte sollten niemals nur per Record‑ID nachschlagen. Das Lookup muss tenantId und id enthalten (oder einen anderen tenant‑gebundenen eindeutigen Schlüssel). Wenn Ihr ORM Helfer wie findUnique(id) hat, betrachten Sie diese mit Misstrauen, es sei denn, der Unique‑Key enthält tenantId.

Bevorzugen Sie „findFirst where tenantId = X and id = Y“ statt „find by id then check tenant later“. Das zweite Muster vergisst man leicht in einem Handler.

3) Joins, Exporte und „Special“‑Abfragen

Joins sind ein häufiger Ort, an dem Mandanten‑Scoping verschwindet. Eine Abfrage kann scoped starten, dann eine Join auf eine andere Tabelle machen und auf dem falschen Tenant‑Feld filtern (oder gar nicht filtern).

Prüfen Sie außerdem Reports, Exporte und Hintergrundjobs. Diese umgehen oft normalen API‑Code und benötigen dieselben Scoping‑Regeln auf Query‑Ebene.

Häufige Fallen, die Privilegien‑Schleichfahrt verursachen

Get a clear permission audit
We diagnose your codebase and list every risky route, query, and permission gap.

Privilege Creep passiert selten, weil jemand bewusst „alles erlauben“ schreibt. Er entsteht, weil kleine Abkürzungen sich summieren: Eine Route vertraut der UI, eine andere geht davon aus „admin“ sei global, eine dritte vergisst einen Hintergrundtask.

Fehler 1: Dem Client vertrauen (UI‑Flags, versteckte Buttons)

Wenn ein Nutzer eine Anfrage im Browser bearbeiten kann, kann er alles senden, was der Browser schickt. Ein versteckter „Delete“‑Button oder ein client‑seitiges role: "admin"‑Feld ist kein Schutz. Der Server muss auf Basis der angemeldeten Identität entscheiden.

Eine verbreitete Variante: Die UI blendet „Invoice bearbeiten“ aus, es sei denn, Sie sind Manager, aber der API‑Endpunkt prüft nur, dass Sie angemeldet sind. Jeder kann die Route direkt aufrufen und die Rechnung eines anderen aktualisieren.

Fehler 2: „isAdmin“ ohne Mandanten‑Scope verwenden

„Admin“ ist bedeutungslos, wenn Sie nicht sagen: Admin von was? In Multi‑Tenant‑Apps sollten die meisten Rollen auf einen Mandanten begrenzt sein. Die Falle ist Logik wie „if isAdmin, allow“, die versehentlich Zugriff über alle Mandanten gewährt.

Eine sichere mentale Prüfung: Jede Autorisierungsentscheidung sollte zwei Fragen beantworten: „Wer ist dieser Nutzer?“ und „Zu welchem Mandanten gehören diese Daten?“. Wenn eine Antwort unklar ist, sind Sie einen Refactor entfernt von Cross‑Tenant‑Zugriff.

Fehler 3: Prüfen, nachdem der Datensatz geladen wurde

Viele Lecks passieren, weil Code zuerst den Datensatz holt und erst dann prüft, ob der Nutzer Zugriff hat. Selbst wenn Sie die Antwort blockieren, können Sie durch Nebenwirkungen noch Informationen leaken (unterschiedliche 404 vs 403, Timing, oder geladene verwandte Daten).

Bevorzugen Sie Prüfungen, die verhindern, dass der Datensatz überhaupt geladen wird, indem Sie Tenant‑ und Ownership‑Regeln direkt in die Abfrage einbauen.

Fehler 4: „Seitentüren“ vergessen (Jobs, Webhooks, Downloads)

Hintergrundjobs, Cron‑Tasks, Webhook‑Handler und Dateidownload‑Endpunkte überspringen oft die übliche Middleware und enden mit schwächeren Prüfungen. Wenn ein Job „alle Rechnungen“ verarbeitet ohne Mandanten‑Filter, kann er falsche Kundendaten mailen oder exportieren.

Für diese Pfade sollten Sie beantworten können: Authentifiziert der Aufrufer (oder validiert der Webhook)?, Erzwingt er Mandanten‑Scoping bei jeder Abfrage?, Loggt er, was er berührt hat (tenant id, record id, actor)?

Fehler 5: Geteilte Helfer mit unklaren Defaults

Ein Helfer wie getUserProjects(userId) klingt sicher, bis jemand ihn in einem Admin‑Screen wiederverwendet und annimmt, er gebe „alle Projekte“ zurück. Oder schlimmer: Ein Helfer defaultet zu „kein Mandanten‑Filter“, wenn tenantId fehlt.

Gute Helfer schlagen laut fehl. Wenn tenantId aus Sicherheitsgründen erforderlich ist, machen Sie es zur Pflicht im Funktions‑Signature und werfen Sie einen Fehler, wenn es fehlt.

Ein realistisches Beispiel: eine fehlerhafte Route, ein großes Leck

Stellen Sie sich vor, Sie haben eine Support‑Agent‑Rolle. Diese darf Support‑Tickets sehen, aber nur für ihre eigene Organisation (ihren Mandanten). Das klingt einfach, aber eine sorglose Endpoint‑Implementierung reicht, um die Regel zu brechen.

Der Fehler: Es gibt eine Route GET /api/tickets/:ticketId. Der Handler prüft, dass der Nutzer angemeldet ist, holt dann das Ticket per ID. Er prüft nie den Mandanten.

// Unsafe: fetches by ID only
const ticket = await db.ticket.findUnique({
  where: { id: ticketId }
});

return ticket;

Warum das Daten leakt: Ticket‑IDs tauchen oft an Orten auf, auf die Nutzer Zugriff haben, z. B. Browser‑URLs, E‑Mail‑Benachrichtigungen, Logs in Support‑Tools oder exportierte CSVs. Selbst wenn das nicht der Fall ist, verwenden viele Apps vorhersehbare IDs (inkrementelle Zahlen, kurze UUIDs, die aus der UI kopiert werden). Ein neugieriger oder böswilliger Nutzer kann eine ID tauschen und ein Ticket einer anderen Organisation sehen.

Das ist ein häufiger Fehler: Der Code geht davon aus, dass das Kennen einer ID Beweis dafür ist, dass man den Datensatz sehen darf.

Ein sicherer Handler macht zwei Dinge anders:

  1. Er scoped die Abfrage auf den Mandanten (Org) aus der Session.
  2. Er prüft die Rolle für die Aktion (Ticket ansehen).
// Safer: enforce role + tenant scoping
if (user.role !== "support_agent") throw new Error("Forbidden");

const ticket = await db.ticket.findFirst({
  where: { id: ticketId, orgId: user.orgId }
});

if (!ticket) throw new Error("Not found");
return ticket;

Beachten Sie das Verhalten „Not found“. Es vermeidet die Bestätigung, dass ein Ticket in einer anderen Organisation existiert.

Um die Änderung zu verifizieren, halten Sie den Test einfach:

  • Erstellen Sie zwei Orgas, Org A und Org B.
  • Legen Sie ein Ticket in Org B an.
  • Melden Sie sich als Support‑Agent in Org A an.
  • Rufen Sie den Endpunkt mit der Ticket‑ID von Org B auf.
  • Bestätigen Sie, dass Sie „Not found“ (oder 404) erhalten und keine Ticketdaten zurückkommen.

Schnelle Prüfungen, die Sie vor einem Release ausführen können

Make authorization reliable
Turn an AI-built CRUD prototype into production-ready code with verified authorization rules.

Die meisten Autorisierungsfehler treten in der letzten Meile auf: ein neuer Endpunkt, eine „hilfreiche“ Admin‑Abkürzung oder eine Abfrage, die Mandanten‑Scoping vergessen hat. Diese Checks sind einfach und wiederholbar.

Der Zwei‑Mandanten Smoke‑Test (10 Minuten)

Erstellen Sie zwei Testaccounts, die normal aussehen, aber unterschiedlichen Mandanten angehören (Firma A und Firma B). Geben Sie ihnen realistische Daten, damit Sie erkennen, was wohin gehört.

Mischen Sie gezielt Identifikatoren:

  • Kopieren Sie eine Datensatz‑ID aus Tenant A und versuchen Sie, sie in Tenant B zu lesen.
  • Versuchen Sie ein Update mit Tenant A’s ID, während Sie in Tenant B eingeloggt sind.
  • Versuchen Sie ein Delete mit Tenant A’s ID, während Sie in Tenant B eingeloggt sind.
  • Wenn Ihre App Soft‑Deletes nutzt, testen Sie auch Restore/Undelete.
  • Wiederholen Sie das für Child‑Objekte (Kommentare, Rechnungen, Dateien), die anders scoped sein können.

Wenn einer dieser Versuche Erfolg hat oder echte Daten zurückgibt, haben Sie wahrscheinlich fehlende Mandantenfilter oder eine Rollenprüfung, die nur in der UI läuft.

Vergessen Sie nicht Bulk‑ und Seitentüren‑Funktionen

Lecks passieren oft außerhalb der Haupt‑CRUD‑Screens. Eine Listen‑API kann scoped sein, aber der Export nicht. Ein Dateidownload überspringt Prüfungen, weil es „nur eine URL“ ist.

Machen Sie einen schnellen Durchlauf über:

  • Listenseiten mit Filtern, Suche, Sortierung und Pagination (versuchen Sie nach einem bekannten Wert des anderen Mandanten zu suchen).
  • Export‑Endpunkte (CSV, PDF, Reports) und Hintergrundjobs, die diese erzeugen.
  • Dateidownloads und Vorschauen (signed URLs, Attachment‑IDs, Image‑Endpoints).
  • Aktivitätslogs, Admin‑Dashboards und „recent items“ Widgets.
  • Jede Route, die eine ID im Pfad akzeptiert, auch wenn die UI sie nie anzeigt.

Stellen Sie außerdem sicher, dass Ihre Admin‑Rollen so scoped sind, wie Sie es beabsichtigen. Ein „Tenant‑Admin“ sollte nicht wie ein „Global‑Admin“ handeln, nur weil es gerade praktisch ist.

Nächste Schritte: Machen Sie Autorisierung schwer zu brechen

Autorisierungsfehler passieren selten, weil Leute sich nicht kümmern. Sie passieren, weil Prüfungen verstreut sind, Mandantenfilter leicht zu vergessen sind und neue Features schneller shipped werden als die Regeln aktualisiert werden. Das Ziel ist, den sicheren Weg zum einfachsten Weg zu machen.

Setzen Sie eine Leitplanke vor alles

Verwenden Sie eine konsistente Autorisierungsschicht überall: Middleware, einen Policy‑Helper oder einen Service, den jeder Handler vor Arbeit aufruft. Wenn Sie sich merken müssen, welche Routen „Prüfungen brauchen“, werden Sie eine vergessen.

Eine nützliche Faustregel: Routen‑Handler sollten keine benutzerdefinierte Berechtigungslogik enthalten. Sie sollten eine Policy‑Schicht fragen (z. B. „Darf dieser Nutzer diese Rechnung aktualisieren?“) und dann fortfahren.

Schnelle Änderungen, die Fehler reduzieren:

  • Erstellen Sie einen Policy‑Helper (oder Middleware) pro Ressource: read, create, update, delete.
  • Machen Sie Mandanten‑Scoping zum Default (z. B. ein scoped Query‑Helper, der immer tenantId anwendet).
  • Verweigern Sie standardmäßig, wenn Daten fehlen oder unklar sind (kein Mandant, keine Rolle, keine Ownership).
  • Loggen Sie Autorisierungs‑Denials mit genug Kontext zum Debuggen (User, Tenant, Resource, Action).

Backen Sie Mandanten‑Scoping in den Datenzugriff ein

Zentralisieren Sie Mandanten‑Scoping, damit es schwer zu vergessen ist. Der beste Ort ist dort, wo Queries gebaut werden, nicht dort, wo Antworten zurückgegeben werden.

Statt überall where: { id } zu schreiben, bieten Sie einen Helfer an, der bereits tenantId einschließt. Wenn ein Entwickler versucht, ihn zu umgehen, sollte das im Code‑Review auffallen.

Wertvolle Tests fangen die Regressionen ein, die am meisten zählen:

  • Cross‑tenant read schlägt fehl (User A kann User B’s Datensatz per ID nicht abrufen).
  • Cross‑tenant write schlägt fehl (User A kann User B’s Datensatz nicht updaten/löschen).
  • Rollendowngrade ist sicher (ein Nutzer, der Admin‑Rechte verliert, behält keinen Admin‑Zugriff).
  • Create ist scoped (neue Datensätze werden mit dem aktuellen Mandanten versehen).

Wenn Sie eine KI‑generierte Codebasis übernommen haben und nicht sicher sind, ob Mandanten‑Scoping und Rollenprüfungen konsequent angewandt werden, kann ein fokussiertes Audit Tage der Fehlersuche sparen. FixMyMess (fixmymess.ai) spezialisiert sich darauf, diese Arten von Autorisierungs‑Lücken zu diagnostizieren und zu reparieren, besonders die „id‑only“ Handler und ungescoped Queries, die bis zur Produktion unauffällig bleiben.

Häufige Fragen

Why can users see someone else’s data even though login works?

Es ist ein Autorisierungsproblem, nicht eines der Authentifizierung. Nutzer können komplett angemeldet sein, aber der Server prüft nicht konsequent, ob der angefragte Datensatz zu ihrem Benutzer/Team/Mandanten gehört, bevor er ihn zurückgibt.

What’s the difference between authentication and authorization?

Authentifizierung beantwortet „Wer bist du?“. Autorisierung beantwortet „Was darfst du tun und welche Datensätze gehören dir?“. Viele Cross‑Account‑Lecks entstehen, weil Apps nur prüfen, dass ein Nutzer angemeldet ist, und dann Daten per ID abrufen, ohne Besitz oder Mandanten‑Scope zu prüfen.

Do I need role checks, tenant scoping, or both?

Rollenprüfungen bestimmen, welche Aktionen ein Nutzer ausführen darf (z. B. „Rechnungen bearbeiten“). Mandanten‑Scoping bestimmt, wo er diese Aktionen ausführen darf (z. B. „nur innerhalb dieses Workspaces“). In der Regel brauchen Sie beides: die Rolle sagt, was, der Mandant sagt, wo.

Which endpoints are most likely to leak cross-tenant data?

Jeder Endpunkt, der eine ID akzeptiert, ist ein Hotspot — besonders Download‑, Export‑ und Detail‑Routen. Wenn der Handler allein per id sucht, kann ein Nutzer IDs austauschen und möglicherweise Daten eines anderen Mandanten sehen.

If the UI hides admin features, is that enough security?

Nein. Buttons ausblenden verbessert nur die Usability, schützt aber nicht die API. Jeder sollte Endpoints direkt aufrufen können; der Server muss Berechtigungen und Mandanten‑Besitz für jeden Lese‑ und Schreibzugriff durchsetzen.

What’s the “admin trap” and how do I avoid it?

„Admin“ braucht eine Eingrenzung. Ein Tenant‑Admin darf nur innerhalb seines Mandanten verwalten; ein Global‑Admin darf alles sehen und sollte selten sein. Wenn Ihr Code admin pauschal als global interpretiert, kann das unbeabsichtigt Cross‑Tenant‑Zugriff gewähren.

Why is it risky to load a record first and check access afterward?

Weil bereits das Laden des Datensatzes Informationen preisgeben kann (unterschiedliche Fehlermeldungen, Timing, oder geladene verwandte Daten). Besser ist es, Tenant‑ und Ownership‑Regeln direkt in der Datenbankabfrage zu haben, damit unautorisierte Datensätze gar nicht erst geholt werden.

Should the server trust tenantId or role sent from the client?

Leiten Sie Identität und Mandant aus der serverseitigen Session oder Token‑Claims ab und wenden Sie diese auf jede Abfrage/Mutation an. Behandeln Sie tenantId, role oder userId vom Client bestenfalls als Hinweis, niemals als vertrauenswürdige Quelle.

What’s the fastest way to smoke test for authorization leaks?

Erstellen Sie zwei Konten in zwei verschiedenen Mandanten mit deutlich unterschiedlichem Test‑Datensatz. Loggen Sie sich in Mandant B ein und versuchen Sie, Datensätze von Mandant A mit wiederverwendeten IDs zu lesen/aktualisieren/löschen. Gelingt das, haben Sie eine Scoping‑Lücke.

Why do AI-generated CRUD apps have so many authorization bugs, and what can I do?

AI‑generierte Prototypen bringen oft Authentifizierung schnell zum Laufen, wenden aber Autorisierungsregeln inkonsistent an — besonders bei kopierten Handlern und temporären Endpunkten. Wenn Sie so einen Code geerbt haben und schnell produktionssicher werden wollen, kann FixMyMess (fixmymess.ai) eine fokussierte Prüfung durchführen und id‑only‑Handler, ungescoped Queries sowie Rollen/Mandanten‑Fehler innerhalb kurzer Zeit beheben.