`Cookie` i TypeScript

`Cookie` i TypeScript

Denne artikel forklarer Cookie i TypeScript.

Vi gennemgår praktiske mønstre til at håndtere cookies sikkert og pålideligt både i browseren og på serveren.

YouTube Video

Cookie i TypeScript

Grundlæggende begreber om cookies

En cookie er en mekanisme til at gemme små strenge (navn=værdi-par) på klienten, oprettet via HTTP-headeren Set-Cookie eller document.cookie. Du kan styre deres adfærd med sikkerhedsattributter (HttpOnly, Secure, SameSite osv.).

Grundlæggende operationer i browseren: document.cookie

Nedenfor er det minimale eksempel på at skrive en cookie i browseren. Opret en cookie ved at tilføje en streng til document.cookie.

 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');
  • Denne kode opretter en cookie, der udløber om 7 dage. Det er let at bruge på browsersiden, men da du ikke kan sætte HttpOnly, bør du undgå at gemme følsomme oplysninger.

Læsning af cookies (browser)

Følgende funktion er en hjælpefunktion, der henter værdien af den cookie med et angivet navn fra document.cookie. document.cookie returneres som en streng, der er adskilt af et semikolon og et mellemrum ('; '). Derfor deler vi den op og søger efter den ønskede 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);
  • Denne funktion afkoder både cookiens navn og værdi for sikkert at kunne sammenligne og hente dem. Den håndterer duplikerede cookienavne og kodede specialtegn, hvilket gør det til en enkel, men robust implementering.

Forståelse og anvendelse af sikkerhedsattributter

Cookies har flere vigtige attributter, som hver især styrer sikkerheden og anvendelsesområdet.

  • Attributten HttpOnly forhindrer, at cookien er tilgængelig fra JavaScript, hvilket hjælper med at afbøde XSS (cross-site scripting)-angreb. Bemærk, at du ikke kan sætte HttpOnly fra browsersiden.
  • Attributten Secure begrænser cookies til kun at blive sendt over HTTPS, hvilket reducerer risikoen for aflytning og manipulation.
  • Attributten SameSite styrer, om cookies sendes med forespørgsler på tværs af websteder, og hjælper med at forhindre CSRF (cross-site request forgery)-angreb.
  • Attributten Path angiver, for hvilke anmodningsstier cookien sendes, og attributten Domain angiver det domæne, hvor cookien er gyldig.
  • Ved at sætte attributten Expires eller Max-Age kan du styre cookiens udløb.

Eksempel på at tilføje SameSite / Secure i browseren (hvad der er muligt)

HttpOnly kan ikke sættes på klientsiden. Nedenfor er et eksempel på at tilføje SameSite og Secure på klientsiden. Dog gælder Secure kun på HTTPS-sider.

 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 er et sikkert og almindeligt valg. For stærkere restriktioner brug SameSite=Strict, men legitime navigationer fra eksterne sider kan stoppe med at virke.

Indstilling af cookies på serveren (Set-Cookie) (Node / TypeScript)

På serveren kan du tilføje HttpOnly via Set-Cookie-headeren, så sessionstyring bør ideelt set ske på serversiden. Nedenfor er et eksempel med 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});
  • Når du sætter attributten HttpOnly på serversiden, bliver cookien utilgængelig fra JavaScript, hvilket gør det sværere for XSS (cross-site scripting)-angreb at stjæle den. Derudover kan du ved at tilføje attributten Secure, så cookies altid sendes over HTTPS, forhindre aflytning og manipulation og forbedre kommunikationssikkerheden.

Implementering af serialisering/parsing af cookies (en serverside-hjælpefunktion)

Nedenfor er et simpelt værktøj på serversiden, der bygger en Set-Cookie-headerstreng.

 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"
  • Dette værktøj danner grundlaget for at oprette Set-Cookie på en skræddersyet server. Biblioteker kan håndtere mange flere kanttilfælde.

Forebyg forfalskning med signaturer (HMAC)

Det er farligt at gemme vigtige værdier direkte i cookies. Vi introducerer en metode, der signerer værdier på serveren for at opdage manipulation. Her bruger vi HMAC-SHA256. I produktion skal du også administrere tilbagekaldelse af tokens på serversiden.

 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"
  • Med denne metode kan du kontrollere, om cookie-værdien er blevet manipuleret. Medtag ikke signeringsnøglen i kildekoden; administrer den via sikre metoder såsom miljøvariabler.

Cookies og CSRF-afværgning (generelle mønstre)

