Eine praktische Anleitung zur API-Authentifizierung und -Autorisierung, einschließlich OAuth 2.0, JWT und API-Schlüsselverwaltung.
OAuth 2.0 ist der am häufigsten verwendete Autorisierungsrahmen für den API-Zugang. Die Auswahl des geeigneten Berechtigungstyps ist entscheidend.
| Art der Finanzhilfe | Anwendungsfall | Sicherheitsstufe |
|---|---|---|
| Authorization Code + PKCE | SPAs und mobile Anwendungen (empfohlen) | Höchste |
| Authorization Code | Server-seitige Webanwendungen | Hoch |
| Client Credentials | Server-zu-Server-Kommunikation (M2M) | Mittel |
| Implizit (veraltet) | Ältere SPAs | Niedrig |
| Passwort des Ressourcenbesitzers (veraltet) | Höchste Vertrauenswürdigkeit nur bei Erstanbietern | Niedrig |
Implizites Grant ist jetzt veraltet, da das Token im URL-Fragment offengelegt wird. Verwenden Sie Autorisierungscode + PKCE für SPAs.
// 1. zufällige Generierung von code_verifier function generateCodeVerifier() { const array = new Uint8Array(32); crypto.getRandomValues(array); return btoa(String.fromCharCode(...array)) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } // 2. code_challenge mit SHA-256 generieren async function generateCodeChallenge(verifier) { const encoder = new TextEncoder(); const data = encoder.encode(verifier); const digest = await crypto.subtle.digest('SHA-256', data); return btoa(String.fromCharCode(...new Uint8Array(digest))) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } // 3. autorisierungsanfrage const verifier = generateCodeVerifier(); const challenge = await generateCodeChallenge(verifier); const authUrl = `https://auth.example.com/authorize?` + `response_type=code&` + `client_id=${clientId}&` + `redirect_uri=${redirectUri}&` + `code_challenge=${challenge}&` + `code_challenge_method=S256&` + `scope=read write`;
JWT ist als zustandsloses Authentifizierungs-Token weit verbreitet, aber Implementierungsfehler führen direkt zu Sicherheitslücken.
const jwt = require('jsonwebtoken'); // Token-Ausgabe --- function issueTokens(user) { const accessToken = jwt.sign( { sub: user.id, role: user.role }, process.env.JWT_SECRET, { algorithm: 'HS256', expiresIn: '15m', issuer: 'api.example.com', audience: 'app.example.com', } ); const refreshToken = jwt.sign( { sub: user.id, type: 'refresh' }, process.env.REFRESH_SECRET, { algorithm: 'HS256', expiresIn: '7d' } ); return { accessToken, refreshToken }; } // Token-Prüfung --- function verifyAccessToken(token) { return jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'], // ← Immer angeben! issuer: 'api.example.com', audience: 'app.example.com', }); }
| Speicherort | XSS-Widerstand | CSRF-Widerstand | Empfehlung |
|---|---|---|---|
| HttpOnly Cookie | ◎ | △ (erfordert SameSite-Einstellung) | Empfohlen |
| Speicher (variabel) | ◎ | ◎ | Navigation auf der Seite verloren |
| localStorage | ✕ (kann über XSS gestohlen werden) | ◎ | Nicht empfohlen |
| sessionStorage | ✕ (kann über XSS gestohlen werden) | ◎ | Eingeschränkte Nutzung |
Die Speicherung des Zugriffstokens in einem Cookie mit den Attributen HttpOnly, Secure und SameSite=Strict ist der sicherste Ansatz. Dies verhindert den Diebstahl des Tokens durch XSS-Angriffe.
API-Schlüssel sind ein einfacher Authentifizierungsmechanismus, aber eine unsachgemäße Verwaltung kann zu ernsthaften Risiken führen.
Generieren Sie mit ausreichender Entropie (256 Bit oder mehr). Gehashte Daten speichern; niemals reinen Text in der Datenbank aufbewahren.
Weisen Sie jedem Schlüssel einen Mindestumfang zu (nur Lesezugriff, nur bestimmte Ressourcen usw.). Vermeiden Sie Wildcard-Berechtigungen.
Wechseln Sie die Schlüssel regelmäßig (innerhalb von 90 Tagen). Setzen Sie eine Schonfrist für alte Schlüssel. Sofortiger Widerruf bei Auslaufen.
Senden Sie per Header (X-API-Key oder Authorization). Nicht in URL-Abfrageparameter aufnehmen (sie erscheinen in den Protokollen).
Schreiben Sie die Schlüssel nicht fest in den Quellcode. Verwalten Sie sie mit Umgebungsvariablen oder einem Secret Manager.
Überwachen Sie die Verwendung von API-Schlüsseln. Erkennen Sie anomale Muster (Massenanfragen, Zugriff von unbekannten IPs).
const crypto = require('crypto'); // Hash des API-Schlüssels mit SHA-256 und Vergleich async function validateApiKey(req, res, next) { const apiKey = req.headers['x-api-key']; if (!apiKey) { return res.status(401).json({ error: 'API key required' }); } // Hash und Nachschlagen in DB (timingSafeEqual nicht erforderlich, da wir Hashes vergleichen) const hashedKey = crypto .createHash('sha256') .update(apiKey) .digest('hex'); const keyRecord = await db.findApiKey(hashedKey); if (!keyRecord || keyRecord.revokedAt) { return res.status(403).json({ error: 'Invalid API key' }); } // Verfall prüfen if (keyRecord.expiresAt && new Date() > keyRecord.expiresAt) { return res.status(403).json({ error: 'API key expired' }); } req.apiClient = keyRecord; next(); }
AI agents and LLM-powered applications require additional authentication and authorization patterns beyond traditional API security.
Assign different access levels per model tier (e.g., GPT-4 requires elevated scope). Prevent unauthorized use of expensive or sensitive models.
Embed token budgets in access tokens or API key metadata. Enforce per-request and per-day limits to prevent denial-of-wallet attacks.
Check user permissions before executing certain prompt types (e.g., code generation, data analysis). Map prompt categories to role-based permissions.
Ensure conversation history, fine-tuned models, and RAG data are isolated per tenant. Use tenant-scoped API keys with namespace enforcement.
Assign cryptographic identities to AI agents. Use signed JWTs for inter-agent communication. Verify agent identity before granting tool access.
Automate API key rotation for LLM providers. Use short-lived tokens for agent sessions. Revoke credentials immediately when agents are decommissioned.
| Scenario | Auth Method | Required Scope | Approval |
|---|---|---|---|
| Agent reads public data | API Key | data:read |
Automatic |
| Agent modifies user data | OAuth 2.0 (delegated) | data:write |
User consent required |
| Agent calls external API | OAuth 2.0 + mTLS | external:invoke |
Human-in-the-loop |
| Agent executes code / shell | Scoped JWT + Sandbox | exec:sandbox |
Admin approval + audit log |
import jwt import time from datetime import datetime, timedelta class AgentTokenIssuer: """Issues scoped, short-lived tokens for AI agents.""" ALLOWED_SCOPES = { "reader": ["data:read"], "writer": ["data:read", "data:write"], "executor": ["data:read", "data:write", "exec:sandbox"], } def __init__(self, secret_key: str): self.secret_key = secret_key def issue_agent_token( self, agent_id: str, role: str, tenant_id: str, token_budget: int = 10000, ttl_minutes: int = 15, ) -> str: # Validate role and resolve scopes if role not in self.ALLOWED_SCOPES: raise ValueError(f"Invalid role: {role}") payload = { "sub": agent_id, "tenant": tenant_id, "scopes": self.ALLOWED_SCOPES[role], "token_budget": token_budget, "iat": datetime.utcnow(), "exp": datetime.utcnow() + timedelta(minutes=ttl_minutes), "type": "agent", } return jwt.encode(payload, self.secret_key, algorithm="HS256") def verify_tool_access(self, token: str, required_scope: str) -> dict: # Decode and verify agent token payload = jwt.decode( token, self.secret_key, algorithms=["HS256"] ) if required_scope not in payload["scopes"]: raise PermissionError( f"Agent {payload['sub']} lacks scope: {required_scope}" ) return payload
Related: LLM06: Excessive Agency, ASI01: Excessive Agency, ASI03: Insecure Tool/Function Calling
| Methode | Zustandslos | Anwendungsfall | Überlegungen zur Sicherheit |
|---|---|---|---|
| OAuth 2.0 + PKCE | ○ | APIs, die eine Benutzerautorisierung erfordern | Status/PKCE erforderlich, Verwaltung des Token-Ablaufs |
| JWT Bearer | ○ | Dienstübergreifende Kommunikation | Algorithmus-Spezifikation erforderlich, keine sensiblen Daten in der Nutzlast |
| API-Schlüssel | ○ | Server-zu-Server, externe Integrationen | Rotation und Einschränkung des Geltungsbereichs erforderlich |
| mTLS | ○ | Hochsichere M2M-Kommunikation | Hohe Betriebskosten für das Zertifikatsmanagement |
| Sitzungs-Cookie | ✕ | Traditionelle Webanwendungen | CSRF-Schutz erforderlich, Herausforderungen bei der Skalierbarkeit |