TL;DR
OAuth 2.0 und OIDC sind de-facto-Standards für delegierte Autorisierung, enthalten aber häufige Implementierungsfehler mit kritischen Folgen. Typische Schwachstellen sind offene Redirect-URIs, fehlender CSRF-Schutz durch den state-Parameter, das Fehlen von PKCE für SPAs und mobile Apps sowie unsichere Token-Speicherung in localStorage. Access Tokens gehören in HTTPOnly-Cookies oder in Speicher ohne XSS-Zugriff. Refresh-Token-Rotation, kurze Token-Laufzeiten und Server-side Token-Invalidierung schließen die häufigsten Angriffsvektoren.
Diese Zusammenfassung wurde KI-gestützt erstellt (EU AI Act Art. 50).
Inhaltsverzeichnis (4 Abschnitte)
OAuth 2.0 ist überall: "Login with Google", "Verbinde mit GitHub", API-Zugriffe für Drittanbieter. Richtig implementiert ist OAuth sicher und bequem. Falsch implementiert entstehen kritische Sicherheitslücken - von Account-Takeover bis zu vollständiger API-Kompromittierung.
OAuth 2.0 Grundlagen - was tatsächlich passiert
OAuth 2.0 Rollen:
Resource Owner: Der User der Zugriffsrechte erteilt
Client: Die Anwendung die Zugriff beantragt (z.B. Kalender-App)
Authorization Server: Stellt Tokens aus (Entra ID, Keycloak, Auth0)
Resource Server: API die mit Tokens geschützt ist (Google Calendar API)
Authorization Code Flow (Standard für Web-Apps):
1. User klickt "Login mit Google"
2. Client → Authorization Server:
GET /authorize?
response_type=code&
client_id=my-app-123&
redirect_uri=https://myapp.com/callback&
scope=email+calendar.read&
state=RANDOM-CSRF-TOKEN ← WICHTIG!
3. User meldet sich an + genehmigt Zugriff
4. Authorization Server → Client (Redirect):
GET https://myapp.com/callback?code=AUTH_CODE&state=RANDOM-CSRF-TOKEN
5. Client → Authorization Server (Back-Channel):
POST /token
code=AUTH_CODE&
redirect_uri=https://myapp.com/callback&
client_id=my-app-123&
client_secret=MY-SECRET ← Nur der echte Client kennt das!
6. Authorization Server → Client:
{"access_token": "eyJ...", "expires_in": 3600}
7. Client → Resource Server:
GET /calendar/events
Authorization: Bearer eyJ...
Warum Authorization Code Flow sicher ist:
→ Auth Code geht über Browser (unsicher), aber nutzlos ohne Client Secret
→ Access Token geht nur via Server-zu-Server (nie zum Browser)
→ state-Parameter: verhindert CSRF!
Die wichtigsten OAuth-Sicherheitsprobleme
Schwachstelle 1: Offene Weiterleitungen (Open Redirects)
Problem:
Authorization Server erlaubt beliebige redirect_uri:
Angriff:
GET /authorize?
response_type=code&
client_id=my-app&
redirect_uri=https://evil.com/steal ← beliebige URL erlaubt!
&state=...
Ergebnis: Authorization Code wird an Angreifer gesendet!
→ Auth Code + Client Secret = vollständiger Account-Takeover
Schutz:
→ redirect_uri MUSS exakt vorregistriert sein
→ Authorization Server muss Exact-Match prüfen (kein Wildcard!)
→ Beispiel: nur "https://myapp.com/callback" erlaubt
NICHT: "https://myapp.com/*"
NICHT: "https://*.myapp.com/callback"
Schwachstelle 2: Fehlendes CSRF-Schutz (state-Parameter)
Problem:
Angreifer erstellt speziell präparierten OAuth-Link:
Schickt User zu: GET /authorize?...&state=ATTACKER-CONTROLLED
Wenn User seinen Account verbindet: Angreifer-Account verbunden!
Schutz:
→ state-Parameter: kryptografisch zufällig, in Session gespeichert
→ Bei Callback: state aus URL = state in Session?
→ Wenn nicht gleich: Angriff erkannt, Prozess abbrechen!
Implementierung (Node.js):
const state = crypto.randomBytes(32).toString('hex');
req.session.oauthState = state;
const authUrl = `${AUTH_SERVER}/authorize?...&state=${state}`;
// Callback:
if (req.query.state !== req.session.oauthState) {
return res.status(400).json({error: 'CSRF detected!'});
}
Schwachstelle 3: Authorization Code Interception
Problem:
Mobile Apps: code kommt via Custom URL Scheme:
myapp://callback?code=AUTH_CODE
Anderes App mit gleichem URL-Scheme: Code abgefangen!
Schutz: PKCE (Proof Key for Code Exchange) - Pflicht für SPAs und Mobile!
code_verifier = 32 random bytes (geheimgehalten!)
code_challenge = BASE64URL(SHA256(code_verifier))
1. Start Authorization:
GET /authorize?
...&
code_challenge=<SHA256_of_verifier>&
code_challenge_method=S256
2. Token Request:
POST /token
code=AUTH_CODE&
code_verifier=ORIGINAL_VERIFIER ← Nur der echte Client kennt das!
→ Abgefangener Auth Code nutzlos ohne code_verifier!
Schwachstelle 4: Unsichere Token-Speicherung
Problem:
SPA speichert Access Token in localStorage:
localStorage.setItem('token', accessToken);
→ XSS-Angriff: document.cookie oder localStorage auslesen → Token gestohlen!
Schutz:
→ Access Token: in memory-Variablen (nicht persistent)
→ Refresh Token: in HttpOnly Cookie (kein JS-Zugriff!)
→ SameSite=Strict: CSRF-Schutz für Cookies
// FALSCH - XSS-anfällig:
localStorage.setItem('access_token', token);
// RICHTIG - Token in Memory:
let accessToken = null; // In-memory, verschwindet bei Reload
// Refresh Token: Server setzt HttpOnly Cookie:
res.cookie('refresh_token', refreshToken, {
httpOnly: true, // JavaScript kann nicht zugreifen
secure: true, // Nur HTTPS
sameSite: 'strict', // CSRF-Schutz
maxAge: 30 * 24 * 60 * 60 * 1000 // 30 Tage
});
Schwachstelle 5: Zu weite Scopes
Problem:
App fordert alle Berechtigungen an:
scope=email+profile+contacts+calendar+drive+gmail
Konsequenz: Kompromittierter Token = Zugriff auf alles!
Schutz:
→ Minimal Scope: nur was aktuell benötigt wird
→ Incremental Authorization: Scope erst bei Bedarf anfordern
→ Scope Transparency: User sieht was App tatsächlich braucht
// Falsch: alles auf einmal
scope: 'email profile contacts calendar drive admin'
// Richtig: nur was benötigt wird
scope: 'email' // Beim Login
// Später wenn User Kalender-Feature nutzt:
scope: 'email calendar.readonly' // Minimal!
OpenID Connect (OIDC) Sicherheit
OIDC = OAuth 2.0 + Authentifizierung (Identität):
Zusätzlich zu Access Token: ID Token (JWT):
ID Token enthält: sub (User ID), email, name, issued at, expiry, nonce
OIDC-Schwachstellen:
1. ID Token nicht validiert:
Problem: App vertraut ID Token ohne Signatur-Prüfung
Angriff: Gefälschter ID Token → anderer User
Schutz:
→ IMMER signierte ID Tokens (RS256 oder ES256)
→ IMMER Signatur prüfen (mit JWKS des Authorization Server)
→ IMMER Expiry prüfen
→ IMMER Audience (aud) prüfen
Python (Keycloak + python-jose):
from jose import jwks, jwt
keys = requests.get('https://keycloak/realm/certs').json()['keys']
payload = jwt.decode(
id_token,
keys,
algorithms=['RS256'],
audience='my-client-id', # Pflicht!
issuer='https://keycloak/realm'
)
2. Nonce-Replay:
Problem: Ohne nonce: ID Token kann wiederverwendet werden
Schutz:
→ nonce = random, in Session gespeichert
→ nonce im ID Token muss nonce aus Session entsprechen
3. Implicit Flow (veraltet, unsicher):
Problem: Access + ID Token kommen direkt zum Browser via URL-Fragment
→ Token in URL = in Browser History, Logs, Referrer-Header
→ KEIN verwenden! Ersetze durch Authorization Code Flow + PKCE
---
JWT-Sicherheit (da OIDC JWTs nutzt):
JWT-Struktur: Header.Payload.Signature (base64-encoded, nicht verschlüsselt!)
Header: {"alg": "RS256", "typ": "JWT"}
Payload: {"sub": "123", "email": "user@example.com", "exp": 1770000000}
Signature: RSA/ECDSA signature über Header.Payload
JWT-Schwachstellen:
1. Algorithm Confusion (alg=none):
Angriff: Header ändern zu {"alg": "none"}
Signature weglassen
→ Unsichere Libraries akzeptieren das!
Schutz:
→ Algorithmus explizit festlegen beim Validieren
→ "none" niemals erlauben
2. RS256 → HS256 Switch:
Angriff: RS256 (asymmetrisch) zu HS256 (symmetrisch) wechseln
→ HS256-Schlüssel = öffentlicher Schlüssel (den Angreifer kennt!)
Schutz:
→ Algorithmus beim Validieren explizit auf RS256 fixieren
3. Weak HS256 Secret:
jwt.io Debugger + Brute-Force = geknacktes HS256 JWT
Schutz:
→ HS256 Secret: min. 256 Bit (32 Byte) zufällig
→ Besser: RS256/ES256 (asymmetrisch)
4. JWT ohne Expiry:
Schutz:
→ Immer exp Claim setzen
→ Kurze Expiry: Access Token 15-60 Minuten
OAuth für APIs - Client Credentials Flow
Machine-to-Machine Authentication (keine User-Interaktion):
Client Credentials Flow:
Service A → Authorization Server:
POST /token
grant_type=client_credentials&
client_id=service-a&
client_secret=secret123&
scope=api.read
Authorization Server → Service A:
{"access_token": "eyJ...", "expires_in": 3600}
Service A → Service B:
GET /internal-api/data
Authorization: Bearer eyJ...
Sicherheitsprobleme:
1. Client Secret Rotation:
→ Secrets müssen rotiert werden (Mindestens jährlich!)
→ Rotation: neues Secret erstellen → deploymen → altes deaktivieren
→ Secret im Vault (nicht in Konfigurationsdatei!)
2. Scope-Kontrolle:
→ client_id:service-a darf NUR /api/read
→ Nicht: admin-API, User-Daten anderer Services
3. Mutual TLS (mTLS) statt Client Secret:
→ Noch sicherer: Service A authentifiziert sich mit Zertifikat
→ Client Secret: shared secret (kann geleakt werden)
→ mTLS: privater Schlüssel verlässt Service nie
→ Kubernetes: Istio Service Mesh mit mTLS
Best Practices Zusammenfassung:
□ Authorization Code Flow + PKCE für alle Public Clients
□ state-Parameter IMMER (CSRF-Schutz)
□ redirect_uri: exaktes Matching, vorregistriert
□ Tokens: kurze Lifetime, Refresh Token in HttpOnly Cookie
□ Scopes: minimal, incremental authorization
□ ID Token: immer vollständig validieren (sig + aud + exp + nonce)
□ JWT: expliziter Algorithmus, kein "none", starkes Secret
□ Client Secrets: in Vault, rotieren, minimale Scope
OAuth-Implementierungsfehler sind in der OWASP API Security Top 10 prominent vertreten. AWARE7 analysiert OAuth/OIDC-Implementierungen im Rahmen von Penetrationstests und Web-Application-Security-Reviews.
API-Sicherheitstest anfragen | Penetrationstest Web-Applikationen
Nächster Schritt
Unsere zertifizierten Sicherheitsexperten beraten Sie zu den Themen aus diesem Artikel — unverbindlich und kostenlos.
Kostenlos · 30 Minuten · Unverbindlich