Sessioner, der bruger cookies, har brug for CSRF-beskyttelse. Typiske afværgninger omfatter:.

  • Sæt SameSite til Lax eller Strict for at forhindre unødvendig cross-site-afsendelse. Brug SameSite=None; Secure kun når cross-origin-kommunikation er nødvendig.
  • Brug CSRF-tokens, som valideres af serveren, til formularer og API-anmodninger. Gem ikke tokenet i en HttpOnly-cookie; giv det i stedet via response body eller meta-tags, og lad JavaScript kun læse det, når det er nødvendigt.
  • Hvis tokenet kan tilgås fra JavaScript, skal du også implementere XSS-beskyttelser (CSP, output-escaping osv.).
  • Kræv genautentificering for følsomme handlinger og ved login, og forebyg session-fixation-angreb.

Nedenfor er et sikkert klienteksempel, der sender CSRF-tokenet i en header. Dette forudsætter, at klienten allerede har modtaget tokenet fra serveren, f.eks. i response body.

 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}
  • Hvis du angiver credentials: 'same-origin', sendes kun cookies for samme origin. På serveren skal du validere værdien af X-CSRF-Token-headeren og verificere, at tokenet matcher. Hvis cross-site-forespørgsler er nødvendige, skal du konfigurere CORS omhyggeligt.

Cross-site (CORS) og forholdet til credentials

For at sende og modtage cookies i cross-origin-kommunikation skal klienten angive credentials: 'include', og serveren skal sætte Access-Control-Allow-Credentials: true. Begræns dog denne konfiguration til betroede origins, og brug ikke 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}
  • Kombinationen af CORS og cookies er meget følsom fra et sikkerhedsmæssigt perspektiv. Administrer tilladte origins strengt med en whitelist, og undgå unødvendig cross-site-kommunikation. Håndhæv desuden HTTPS, når du bruger SameSite=None; Secure, for at forhindre man-in-the-middle-angreb.

Eksempel: sikre indstillinger for sessionscookie med Express (TypeScript)

I praksis kan du bruge Express, cookie-biblioteket, express-session osv. Nedenfor er et simpelt eksempel med express og cookie-parser. I praksis skal secure sættes til true, og secret skal styres via miljøvariabler.

 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);
  • Ved at bruge cookieParsers secret-funktion kan du nemt arbejde med signerede cookies. I virkelige applikationer bør du dog af hensyn til sikkerhed og skalerbarhed ikke gemme data direkte i cookies; brug i stedet et dedikeret sessionslager.

Cookie-præfikserne __Host- og __Secure-

Browsere håndhæver særlige regler for visse præfikser.

  • Præfikset __Secure- Hvis et cookienavn starter med __Secure-, er Secure-attributten påkrævet.
  • Præfikset __Host- Hvis det starter med __Host-, er Secure påkrævet, stien skal være / (rod), og Domain må ikke sættes.

Brug af disse reducerer fejlkonfigurationer og forbedrer sikkerheden.

Bedste praksis for cookies

For at håndtere cookies sikkert, overvej følgende punkter.

  • Gem ikke følsomme oplysninger direkte i cookies. Foretræk et serverside-sessions-id til adgangstokens.
  • Sæt sessionscookies med HttpOnly, Secure og SameSite=Lax (eller Strict).
  • Udnyt præfikser som __Host- og __Secure-.
  • Overvej signaturer (HMAC) og kryptering for at forhindre manipulation og aflytning.
  • Aktiver Secure og kræv HTTPS.
  • Tilstræb mindst mulige privilegier og korte udløbstider.
  • Brug CSRF-tokens.
  • Vær opmærksom på forskelle i SameSite-adfærd på tværs af browsere, især ældre.

Almindelige misforståelser om cookies

Vedrørende cookies skal du være opmærksom på følgende almindelige misforståelser.

  • 'Tilføjelse af HttpOnly fjerner virkningen af XSS.' Selvom HttpOnly forhindrer adgang til cookies fra JavaScript, kan XSS stadig bruges til at udføre vilkårlige forespørgsler. Du bør også anvende CSRF-tokenvalidering, CSP (Content Security Policy) og sanitering af input.
  • 'Secure er unødvendig til lokal udvikling.' At simulere HTTPS og verificere adfærd selv i lokale miljøer forbedrer testnøjagtigheden. Som minimum bør HTTPS bruges i staging- og produktionsmiljøer.
  • 'Lange udløbstider er bekvemme.' Hvis du sætter lange cookie-levetider, øges den periode, hvor de kan misbruges, hvis de bliver stjålet. Du kan sætte kortere udløb og indføre periodisk genautentificering og tokenrotation.

Sammendrag

Selvom cookies er nemme at bruge, kan forkert håndtering introducere sikkerhedssårbarheder. For at håndtere dem korrekt med TypeScript er det vigtigt at forstå attributter som HttpOnly, Secure og SameSite og håndhæve sikre serverside-indstillinger. Ved ikke at gemme følsomme data direkte og kombinere signaturer med korte udløbstider kan du opnå sikker og pålidelig sessionstyring.

Du kan følge med i ovenstående artikel ved hjælp af Visual Studio Code på vores YouTube-kanal. Husk også at tjekke YouTube-kanalen.

YouTube Video