`Cookie` in TypeScript

`Cookie` in TypeScript

Dieser Artikel erklärt Cookie in TypeScript.

Wir gehen praxisnahe Muster durch, um Cookies sowohl im Browser als auch auf dem Server sicher und zuverlässig zu behandeln.

YouTube Video

Cookie in TypeScript

Grundkonzepte von Cookies

Ein Cookie ist ein Mechanismus zum Speichern kleiner Zeichenketten (Name=Wert-Paare) auf dem Client, erstellt über den HTTP-Header Set-Cookie oder document.cookie. Ihr Verhalten lässt sich mit Sicherheitsattributen steuern (HttpOnly, Secure, SameSite usw.).

Grundlegende Operationen im Browser: document.cookie

Unten steht ein minimales Beispiel, um im Browser ein Cookie zu schreiben. Erstellen Sie ein Cookie, indem Sie eine Zeichenkette an document.cookie anhängen.

 1// Set a simple cookie that expires in 7 days.
 2// Note: Comments are in English per the user's preference.
 3const setSimpleCookie = (name: string, value: string) => {
 4  const days = 7;
 5  const expires = new Date(Date.now() + days * 86400_000).toUTCString();
 6  // name=value; Expires=...; Path=/
 7  document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; Expires=${expires}; Path=/`;
 8};
 9
10setSimpleCookie('theme', 'dark');
  • Dieser Code erzeugt ein Cookie, das in 7 Tagen abläuft. Es ist auf der Browserseite leicht zu verwenden, aber da Sie HttpOnly nicht setzen können, sollten Sie keine sensiblen Informationen speichern.

Cookies lesen (Browser)

Die folgende Funktion ist eine Hilfsfunktion, die den Wert des Cookies mit einem bestimmten Namen aus document.cookie abruft. document.cookie wird als Zeichenkette zurückgegeben, die durch Semikolon und Leerzeichen ('; ') getrennt ist. Daher teilen wir sie auf und suchen nach dem gewünschten Cookie.

 1// Parse document.cookie and return value for given name or null if not found.
 2const getCookie = (name: string): string | null => {
 3  const cookies = document.cookie ? document.cookie.split('; ') : [];
 4  for (const cookie of cookies) {
 5    const [k, ...rest] = cookie.split('=');
 6    const v = rest.join('=');
 7    if (decodeURIComponent(k) === name) {
 8      return decodeURIComponent(v);
 9    }
10  }
11  return null;
12};
13
14// Example usage:
15const theme = getCookie('theme'); // => "dark" if set
16console.log('theme cookie:', theme);
  • Diese Funktion dekodiert sowohl den Cookie-Namen als auch den Cookie-Wert, um sie sicher zu vergleichen und abzurufen. Sie behandelt doppelte Cookie-Namen und kodierte Sonderzeichen und ist dadurch eine einfache, aber robuste Implementierung.

Sicherheitsattribute verstehen und anwenden

Cookies besitzen mehrere wichtige Attribute, die jeweils Sicherheit und Geltungsbereich des Verhaltens steuern.

  • Das Attribut HttpOnly verhindert, dass das Cookie über JavaScript zugänglich ist, und hilft, XSS (Cross-Site Scripting)-Angriffe abzumildern. Beachten Sie, dass HttpOnly clientseitig im Browser nicht gesetzt werden kann.
  • Das Attribut Secure bewirkt, dass Cookies nur über HTTPS gesendet werden, wodurch das Risiko von Abhören und Manipulation verringert wird.
  • Das Attribut SameSite steuert, ob Cookies bei seitenübergreifenden Anfragen gesendet werden, und hilft, CSRF (Cross-Site Request Forgery)-Angriffe zu verhindern.
  • Das Attribut Path legt den Geltungsbereich der Anforderungspfade fest, für die das Cookie gesendet wird, und das Attribut Domain gibt die Domain an, in der das Cookie gültig ist.
  • Durch Setzen der Attribute Expires oder Max-Age können Sie die Ablaufzeit des Cookies steuern.

Beispiel zum Setzen von SameSite/Secure im Browser (was möglich ist)

HttpOnly kann clientseitig nicht gesetzt werden. Im Folgenden ein Beispiel für das clientseitige Hinzufügen von SameSite und Secure. Allerdings wirkt Secure nur auf HTTPS-Seiten.

 1// Set cookie with SameSite and Secure attributes (Secure only effective over HTTPS).
 2const setCookieWithAttributes = (name: string, value: string) => {
 3  const maxAge = 60 * 60 * 24 * 7; // 7 days in seconds
 4  // Note: HttpOnly cannot be set from JS; set it on server-side when you want to restrict JS access.
 5  document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; `
 6    + `Max-Age=${maxAge}; `
 7    + `Path=/; `
 8    + `SameSite=Lax; `
 9    + `Secure`;
10};
11
12setCookieWithAttributes('session_hint', 'true');
  • SameSite=Lax ist eine sichere und verbreitete Wahl. Für stärkere Einschränkungen verwenden Sie SameSite=Strict, allerdings können legitime Navigationsvorgänge von externen Seiten nicht mehr funktionieren.

