Tips & Tricks (Aktualisiert: 2.6.2026)

Sichere Cookie-Verwaltung mit Claude Code: Next.js-Sessions, CSRF und Consent

Implementieren Sie sichere Next.js-Cookies mit Claude Code: HttpOnly, Secure, SameSite, CSRF, Logout und Consent.

Sichere Cookie-Verwaltung mit Claude Code: Next.js-Sessions, CSRF und Consent

Cookie-Verwaltung wirkt klein, bis sie zur Ursache einer Kontoübernahme wird. Ein Session-Cookie ist nur ein kurzer Wert in einem HTTP-Header, aber der Browser sendet ihn automatisch. Genau deshalb ist er praktisch für Authentifizierung und riskant, wenn die Attribute falsch sind.

Claude Code kann den Code schnell erzeugen. Eine vage Aufgabe wie “setze ein Login-Cookie” lässt aber oft Produktionsdetails offen: HttpOnly fehlt, SameSite=None steht ohne Secure, der Logout löscht nichts, weil der Path anders ist, oder Analytics-Cookies werden mit Authentifizierungs-Cookies vermischt.

Dieser Leitfaden zeigt einen vollständigen Next.js-App-Router-Workflow: Cookie-Inventar, kopierbaren Route Handler, Logout, serverseitiges Lesen, CSRF-Schutz, Schutz vor Session Fixation, Browserverhalten, Consent-Grenze, Prüfkommandos und offizielle Quellen.

Legen Sie den Zweck fest, bevor Sie Attribute setzen. Ein Authentifizierungs-Cookie ist ein Zugangsnachweis. Ein Preference-Cookie speichert UI-Zustand wie Sprache oder Theme. Analytics- und Werbe-Cookies gehören zur Messung und Nachverfolgung. Sie brauchen andere Regeln.

ZweckBeispielEmpfohlene AttributeConsent-Grenze
Authentifizierungs-Session__Host-sessionHttpOnly, Secure, SameSite=Lax, Path=/, kurzes Max-AgeOft für den angeforderten Dienst notwendig, aber lokal rechtlich prüfen
CSRF-Tokencsrf-tokenSecure, SameSite=Lax, kurzes Max-AgeSicherheits-Cookie, kein Analytics-Identifier
UI-Präferenztheme, localeSecure, SameSite=Lax, begrenzte LaufzeitJe nach Region und Policy erklären
Analytics oder Werbung_ga, campaign IDNur nach Consent, wenn erforderlichVon Login und Checkout trennen

HttpOnly bedeutet: Browser-JavaScript kann den Cookie nicht über document.cookie lesen. Secure beschränkt die Übertragung auf HTTPS, wobei localhost besonders behandelt wird. SameSite steuert, ob der Cookie bei Cross-Site-Requests mitgesendet wird. Max-Age ist eine relative Dauer in Sekunden; Expires ist ein absolutes Datum.

MDN empfiehlt in Secure cookie configuration, den Scope mit Secure, HttpOnly, SameSite und Prefixes zu begrenzen. Die Set-Cookie-Referenz erklärt außerdem, dass SameSite=None Secure benötigt und dass Max-Age Vorrang vor Expires hat, wenn beide gesetzt sind.

Für Sessions ist __Host- sinnvoll. Unterstützende Browser akzeptieren ein __Host--Cookie nur mit Secure, ohne Domain und mit Path=/. Dadurch kann ein Subdomain-Cookie die Session schwerer überschreiben.

Die aktuelle Next.js-Dokumentation zu cookies beschreibt cookies() als asynchrone API und listet Optionen wie httpOnly, secure, sameSite, maxAge, path und domain. Server Components können Cookies lesen. Setzen und Löschen gehört in Route Handler oder Server Actions.

Legen Sie app/api/login/route.ts an. Der Beispielcode nutzt eine Map im Speicher, damit Sie lokal testen können. In Produktion ersetzen Sie sie durch Redis, PostgreSQL, DynamoDB oder einen anderen dauerhaften Session Store.

import { createHmac, randomBytes } from "node:crypto";
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";

export const runtime = "nodejs";

const env = z
  .object({
    NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
    SESSION_SECRET: z.string().min(32),
  })
  .parse(process.env);

const SESSION_COOKIE = "__Host-session";
const SESSION_MAX_AGE_SECONDS = 60 * 60 * 8;

type SessionRecord = {
  userId: string;
  expiresAt: number;
};

declare global {
  var demoSessions: Map<string, SessionRecord> | undefined;
}

const sessions = globalThis.demoSessions ?? new Map<string, SessionRecord>();
globalThis.demoSessions = sessions;

const loginSchema = z.object({
  email: z.string().email(),
  password: z.string().min(12),
});

