Stell dir vor, du loggst dich in eine E-Commerce-Plattform ein, öffnest deine letzte Rechnung unter https://example.com/api/invoices/1042, dann änderst du aus Neugier die URL in der Adressleiste auf 1043.
Wenn der Server dann den vollständigen Namen, die Wohnadresse und die Steuernummer eines anderen Kunden zurückgibt, hat dieses System eine katastrophale IDOR-Schwachstelle (Insecure Direct Object Reference) – von OWASP als API1: Broken Object Level Authorization (BOLA) eingestuft, die häufigste und gefährlichste Schwachstelle in modernen APIs.
1. Warum entsteht IDOR? Authentication und Authorization werden verwechselt
Fast jeder Backend-Entwickler denkt daran, geschützten Routen eine Authentication-Middleware (requireAuth oder verifyJWT) vorzuschalten. Authentication und Authorization beantworten aber zwei völlig verschiedene Fragen:
- Authentication (
401 Unauthorized): „Wer bist du? Bist du in einem gültigen Konto angemeldet?“ - Authorization (
403 Forbidden): „Gehört dir Ressource #1043 tatsächlich, oder darfst du darauf zugreifen?“
Das verwundbare Code-Muster
// ❌ VULNERABLE: Checks if the user is logged in, but never checks ownership!
app.get('/api/invoices/:id', requireAuth, async (req, res) => {
const invoice = await db.invoices.findUnique({
where: { id: Number(req.params.id) }
});
return res.json(invoice);
});Weil der Angreifer in seinem eigenen, gültigen Konto angemeldet ist, geht requireAuth durch! Mit einem Skript von fünf Zeilen kann er dann von 1 bis 500.000 durchzählen und deine gesamte Datenbank in wenigen Minuten abgreifen.
2. Der UUID-Mythos: „Ich nutze UUIDv4, also bin ich sicher!“
Warnung: Security by Obscurity ist KEINE Sicherheit
Wenn du fortlaufende Integer (1042) durch zufällige UUIDs (f47ac10b-58cc-4372-a567-0e02b2c3d479) ersetzt, wird Brute-Force-Raten schwerer, aber die IDOR-Schwachstelle ist damit nicht behoben! Objekt-IDs tauchen oft in öffentlichen Profilen, Webhook-Payloads, im Browserverlauf oder in GraphQL-Antworten auf. Wenn der Besitz nicht geprüft wird, kann jeder, der die UUID in die Hände bekommt, den Datensatz weiterhin lesen oder löschen.
3. Die echte Lösung: Besitz auf Ebene der Datenbankabfrage erzwingen
Vertraue niemals einer ID aus req.params oder req.body für sich allein. Binde die Abfrage immer direkt an die Identität aus der Session bzw. dem JWT des authentifizierten Nutzers (req.user.id):
// ✅ SECURE: Combines object ID with the authenticated user's ID in the WHERE clause
app.get('/api/invoices/:id', requireAuth, async (req, res) => {
const invoice = await db.invoices.findFirst({
where: {
id: req.params.id,
userId: req.user.id // <- Strict Object-Level Authorization!
}
});
if (!invoice) {
// Return 404 (or 403) so attackers cannot even probe if the ID exists
return res.status(404).json({ error: 'Invoice not found' });
}
return res.json(invoice);
});Wenn du PostgreSQL einsetzt, kannst du noch einen Schritt weitergehen und Row-Level Security (RLS) auf Ebene der Datenbank-Engine erzwingen, sodass eine Abfrage mathematisch gar keine Zeilen eines anderen Mandanten (Tenants) zurückgeben kann.
Interaktive Roadmap & Tool
Junior Backend Developer 2026
Was du wirklich wissen musst, um 2026 eine Junior-Backend-Stelle zu bekommen, und was warten kann – ohne die Buzzword-Fallen aus echten Stellenanzeigen.
SQL oder NoSQL?
Beantworte sechs Fragen, und ich sage dir, welche Art von Datenbank zu deinem Projekt passt und warum.
Als Nächstes lesen
- CZDEDurch den Dschungel: Welche Backend-Sprache und welches Framework solltest du 2026 wählen?
Durch den Dschungel: Welche Backend-Sprache und welches Framework solltest du 2026 wählen?
25. Oktober 2026 · 3 Min. Lesezeit - CZDEPHP CVE-2026-115560 im Detail: Session Upload Progress und Serialize-Handler-Mismatch erklärt
PHP CVE-2026-115560 im Detail: Session Upload Progress und Serialize-Handler-Mismatch erklärt
24. Oktober 2026 · 3 Min. Lesezeit