Cookies auf dem Server setzen (Set-Cookie) (Node/TypeScript)

Auf dem Server können Sie HttpOnly über den Set-Cookie-Header setzen; daher sollte das Sitzungsmanagement idealerweise serverseitig erfolgen. Unten ein Beispiel mit Nodes http-Modul.

 1// A minimal Node HTTP server in TypeScript that sets a secure HttpOnly cookie.
 2// This example uses built-in 'crypto' for a random session id.
 3import http from 'http';
 4import crypto from 'crypto';
 5
 6const server = http.createServer((req, res) => {
 7  if (req.url === '/login') {
 8    const sessionId = crypto.randomBytes(16).toString('hex');
 9    // Set cookie with HttpOnly, Secure, SameSite and Path
10    // Expires is optional — Max-Age preferred for relative lifetimes.
11    res.setHeader('Set-Cookie', `sid=${sessionId}; `
12      + `HttpOnly; `
13      + `Secure; `
14      + `SameSite=Strict; `
15      + `Path=/; `
16      + `Max-Age=3600`
17    );
18    res.writeHead(302, { Location: '/' });
19    res.end();
20    return;
21  }
22
23  res.writeHead(200, { 'Content-Type': 'text/plain' });
24  res.end('Hello\n');
25});
26
27server.listen(3000, () => {
28  console.log('Server running on http://localhost:3000');
29});
  • Wenn Sie das Attribut HttpOnly serverseitig setzen, ist das Cookie von JavaScript aus nicht mehr zugänglich, was es XSS (Cross-Site Scripting)-Angriffen erschwert, es zu stehlen. Zusätzlich können Sie durch das Hinzufügen des Attributs Secure, sodass Cookies stets über HTTPS gesendet werden, Abhören und Manipulation verhindern und die Kommunikationssicherheit verbessern.

Implementierung für Cookie-Serialisierung/-Parsing (ein serverseitiger Helfer)

Nachfolgend eine einfache serverseitige Hilfsfunktion, die einen Set-Cookie-Header-String erzeugt.

 1// Cookie serialization helper for server-side use.
 2// Returns a properly formatted Set-Cookie header value.
 3type CookieOptions = {
 4  path?: string;
 5  domain?: string;
 6  maxAge?: number;
 7  expires?: Date;
 8  httpOnly?: boolean;
 9  secure?: boolean;
10  sameSite?: 'Strict' | 'Lax' | 'None';
11};
12
13const serializeCookie = (name: string, value: string, opts: CookieOptions = {}): string => {
14  const parts: string[] = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
15
16  if (opts.maxAge != null) parts.push(`Max-Age=${Math.floor(opts.maxAge)}`);
17  if (opts.expires) parts.push(`Expires=${opts.expires.toUTCString()}`);
18  if (opts.domain) parts.push(`Domain=${opts.domain}`);
19  parts.push(`Path=${opts.path ?? '/'}`);
20  if (opts.httpOnly) parts.push('HttpOnly');
21  if (opts.secure) parts.push('Secure');
22  if (opts.sameSite) parts.push(`SameSite=${opts.sameSite}`);
23
24  return parts.join('; ');
25};
26
27// Example usage:
28const headerValue = serializeCookie('uid', 'abc123', {
29  httpOnly: true,
30  secure: true,
31  sameSite: 'Lax',
32  maxAge: 3600
33});
34console.log(headerValue);
35// => "uid=abc123; Max-Age=3600; Path=/; HttpOnly; Secure; SameSite=Lax"
  • Dieses Hilfsprogramm bildet die Grundlage für das Erstellen von Set-Cookie auf einem eigenen Server. Bibliotheken decken weitaus mehr Randfälle ab.

Manipulationsschutz mit Signaturen (HMAC)