function createSessionToken() {
  const id = randomBytes(32).toString("base64url");
  const signature = createHmac("sha256", env.SESSION_SECRET)
    .update(id)
    .digest("base64url");

  return `${id}.${signature}`;
}

async function authenticate(email: string, password: string) {
  if (email === "masa@example.com" && password === "correct-horse-battery-staple") {
    return { id: "user_123" };
  }

  return null;
}

export async function POST(request: NextRequest) {
  const body = loginSchema.safeParse(await request.json());
  if (!body.success) {
    return NextResponse.json({ error: "Invalid login payload" }, { status: 400 });
  }

  const user = await authenticate(body.data.email, body.data.password);
  if (!user) {
    return NextResponse.json({ error: "Invalid credentials" }, { status: 401 });
  }

  const token = createSessionToken();
  sessions.set(token, {
    userId: user.id,
    expiresAt: Date.now() + SESSION_MAX_AGE_SECONDS * 1000,
  });

  const response = NextResponse.json({ ok: true });
  response.cookies.set({
    name: SESSION_COOKIE,
    value: token,
    httpOnly: true,
    secure: true,
    sameSite: "lax",
    path: "/",
    maxAge: SESSION_MAX_AGE_SECONDS,
  });

  return response;
}

Der Token wird bei jedem erfolgreichen Login neu erzeugt. Das verhindert Session Fixation: Ein Angreifer versucht, das Opfer mit einer bekannten Session-ID einzuloggen. OWASP beschreibt diesen Angriff unter Session Fixation.

Logout und serverseitiges Lesen

Cookie-Löschung muss denselben Scope treffen. Wenn der Cookie mit Path=/ gesetzt wurde, löschen Sie ihn mit Path=/. Wenn Domain gesetzt wurde, muss auch die Löschung dieselbe Domain verwenden. Bei __Host- wird Domain nicht gesetzt.

app/api/logout/route.ts:

import { NextResponse } from "next/server";

const SESSION_COOKIE = "__Host-session";

export async function POST() {
  const response = NextResponse.json({ ok: true });

  response.cookies.set({
    name: SESSION_COOKIE,
    value: "",
    httpOnly: true,
    secure: true,
    sameSite: "lax",
    path: "/",
    maxAge: 0,
  });

  return response;
}

In Produktion muss zusätzlich der serverseitige Session-Eintrag gelöscht oder widerrufen werden. Nur den Browser-Cookie zu entfernen reicht nicht gegen einen bereits gestohlenen Token.

Serverseitiges Lesen:

import { cookies } from "next/headers";
import { redirect } from "next/navigation";

const SESSION_COOKIE = "__Host-session";

export default async function AccountPage() {
  const cookieStore = await cookies();
  const sessionToken = cookieStore.get(SESSION_COOKIE)?.value;

  if (!sessionToken) {
    redirect("/login");
  }

  return <main>Account dashboard</main>;
}

Ein vorhandener Cookie ist kein ausreichender Login-Beweis. Der Server muss Session Store, Ablauf, Widerruf, Benutzer und Berechtigungen prüfen.

CSRF ist nicht durch HttpOnly gelöst

CSRF bedeutet Cross-Site Request Forgery: Eine fremde Seite bringt den eingeloggten Browser dazu, eine unerwünschte Anfrage zu senden. Da Cookies automatisch mitgesendet werden, schützt HttpOnly nur vor JavaScript-Lesen, nicht vor dem Mitsenden.

OWASP empfiehlt im CSRF Prevention Cheat Sheet Tokens für zustandsändernde Requests. Dieses Hilfsmodul erzeugt einen signierten Token, der an die Session gebunden ist.

import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";

const CSRF_SECRET = process.env.SESSION_SECRET;
if (!CSRF_SECRET || CSRF_SECRET.length < 32) {
  throw new Error("SESSION_SECRET must be at least 32 characters");
}

export function createCsrfToken(sessionToken: string) {
  const nonce = randomBytes(16).toString("base64url");
  const signature = createHmac("sha256", CSRF_SECRET)
    .update(`${sessionToken}.${nonce}`)
    .digest("base64url");

  return `${nonce}.${signature}`;
}

