Moderne Web‑Anwendungen basieren immer häufiger auf JWT‑Tokens (JSON Web Token) als grundlegenden Authentifizierungsmechanismus. In einer Next.js‑Umgebung, die Server‑Side‑Rendering (SSR) und Client‑Side‑Rendering (CSR) kombiniert, erfordert ein korrektes Sitzungs‑Management eine durchdachte Architektur. Dieser Artikel stellt das Threat‑Model vor, erklärt die Funktionsweise des Mechanismus, zeigt den Kontrast zwischen verwundbarem und verbessertem Code und liefert eine Checkliste zur Minderung, die den OWASP‑, CWE‑Standards und dem Prinzip des Least‑Privilege entspricht.
Threat‑Model – was kann schiefgehen?
Die grundlegenden Angriffsvektoren auf JWT‑basierte Systeme sind:
- Diebstahl des Tokens durch XSS (ein in den Browser injizierter Skript greift auf das Token im lokalen Speicher oder auf ein ungeschütztes Cookie zu);
- CSRF‑Angriff, wenn der Browser das Cookie mit dem Token automatisch in einer API‑Anfrage mitsendet;
- Replay‑Attack – ein abgefangenes Token wird vor dem Ablauf erneut verwendet;
- Fehlende Schlüsselrotation – bei einem Leak können alle Tokens auf einmal invalidiert werden.
Dieses Modell lässt sich als drei Schichten visualisieren: Token‑Speicherung, Token‑Verifizierung und Lebenszyklus‑Management. Jede Schicht erfordert eigene Sicherheitsmaßnahmen.
Wie funktioniert der JWT‑Mechanismus in Next.js?
Ein typischer Ablauf sieht folgendermaßen aus:
- Der Benutzer meldet sich an, der Server (Node.js) erzeugt zwei Tokens –
accessToken(kurze Lebensdauer) undrefreshToken(längere). - Die Tokens werden in
HttpOnly‑ undSecure‑Cookies an den Browser gesendet. - Bei einer API‑Anfrage fügt der Browser automatisch das
accessTokenhinzu. Middleware in Next.js prüft Signatur und Ablaufdatum. - Wenn das
accessTokenabläuft, ruft die Anwendung den Refresh‑Endpoint auf und übergibt dasrefreshToken. Der Server liefert ein neuesaccessTokenund optional ein neuesrefreshTokenzurück.
Entscheidend ist, dass die Tokens nicht im JavaScript verfügbar sind (daher HttpOnly) und dass jeder Endpoint die Signatur mit dem aktuellen Schlüssel verifizieren muss.
Beispiel: verwundbarer vs. verbesserter Code
Im Folgenden vergleichen wir zwei Code‑Snippets – zuerst eine einfache, aber unsichere Methode, das Token im localStorage zu speichern, und danach die empfohlene Implementierung mit HttpOnly‑Cookies und Middleware in Next.js.
// VULNERABLE – Speicherung im localStorage (anfällig für XSS)
function login(credentials) {
fetch('/api/auth/login', {method: 'POST', body: JSON.stringify(credentials)})
.then(r => r.json())
.then(data => {
localStorage.setItem('accessToken', data.accessToken);
localStorage.setItem('refreshToken', data.refreshToken);
});
}
// SECURE – Setzen von HttpOnly‑Cookies auf Serverseite
// pages/api/auth/login.js
import { sign } from 'jsonwebtoken';
export default async function handler(req, res) {
const { username, password } = req.body;
// ... credentials prüfen ...
const accessToken = sign({ sub: username }, process.env.JWT_ACCESS_SECRET, { expiresIn: '15m' });
const refreshToken = sign({ sub: username }, process.env.JWT_REFRESH_SECRET, { expiresIn: '30d' });
res.setHeader('Set-Cookie', [
`accessToken=${accessToken}; HttpOnly; Secure; SameSite=Strict; Path=/api; Max-Age=900`,
`refreshToken=${refreshToken}; HttpOnly; Secure; SameSite=Strict; Path=/api/auth/refresh; Max-Age=2592000`
]);
res.status(200).json({ message: 'Logged in' });
}
Im gesicherten Ansatz gelangen die Tokens nicht in JavaScript, und die Flags SameSite=Strict reduzieren das CSRF‑Risiko. Zusätzlich begrenzen wir den Path, sodass die Cookies nur an die dafür vorgesehenen Endpunkte gesendet werden.
Checklisten zur Minderung – was zu tun ist, um OWASP und CWE zu erfüllen
- Speicherung: Verwende
HttpOnly+Secure‑Cookies; vermeidelocalStorageundsessionStoragefür Tokens. - Domain‑ und Pfad‑Einschränkung: Setze
DomainundPathso, dass das Cookie nur für die API verfügbar ist. - SameSite: Wähle
StrictoderLaxje nach Anforderung, um CSRF zu verhindern. - Validierung: Verifiziere Signatur, Ablaufdatum und Claims (
iss,aud) serverseitig. - Schlüsselrotation: Ändere JWT‑Secrets regelmäßig; bewahre den vorherigen Schlüssel für einen kurzen Zeitraum auf, um die Validierung bestehender Tokens zu ermöglichen.
- Token‑Erneuerung: Nutze kurze
accessTokenund längererefreshToken; nach erfolgreicher Erneuerung den vorherigen Refresh‑Token invalidieren. - Rate‑Limiting und Monitoring fehlgeschlagener Verifizierungen – reduziert das Risiko von Brute‑Force‑ und Token‑Replay‑Angriffen.
Die Implementierung der oben genannten Punkte entspricht CWE‑287 (Improper Authentication) und CWE‑352 (Cross‑Site Request Forgery).
Strategie zur JWT‑Schlüsselrotation
Schlüssel, die zum Signieren von Tokens verwendet werden, sollten eine festgelegte Gültigkeitsdauer haben (z. B. 30 Tage). Der Rotationsprozess umfasst:
- Erzeugen eines neuen Schlüssels und Speichern in einem sicheren Speicher (z. B. AWS KMS, HashiCorp Vault).
- Hinzufügen des neuen Schlüssels zur Liste der „akzeptierten“ Schlüssel in der Anwendungskonfiguration, wobei der vorherige Schlüssel als „Fallback“ erhalten bleibt.
- Nach Ablauf der Übergangsphase (z. B. 24 h) Entfernen des alten Schlüssels – alle mit dem älteren Schlüssel signierten Tokens werden nicht mehr akzeptiert.
In Next.js lässt sich das mit einer Hilfsfunktion umsetzen:
// utils/jwt.js
import jwt from 'jsonwebtoken';
const keys = {
current: process.env.JWT_ACCESS_SECRET,
previous: process.env.JWT_ACCESS_SECRET_OLD,
};
export function verify(token) {
try {
return jwt.verify(token, keys.current);
} catch (e) {
// fallback to previous key
return jwt.verify(token, keys.previous);
}
}
Dieser Ansatz erfüllt die OWASP‑Anforderung „Key Management“ und minimiert das Risiko von Sitzungsunterbrechungen während der Rotation.
Typische Fehler und Kompromisse
Even experienced teams make mistakes. Die häufigsten sind:
- Festlegung einer zu langen Lebensdauer für Access‑Tokens – erhöht die Angriffsfläche bei einem Diebstahl.
- Kein Refresh‑Token‑Renewal – ermöglicht die unbegrenzte Nutzung eines gestohlenen Tokens.
- Verwendung eines einzigen Schlüssels für Access‑ und Refresh‑Tokens – erschwert die Rotation und erhöht die Folgen eines Lecks.
- Ungeeignete SameSite‑Flags – bei
Nonesind Tokens anfällig für CSRF im Cross‑Site‑Kontext. - Exponieren von Tokens in Logs – besonders in CI/CD‑Umgebungen.
Kompromisslösungen wie ein „sliding expiration“ für Refresh‑Tokens können helfen, sollten aber stets hinsichtlich Sicherheit und Usability bewertet werden.
Die beste Verteidigung ist nicht nur Technologie, sondern ein bewusstes Sitzungsdesign von Anfang an.
Praktische Implementierungs‑Checkliste
- ✅ Verwende
HttpOnly,SecureundSameSite=Strictbeim Setzen von JWT‑Cookies. - ✅ Begrenze den
Pathder Cookies auf API‑Endpoints. - ✅ Implementiere Middleware in Next.js, die das Token bei jeder Anfrage verifiziert.
- ✅ Erstelle einen Token‑Refresh‑Endpoint mit Rotation des Refresh‑Tokens.
- ✅ Führe die Schlüsselrotation alle 30‑60 Tage durch und behalte den vorherigen Schlüssel als Fallback.
- ✅ Überwache fehlgeschlagene Verifizierungen und setze Rate‑Limiting ein.
- ✅ Prüfe den Code regelmäßig auf XSS und entferne alle gefährlichen Daten‑Einfügungen.
Durch die Erfüllung dieser Liste schafft das Engineering‑Team ein solides Fundament für Sitzungs‑Sicherheit, konform mit den OWASP Top 10‑Standards und CWE‑798 (Use of Hard‑coded Credentials).
Zusammenfassend erfordern sichere JWT‑Tokens in Next.js eine durchdachte Speicher‑Strategie, regelmäßige Schlüsselrotation und strenge Kontrolle über den Token‑Lebenszyklus. Durch die Umsetzung der beschriebenen Praktiken reduzieren Sie das Risiko von Token‑Diebstahl, CSRF‑ und XSS‑Angriffen und gewährleisten die Konformität mit den besten Branchenstandards. Wenn Sie Unterstützung bei einer Sicherheits‑Audit oder der Implementierung eines solchen Systems in Ihrem Unternehmen benötigen, kontaktieren Sie Coderia.it – wir helfen Ihnen, eine Lösung zu bauen, die höchste Sicherheits‑ und Leistungsanforderungen erfüllt.



