JWT-Tokens erklärt: Authentifizierung für moderne Webanwendungen
· 12 Min. Lesezeit
Inhaltsverzeichnis
- Was ist JWT?
- JWT-Struktur im Detail
- Wie JWT-Authentifizierung funktioniert
- JWT vs. sitzungsbasierte Authentifizierung
- JWT in Ihrer Anwendung implementieren
- Best Practices für Sicherheit
- Häufige Schwachstellen und wie man sie verhindert
- Token-Refresh-Strategien
- Wo JWTs gespeichert werden sollten
- Leistungsaspekte
- Häufig gestellte Fragen
- Verwandte Artikel
JSON Web Tokens (JWT) sind zum De-facto-Standard für Authentifizierung in modernen Webanwendungen geworden. Sie bieten eine kompakte, URL-sichere Möglichkeit, Ansprüche zwischen zwei Parteien darzustellen und ermöglichen zustandslose Authentifizierung, die horizontal skaliert, ohne gemeinsamen Sitzungsspeicher zu benötigen.
Egal, ob Sie eine REST-API, eine Microservices-Architektur oder eine Single-Page-Anwendung erstellen – das Verständnis von JWTs ist entscheidend für die Implementierung sicherer, skalierbarer Authentifizierung. Dieser umfassende Leitfaden erklärt, wie JWTs funktionieren, ihre Struktur, Sicherheitsaspekte und produktionsreife Best Practices.
Was ist JWT?
JWT (ausgesprochen „dschott") ist ein offener Standard (RFC 7519), der ein kompaktes, eigenständiges Format für die sichere Übertragung von Informationen zwischen Parteien als JSON-Objekt definiert. Die Informationen werden digital signiert, entweder mit einem geheimen Schlüssel (HMAC) oder einem öffentlichen/privaten Schlüsselpaar (RSA oder ECDSA), um die Integrität und Authentizität des Tokens zu gewährleisten.
Im Gegensatz zur traditionellen sitzungsbasierten Authentifizierung, bei der der Server den Sitzungsstatus im Speicher oder in einer Datenbank verwaltet, sind JWTs zustandslos. Alle Informationen, die zur Überprüfung der Benutzeridentität erforderlich sind, sind im Token selbst enthalten.
Hauptanwendungsfälle für JWT
- Authentifizierung: Nachdem sich ein Benutzer angemeldet hat, stellt der Server ein JWT aus, das der Client bei nachfolgenden Anfragen einschließt, um seine Identität nachzuweisen
- Autorisierung: JWTs können Benutzerrollen, Berechtigungen und Bereiche enthalten, sodass Server granulare Zugriffskontrollentscheidungen ohne Datenbankabfragen treffen können
- Informationsaustausch: Die signierte Natur von JWTs stellt sicher, dass der Absender der ist, für den er sich ausgibt, und dass die Daten während der Übertragung nicht manipuliert wurden
- Single Sign-On (SSO): JWTs ermöglichen nahtlose Authentifizierung über mehrere Domains und Dienste hinweg
- API-Authentifizierung: Mobile Apps und Drittanbieter-Integrationen können API-Anfragen authentifizieren, ohne serverseitige Sitzungen zu verwalten
Profi-Tipp: Verwenden Sie unseren JWT-Decoder, um Tokens während der Entwicklung zu inspizieren und zu validieren. Er hilft Ihnen, die Struktur zu verstehen und häufige Probleme zu erkennen, bevor sie in die Produktion gelangen.
JWT-Struktur im Detail
Ein JWT besteht aus drei verschiedenen Teilen, die durch Punkte (.) getrennt sind: header.payload.signature
So sieht ein vollständiges JWT aus:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRGV2ZWxvcGVyIiwiZW1haWwiOiJqYW5lQGV4YW1wbGUuY29tIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzEwNTkwNDAwLCJleHAiOjE3MTA2NzY4MDB9.4Adcj0mYZ8s5vxjKvV8pF7jKX9s8vZ5xJ3kL9mN2pQ4
Teil 1: Header
Der Header enthält typischerweise zwei Felder, die die kryptografischen Operationen des Tokens beschreiben:
{
"alg": "HS256",
"typ": "JWT"
}
alggibt den Signaturalgorithmus an (HS256, RS256, ES256 usw.)typdeklariert den Token-Typ, der immer „JWT" ist
Dieses JSON-Objekt wird Base64Url-kodiert, um den ersten Teil des JWT zu bilden. Die Kodierung macht es URL-sicher und kompakt für die Übertragung in HTTP-Headern.
Teil 2: Payload
Die Payload enthält Claims – Aussagen über den Benutzer und zusätzliche Metadaten. Claims werden in drei Typen kategorisiert:
{
"sub": "1234567890",
"name": "Jane Developer",
"email": "[email protected]",
"role": "admin",
"iat": 1710590400,
"exp": 1710676800
}
Registrierte Claims sind von der JWT-Spezifikation vordefiniert und liefern Standardinformationen:
iss(issuer): Identifiziert, wer das Token ausgestellt hatsub(subject): Identifiziert das Subjekt des Tokens (normalerweise die Benutzer-ID)aud(audience): Identifiziert die beabsichtigten Empfängerexp(expiration time): Unix-Zeitstempel, wann das Token abläuftnbf(not before): Token ist vor dieser Zeit nicht gültigiat(issued at): Wann das Token erstellt wurdejti(JWT ID): Eindeutige Kennung für das Token
Öffentliche Claims sind benutzerdefinierte Claims, die in der IANA JSON Web Token Registry definiert werden sollten oder kollisionsresistente Namen verwenden (wie URLs).
Private Claims sind benutzerdefinierte Claims, auf die sich die Parteien geeinigt haben, wie role, permissions oder organizationId.
Wichtig: Die Payload ist nur Base64Url-kodiert, nicht verschlüsselt. Speichern Sie niemals sensible Informationen wie Passwörter, Kreditkartennummern oder Sozialversicherungsnummern in einer JWT-Payload.
Teil 3: Signatur
Die Signatur stellt sicher, dass das Token nicht verändert wurde. Sie wird erstellt, indem der kodierte Header, die kodierte Payload, ein geheimer Schlüssel genommen und der im Header angegebene Algorithmus angewendet wird:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
Die Signatur ermöglicht es dem Empfänger zu überprüfen, dass der Absender der ist, für den er sich ausgibt, und dass die Nachricht unterwegs nicht geändert wurde.
Vergleich der Signaturalgorithmen
| Algorithmus | Typ | Sicherheitsstufe | Anwendungsfall | Schlüsselverwaltung |
|---|---|---|---|---|
| HS256 | Symmetrisch (HMAC) | Gut | Einzelner Dienst, interne APIs | Gemeinsames Geheimnis |
| RS256 | Asymmetrisch (RSA) | Sehr gut | Mehrere Dienste, öffentliche APIs | Öffentliches/privates Schlüsselpaar |
| ES256 | Asymmetrisch (ECDSA) | Ausgezeichnet | Hohe Sicherheitsanforderungen | Öffentliches/privates Schlüsselpaar |
| PS256 | Asymmetrisch (RSA-PSS) | Ausgezeichnet | Moderne Anwendungen | Öffentliches/privates Schlüsselpaar |
Wie JWT-Authentifizierung funktioniert
Das Verständnis des vollständigen Authentifizierungsablaufs hilft Ihnen, JWTs korrekt zu implementieren und Probleme effektiv zu beheben.
Der vollständige Authentifizierungsablauf
- Benutzeranmeldung: Der Client sendet Anmeldedaten (Benutzername/Passwort) an den Authentifizierungsendpunkt
- Überprüfung der Anmeldedaten: Der Server validiert die Anmeldedaten gegen die Datenbank
- Token-Generierung: Bei erfolgreicher Authentifizierung erstellt der Server ein JWT mit Benutzerinformationen und Claims
- Token-Übermittlung: Der Server sendet das JWT zurück an den Client (typischerweise im Response-Body)
- Token-Speicherung: Der Client speichert das JWT (im Speicher, localStorage oder httpOnly-Cookies)
- Authentifizierte Anfragen: Bei nachfolgenden Anfragen fügt der Client das JWT im Authorization-Header ein
- Token-Überprüfung: Der Server validiert die JWT-Signatur und prüft das Ablaufdatum
- Zugriff gewährt: Wenn gültig, verarbeitet der Server die Anfrage unter Verwendung der Claims im Token
Beispiel einer Authentifizierungsanfrage
POST /api/auth/login HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"email": "[email protected]",
"password": "securePassword123"
}
Beispiel einer Authentifizierungsantwort
HTTP/1.1 200 OK
Content-Type: application/json
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 3600,
"tokenType": "Bearer"
}
Beispiel einer authentifizierten API-Anfrage
GET /api/users/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
JWT vs. sitzungsbasierte Authentifizierung
Die Wahl zwischen JWT und sitzungsbasierter Authentifizierung hängt von der Architektur, dem Umfang und den Anforderungen Ihrer Anwendung ab.
| Aspekt | JWT | Sitzungsbasiert |
|---|---|---|
| Zustand | Zustandslos (Server speichert keine Tokens) | Zustandsbehaftet (Server speichert Sitzungsdaten) |
| Skalierbarkeit | Ausgezeichnet (kein gemeinsamer Speicher erforderlich) | Erfordert Sitzungsspeicher (Redis, Datenbank) |
| Widerruf | Schwierig (erfordert Blacklist oder kurze Gültigkeit) | Einfach (Sitzung aus Speicher löschen) |
| Größe | Größer (wird mit jeder Anfrage gesendet) | Kleiner (nur Sitzungs-ID) |
| Cross-Domain | Einfach (CORS-freundlich) | Komplex (Cookie-Domain-Einschränkungen) |
| Mobile Apps | Ideal (keine Cookie-Unterstützung erforderlich) | Herausfordernd (Cookie-Handhabung) |
| Sicherheit | Erfordert sorgfältige Implementierung | Etablierte Muster |
Wann JWT verwendet werden sollte
- Beim Erstellen von Microservices, die Authentifizierung teilen müssen
- Bei der Entwicklung mobiler Anwendungen oder SPAs
- Beim Erstellen öffentlicher APIs für Drittanbieter-Integrationen
- Bei der Implementierung von Single Sign-On (SSO) über mehrere Domains hinweg
- Bei horizontaler Skalierung ohne gemeinsamen Sitzungsspeicher
Wann Sitzungen verwendet werden sollten
- Beim Erstellen traditioneller serverseitig gerenderter Webanwendungen
- Wenn sofortiger Token-Widerruf erforderlich ist
- Bei der Arbeit mit sensiblen Daten, die serverseitige Kontrolle benötigen
- Zur Minimierung der Bandbreite (Sitzungen verwenden kleinere Cookies)
- Bei der Implementierung komplexer Sitzungsverwaltungsfunktionen
JWT in Ihrer Anwendung implementieren
Lassen Sie uns praktische Implementierungsbeispiele in beliebten Programmiersprachen und Frameworks durchgehen.
Node.js mit Express
const jwt = require('jsonwebtoken');
const express = require('express');
const app = express();
const SECRET_KEY = process.env.JWT_SECRET;
// Login-Endpunkt
app.post('/api/auth/login', async (req, res) => {
const { email, password } = req.body;
// Anmeldedaten überprüfen (Pseudocode)
const user = await verifyCredentials(email, password);
if (!user) {
return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
}
// JWT generieren
const token = jwt.sign(
{
sub: user.id,
email: user.email,
role: user.role
},
SECRET_KEY,
{ expiresIn: '1h' }
);
res.json({ accessToken: token });
});
// Authentifizierungs-Middleware
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Kein Token bereitgestellt' });
}
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Ungültiges Token' });
}
req.user = user;
next();
});
};
// Geschützte Route
app.get('/api/users/profile', authenticateToken, (req, res) => {
res.json({ user: req.user });
});
Python mit Flask
from flask import Flask, request, jsonify
import jwt
import datetime
import os
app = Flask(__name__)
SECRET_KEY = os.getenv('JWT_SECRET')
@app.route('/api/auth/login', methods=['POST'])
def login():
data = request.get_json()
email = data.get('email')
password = data.get('password')
# Anmeldedaten überprüfen
user = verify_credentials(email, password)
if not user:
return jsonify({'error': 'Ungültige Anmeldedaten'}), 401
# JWT generieren
token = jwt.encode({
'sub': user['id'],
'email': user['email'],
'role': user['role'],
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}, SECRET_KEY, algorithm='HS256')
return jsonify({'accessToken': token})
def token_required(f):
def decorator(*args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({'error': 'Kein Token bereitgestellt'}), 401
try:
token = token.split(' ')[1]
data = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
request.user = data
except jwt.ExpiredSignatureError:
return jsonify({'error': 'Token abgelaufen'}), 403
except jwt.InvalidTokenError:
return jsonify({'error': 'Ungültiges Token'}), 403
return f(*args, **kwargs)
decorator.__name__ = f.__name__
return decorator
@app.route('/api/users/profile')
@token_required
def profile():
return jsonify({'user': request.user})
Schneller Tipp: Verwenden Sie immer Umgebungsvariablen für geheime Schlüssel. Codieren Sie sie niemals fest in Ihren Quellcode oder committen Sie sie in die Versionskontrolle. Verwenden Sie Tools wie Passwort-Generator, um starke Geheimnisse zu erstellen.