export function verifyCsrfToken(sessionToken: string, token: string) {
  const [nonce, signature] = token.split(".");
  if (!nonce || !signature) return false;

  const expected = createHmac("sha256", CSRF_SECRET)
    .update(`${sessionToken}.${nonce}`)
    .digest("base64url");

  return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

Verlangen Sie den Token für POST, PUT, PATCH und DELETE. GET darf keinen Zustand ändern. SameSite=Lax ist eine zusätzliche Schicht, aber kein Ersatz für Token, Origin-Prüfung und saubere HTTP-Methoden.

Browserverhalten und Ablaufzeiten

Browser stellen Set-Cookie nicht für Frontend-JavaScript bereit. In DevTools oder mit curl -i ist der Header sichtbar, in fetch()-Response-Headers nicht. Bei Cross-Origin-Requests müssen CORS und credentials ebenfalls passen.

Max-Age ist eine relative Lebensdauer in Sekunden. Expires ist ein konkreter Zeitpunkt. Für Anwendungscode ist Max-Age meist klarer und weniger anfällig für Uhrzeitunterschiede. Sind beide vorhanden, gewinnt Max-Age.

SameSite=Lax erlaubt bestimmte Top-Level-Navigationen mit sicheren Methoden. Ein GET-Endpunkt, der Daten verändert, bleibt deshalb gefährlich. Strict ist stärker, kann aber externe Einstiege stören. None braucht einen echten Cross-Site-Grund und immer Secure.

Die Consent-Grenze trennt Cookies, die für den angeforderten Dienst nötig sind, von Analytics-, Werbe- oder Experiment-Cookies. Die Cookie-Richtlinie der Europäischen Kommission zeigt diese praktische Trennung zwischen Consent, Authentifizierung und Analyse. Das ist keine Rechtsberatung, aber eine klare Architekturregel: Authentifizierungssicherheit darf nicht an Marketing-Consent hängen.

Fall 1: SaaS-Login. Verwenden Sie __Host-session, kurzes Max-Age, serverseitigen Widerruf, CSRF-Token und neuen Token nach Login. Für Admin und Billing sind SameSite=Strict und zusätzliche Prüfung sinnvoll.

Fall 2: Content-Site mit Gratis-PDF, Produkten und Beratungsformular. CTA-Messung darf nicht bedeuten, dass Ablehnung von Analytics Login, Download, Kauf oder Anfrage kaputt macht.

Fall 3: Sprache und Theme. Diese Cookies müssen manchmal von JavaScript gelesen werden und sind dann nicht HttpOnly. Speichern Sie dort niemals Token, Rollen, Preise oder Rechte.

Häufige Fehler

Erster Fehler: __Host-session ohne Secure, mit Domain oder ohne Path=/. Der Prefix verliert seine Schutzwirkung.

Zweiter Fehler: Logout löscht nichts, weil Path oder Domain nicht übereinstimmen.

Dritter Fehler: SameSite als vollständige CSRF-Lösung. Token, HTTP-Methode und Serverprüfung bleiben nötig.

Vierter Fehler: Tokens in localStorage oder client-lesbaren Cookies. Bei XSS sind sie offen.

Fünfter Fehler: Consent-Banner blockiert Sicherheits-Cookies. Ablehnung von Analytics darf Login, Warenkorb, Checkout oder CSRF-Schutz nicht deaktivieren.

Prompt und Prüfung

Empfohlener Prompt:

Implementiere ein Next.js App Router Login-Cookie.

Anforderungen:
- Name: __Host-session
- Attribute: HttpOnly, Secure, SameSite=Lax, Path=/, Max-Age
- niemals Domain setzen
- nach jedem erfolgreichen Login neuen Session-Token erzeugen
- Logout mit Max-Age=0 und demselben Path
- Analytics-Consent nicht mit Authentifizierung mischen
- CSRF-Token für zustandsändernde Requests erklären
- gegen MDN, Next.js und OWASP prüfen

Login prüfen:

curl -i -X POST http://localhost:3000/api/login \
  -H "Content-Type: application/json" \
  -d '{"email":"masa@example.com","password":"correct-horse-battery-staple"}'

Erwartete Form:

Set-Cookie: __Host-session=...; Path=/; Max-Age=28800; HttpOnly; Secure; SameSite=Lax

Logout prüfen:

curl -i -X POST http://localhost:3000/api/logout

Die Antwort sollte denselben Namen, Path=/ und Max-Age=0 enthalten. Mit Playwright können Sie context.cookies() für httpOnly, secure, sameSite und Ablaufzeit verwenden.

Für den gesamten Authentifizierungsfluss lesen Sie den Claude-Code-Authentifizierungsleitfaden, den Vergleich zu JWT-Authentifizierung und den Sicherheitsaudit-Leitfaden. Offizielle Quellen: MDN Set-Cookie, Next.js cookies, OWASP Session Management und OWASP CSRF Prevention.

Für wiederverwendbare Prompts, Checklisten und Review-Templates nutzen Sie die ClaudeCodeLab-Produkte. Wenn Ihr Team diese Regeln in einem echten Repository mit Consent, Checkout und Security Review braucht, ist Training und Beratung der nächste Schritt.

Im Test war der größte Unterschied ein expliziter Cookie-Vertrag: __Host-, Logout-Scope, CSRF-Token und Analytics-Consent-Grenze. Ein kurzer Auftrag wie “mach Cookies sicher” ließ dagegen weiterhin Details offen, die manuell korrigiert werden mussten.

#Claude Code #Cookie #Session #Sicherheit #Next.js #TypeScript
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.