Es ist gefährlich, wichtige Werte direkt in Cookies zu speichern. Wir stellen eine Methode vor, die Werte serverseitig signiert, um Manipulationen zu erkennen. Hier verwenden wir HMAC-SHA256. Im Produktionsbetrieb müssen Sie den Token-Widerruf ebenfalls serverseitig verwalten.

 1// A simple HMAC signing and verification helper for cookies.
 2// Signing prevents client from tampering cookie values.
 3import crypto from 'crypto';
 4
 5const SECRET = 'replace_with_env_secret'; // store in env var in production
 6
 7const signValue = (value: string) => {
 8  const hmac = crypto.createHmac('sha256', SECRET);
 9  hmac.update(value);
10  return `${value}.${hmac.digest('hex')}`;
11};
12
13const verifySignedValue = (signed: string): string | null => {
14  const idx = signed.lastIndexOf('.');
15  if (idx === -1) return null;
16  const value = signed.slice(0, idx);
17  const sig = signed.slice(idx + 1);
18
19  const hmac = crypto.createHmac('sha256', SECRET);
20  hmac.update(value);
21  const expected = hmac.digest('hex');
22  // use timing-safe comparison in production
23  if (crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
24    return value;
25  }
26  return null;
27};
28
29// Example usage:
30const signed = signValue('userId=42');
31console.log('signed:', signed);
32console.log('verified:', verifySignedValue(signed)); // => "userId=42"
  • Mit dieser Methode können Sie prüfen, ob der Cookie-Wert manipuliert wurde. Fügen Sie den Signierschlüssel nicht in den Quellcode ein; verwalten Sie ihn auf sichere Weise, zum Beispiel über Umgebungsvariablen.

Cookies und CSRF-Abwehr (allgemeine Muster)

Sitzungen, die Cookies verwenden, benötigen CSRF-Schutz. Typische Gegenmaßnahmen sind:.

  • Setzen Sie SameSite auf Lax oder Strict, um unnötiges Senden über Websites hinweg zu verhindern. Verwenden Sie SameSite=None; Secure nur, wenn eine ursprungsübergreifende Kommunikation erforderlich ist.
  • Verwenden Sie CSRF-Token, die vom Server validiert werden, für Formulare und API-Anfragen. Speichern Sie das Token nicht in einem HttpOnly-Cookie; stellen Sie es stattdessen über den Antwortkörper oder Meta-Tags bereit und lassen Sie JavaScript es nur bei Bedarf auslesen.
  • Wenn auf das Token aus JavaScript zugegriffen werden kann, stellen Sie sicher, dass zudem XSS-Schutzmaßnahmen (CSP, Output-Escaping usw.) implementiert sind.
  • Verlangen Sie für sensible Aktionen und bei der Anmeldung eine erneute Authentifizierung und verhindern Sie Session-Fixation-Angriffe.

Im Folgenden finden Sie ein sicheres Client-Beispiel, das das CSRF-Token in einem Header sendet. Dabei wird angenommen, dass der Client das Token bereits vom Server erhalten hat, z. B. im Antwortkörper.

 1// Example of sending CSRF token safely in a header using fetch.
 2// Assumes CSRF token was provided securely (e.g., via response body or meta tag).
 3async function sendProtectedRequest(url: string, csrfToken: string) {
 4  const res = await fetch(url, {
 5    method: 'POST',
 6    credentials: 'same-origin', // include cookies for same-site requests
 7    headers: {
 8      'Content-Type': 'application/json',
 9      'X-CSRF-Token': csrfToken
10    },
11    body: JSON.stringify({ action: 'doSomething' })
12  });
13  return res;
14}
  • Wenn Sie credentials: 'same-origin' angeben, werden nur Cookies für dieselbe Herkunft gesendet. Validieren Sie auf dem Server den Wert des X-CSRF-Token-Headers und verifizieren Sie, dass das Token übereinstimmt. Wenn Cross-Site-Anfragen erforderlich sind, konfigurieren Sie CORS sorgfältig.

Cross-Site (CORS) und der Zusammenhang mit credentials

Um Cookies in einer ursprungsübergreifenden Kommunikation zu senden und zu empfangen, muss der Client credentials: 'include' angeben und der Server Access-Control-Allow-Credentials: true setzen. Beschränken Sie diese Konfiguration jedoch auf vertrauenswürdige Ursprünge und verwenden Sie nicht Access-Control-Allow-Origin: *.

 1async function fetchCrossOriginData() {
 2  // Example: cross-origin fetch sending cookies.
 3  // Server must set Access-Control-Allow-Credentials: true
 4  // and a specific trusted origin.
 5  const res = await fetch('https://api.example.com/data', {
 6    credentials: 'include', // send cookies only to trusted domains
 7    method: 'GET'
 8  });
 9  return res;
10}
  • Die Kombination aus CORS und Cookies ist aus Sicherheitsperspektive sehr heikel. Verwalten Sie erlaubte Ursprünge strikt mit einer Whitelist und vermeiden Sie unnötige Cross-Site-Kommunikation. Erzwingen Sie außerdem bei Verwendung von SameSite=None; Secure HTTPS, um Man-in-the-Middle-Angriffe zu verhindern.

Beispiel: Sichere Session-Cookie-Einstellungen mit Express (TypeScript)

In der Praxis verwenden Sie Express, die Bibliothek cookie, express-session usw. Unten ein einfaches Beispiel mit express und cookie-parser. In der Praxis setzen Sie secure auf true und verwalten das secret über Umgebungsvariablen.

 1// Express example using cookie-parser and setting a secure httpOnly cookie.
 2// npm install express cookie-parser @types/express @types/cookie-parser
 3import express from 'express';
 4import cookieParser from 'cookie-parser';
 5
 6const app = express();
 7app.use(cookieParser(process.env.COOKIE_SECRET));
 8
 9app.post('/login', (req, res) => {
10  // authenticate user (omitted)
11  const sessionId = 'generated-session-id';
12  res.cookie('sid', sessionId, {
13    httpOnly: true,
14    secure: true,        // require HTTPS in production
15    sameSite: 'lax',
16    maxAge: 1000 * 60 * 60 // 1 hour
17  });
18  res.json({ ok: true });
19});
20
21app.listen(3000);
  • Mit der secret-Funktion von cookieParser können Sie einfach mit signierten Cookies arbeiten. In realen Anwendungen sollten Sie aus Sicherheits- und Skalierbarkeitsgründen keine Daten direkt in Cookies speichern; verwenden Sie stattdessen einen dedizierten Session-Store.

Cookie-Präfixe __Host- und __Secure-

Browser erzwingen besondere Regeln für bestimmte Präfixe.

  • Das Präfix __Secure- Beginnt ein Cookie-Name mit __Secure-, ist das Attribut Secure erforderlich.
  • Das Präfix __Host- Beginnt er mit __Host-, ist Secure erforderlich, der Pfad muss / (Root) sein und Domain darf nicht gesetzt werden.

Die Verwendung dieser Präfixe reduziert Fehlkonfigurationen und verbessert die Sicherheit.

Cookie-Best Practices

Um Cookies sicher zu handhaben, beachten Sie die folgenden Punkte.

  • Speichern Sie keine sensiblen Informationen direkt in Cookies. Bevorzugen Sie für Zugriffstoken eine serverseitige Sitzungs-ID.
  • Setzen Sie Session-Cookies mit HttpOnly, Secure und SameSite=Lax (oder Strict).
  • Nutzen Sie Präfixe wie __Host- und __Secure-.
  • Erwägen Sie Signaturen (HMAC) und Verschlüsselung, um Manipulation und Abhören zu verhindern.
  • Aktivieren Sie Secure und erzwingen Sie HTTPS.
  • Streben Sie minimale Berechtigungen und kurze Ablaufzeiten an.
  • Verwenden Sie CSRF-Token.
  • Achten Sie auf Unterschiede im SameSite-Verhalten zwischen Browsern, insbesondere älteren.

Häufige Missverständnisse über Cookies

Bezüglich Cookies sollten Sie die folgenden verbreiteten Irrtümer beachten.

  • 'Das Hinzufügen von HttpOnly beseitigt die Auswirkungen von XSS.' Zwar verhindert HttpOnly den Zugriff auf Cookies aus JavaScript, dennoch kann XSS weiterhin genutzt werden, um beliebige Anfragen abzusetzen. Sie sollten außerdem CSRF-Token-Überprüfung, CSP (Content Security Policy) und Eingabebereinigung einsetzen.
  • 'Secure ist für die lokale Entwicklung unnötig.' Das Simulieren von HTTPS und die Überprüfung des Verhaltens auch in lokalen Umgebungen verbessert die Testgenauigkeit. Mindestens in Staging- und Produktionsumgebungen sollte HTTPS verwendet werden.
  • 'Lange Ablaufzeiten sind praktisch.' Wenn Sie lange Cookie-Lebensdauern festlegen, verlängert sich der Zeitraum, in dem sie bei Diebstahl missbraucht werden können. Sie können kürzere Ablaufzeiten festlegen und regelmäßige Re-Authentifizierung sowie Token-Rotation einführen.

Zusammenfassung

Obwohl Cookies leicht zu verwenden sind, kann eine falsche Handhabung Sicherheitslücken einführen. Um sie mit TypeScript korrekt zu verwalten, ist es wichtig, Attribute wie HttpOnly, Secure und SameSite zu verstehen und sichere serverseitige Einstellungen durchzusetzen. Indem Sie keine sensiblen Daten direkt speichern und Signaturen mit kurzen Ablaufzeiten kombinieren, erreichen Sie ein sicheres und zuverlässiges Sitzungsmanagement.

Sie können den obigen Artikel mit Visual Studio Code auf unserem YouTube-Kanal verfolgen. Bitte schauen Sie sich auch den YouTube-Kanal an.

YouTube Video