`Cookie` di TypeScript

`Cookie` di TypeScript

Artikel ini menjelaskan Cookie di TypeScript.

Kami membahas pola praktis untuk menangani cookie dengan aman dan andal baik di browser maupun di server.

YouTube Video

Cookie di TypeScript

Konsep dasar cookie

Cookie adalah mekanisme untuk menyimpan string kecil (pasangan name=value) pada klien, yang dibuat melalui header HTTP Set-Cookie atau document.cookie. Anda dapat mengontrol perilakunya dengan atribut keamanan (HttpOnly, Secure, SameSite, dll.).

Operasi dasar di browser: document.cookie

Di bawah ini adalah contoh minimal untuk menulis cookie di browser. Buat cookie dengan menambahkan string ke 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');
  • Kode ini membuat cookie yang kedaluwarsa dalam 7 hari. Ini mudah digunakan di sisi browser, tetapi karena Anda tidak dapat mengatur HttpOnly, Anda sebaiknya menghindari menyimpan informasi sensitif.

Membaca cookie (browser)

Fungsi berikut adalah fungsi pembantu yang mengambil nilai cookie dengan nama tertentu dari document.cookie. document.cookie dikembalikan sebagai string yang dipisahkan oleh tanda titik koma dan spasi ('; '). Karena itu, kita membaginya dan mencari cookie yang diinginkan.

 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);
  • Fungsi ini mendekode nama dan nilai cookie agar dapat membandingkan dan mengambilnya dengan aman. Ini menangani nama cookie yang duplikat dan karakter khusus yang terenkode, sehingga menjadi implementasi yang sederhana namun tangguh.

Memahami dan menerapkan atribut keamanan

Cookie memiliki beberapa atribut penting yang masing-masing mengendalikan keamanan dan ruang lingkup perilaku.

  • Atribut HttpOnly mencegah cookie diakses dari JavaScript, membantu memitigasi serangan XSS (cross-site scripting). Perhatikan bahwa Anda tidak dapat menetapkan HttpOnly dari sisi browser.
  • Atribut Secure membatasi agar cookie hanya dikirim melalui HTTPS, sehingga mengurangi risiko penyadapan dan manipulasi.
  • Atribut SameSite mengendalikan apakah cookie dikirim bersama permintaan lintas-situs dan membantu mencegah serangan CSRF (cross-site request forgery).
  • Atribut Path menentukan ruang lingkup path permintaan untuk mana cookie dikirim, dan atribut Domain menentukan domain tempat cookie berlaku.
  • Dengan menetapkan atribut Expires atau Max-Age, Anda dapat mengendalikan masa kedaluwarsa cookie.

Contoh menambahkan SameSite / Secure di browser (yang dapat dilakukan)

HttpOnly tidak dapat ditetapkan di sisi klien. Berikut adalah contoh menambahkan SameSite dan Secure di sisi klien. Namun, Secure hanya berlaku pada halaman HTTPS.

 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 adalah pilihan yang aman dan umum. Untuk pembatasan yang lebih ketat gunakan SameSite=Strict, namun navigasi yang sah dari situs eksternal dapat berhenti berfungsi.

Menetapkan cookie di server (Set-Cookie) (Node / TypeScript)

Di server Anda dapat menambahkan HttpOnly melalui header Set-Cookie, sehingga pengelolaan sesi idealnya dilakukan di sisi server. Berikut adalah contoh menggunakan modul http milik Node.

 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});
  • Saat Anda menetapkan atribut HttpOnly di sisi server, cookie menjadi tidak dapat diakses dari JavaScript, sehingga menyulitkan serangan XSS (cross-site scripting) untuk mencurinya. Selain itu, dengan menambahkan atribut Secure sehingga cookie selalu dikirim melalui HTTPS, Anda dapat mencegah penyadapan dan manipulasi serta meningkatkan keamanan komunikasi.

Implementasi serialisasi/penguraian cookie (pembantu sisi server)

Berikut adalah utilitas sisi server sederhana yang membangun string header Set-Cookie.

 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"
  • Utilitas ini menjadi dasar untuk membuat Set-Cookie pada server kustom. Pustaka dapat menangani jauh lebih banyak kasus tepi.

Mencegah pengubahan tidak sah dengan tanda tangan (HMAC)

Menyimpan nilai penting langsung di cookie itu berbahaya. Kami memperkenalkan metode yang menandatangani nilai di server untuk mendeteksi pengubahan tidak sah. Di sini kami menggunakan HMAC-SHA256. Di lingkungan produksi, Anda juga perlu mengelola pencabutan token di sisi server.

 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"
  • Dengan metode ini, Anda dapat memeriksa apakah nilai Cookie telah dimanipulasi. Jangan menyertakan kunci penandatanganan dalam kode sumber; kelola melalui cara yang aman seperti variabel lingkungan.

Cookie dan mitigasi CSRF (pola umum)

Sesi yang menggunakan cookie memerlukan perlindungan CSRF. Mitigasi yang umum meliputi:.

  • Atur SameSite ke Lax atau Strict untuk mencegah pengiriman lintas situs yang tidak perlu. Gunakan SameSite=None; Secure hanya ketika komunikasi lintas origin diperlukan.
  • Gunakan token CSRF, yang divalidasi oleh server, untuk formulir dan permintaan API. Jangan menyimpan token di cookie HttpOnly; alih-alih, sediakan melalui badan respons atau tag meta, dan biarkan JavaScript membacanya hanya ketika diperlukan.
  • Jika token dapat diakses dari JavaScript, pastikan juga menerapkan perlindungan XSS (CSP, escaping output, dll.).
  • Wajibkan autentikasi ulang untuk tindakan sensitif dan saat login, serta cegah serangan session fixation.

Di bawah ini adalah contoh klien yang aman yang mengirim token CSRF di header. Ini mengasumsikan klien sudah menerima token dari server, misalnya di badan respons.

 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}
  • Jika Anda menentukan credentials: 'same-origin', hanya cookie untuk origin yang sama yang dikirim. Di server, validasi nilai header X-CSRF-Token dan verifikasi bahwa tokennya cocok. Jika permintaan lintas situs diperlukan, konfigurasikan CORS dengan hati-hati.

Lintas situs (CORS) dan hubungannya dengan credentials

Untuk mengirim dan menerima cookie dalam komunikasi lintas origin, klien harus menetapkan credentials: 'include' dan server harus mengatur Access-Control-Allow-Credentials: true. Namun, batasi konfigurasi ini hanya untuk origin tepercaya dan jangan gunakan 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}
  • Kombinasi CORS dan cookie sangat sensitif dari perspektif keamanan. Kelola origin yang diizinkan secara ketat dengan daftar putih (whitelist) dan hindari komunikasi lintas situs yang tidak perlu. Selain itu, saat menggunakan SameSite=None; Secure, wajibkan HTTPS untuk mencegah serangan man-in-the-middle.

Contoh: pengaturan cookie sesi yang aman dengan Express (TypeScript)

Dalam praktiknya, gunakan Express, pustaka cookie, express-session, dan seterusnya. Berikut adalah contoh sederhana menggunakan express dan cookie-parser. Dalam praktiknya, setel secure ke true dan kelola secret melalui variabel lingkungan.

 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);
  • Dengan menggunakan fitur secret pada cookieParser, Anda dapat dengan mudah menangani cookie yang ditandatangani. Namun, dalam aplikasi nyata, dari perspektif keamanan dan skalabilitas, Anda sebaiknya tidak menyimpan data secara langsung di cookie; sebagai gantinya, gunakan penyimpanan sesi khusus.

Prefiks cookie __Host- dan __Secure-

Browser memberlakukan aturan khusus untuk prefiks tertentu.

  • Prefiks __Secure- Jika nama cookie diawali __Secure-, atribut Secure wajib ada.
  • Prefiks __Host- Jika diawali __Host-, Secure wajib, path harus / (root), dan Domain tidak boleh disetel.

Menggunakannya mengurangi salah konfigurasi dan meningkatkan keamanan.

Praktik terbaik cookie

Untuk menangani Cookie secara aman, pertimbangkan poin-poin berikut.

  • Jangan menyimpan informasi sensitif langsung di cookie. Lebih baik gunakan ID sesi sisi server untuk token akses.
  • Setel cookie sesi dengan HttpOnly, Secure, dan SameSite=Lax (atau Strict).
  • Manfaatkan prefiks seperti __Host- dan __Secure-.
  • Pertimbangkan tanda tangan (HMAC) dan enkripsi untuk mencegah pengubahan tidak sah dan penyadapan.
  • Aktifkan Secure dan wajibkan HTTPS.
  • Upayakan hak akses minimal dan masa kedaluwarsa singkat.
  • Gunakan token CSRF.
  • Waspadai perbedaan perilaku SameSite antar browser, terutama yang lama.

Kesalahpahaman umum tentang cookie

Terkait cookie, perhatikan miskonsepsi umum berikut.

  • 'Menambahkan HttpOnly menghilangkan dampak XSS.' Meskipun HttpOnly mencegah cookie diakses oleh JavaScript, XSS masih dapat digunakan untuk mengirim permintaan sewenang-wenang. Anda juga harus menerapkan verifikasi token CSRF, CSP (Content Security Policy), dan sanitasi input.
  • 'Secure tidak diperlukan untuk pengembangan lokal.' Mensimulasikan HTTPS dan memverifikasi perilaku bahkan di lingkungan lokal meningkatkan akurasi pengujian. Setidaknya, HTTPS harus digunakan di lingkungan staging dan produksi.
  • 'Masa kedaluwarsa yang panjang itu praktis.' Jika Anda menetapkan masa berlaku cookie yang panjang, periode saat cookie dapat disalahgunakan jika dicuri akan meningkat. Anda dapat menetapkan masa kedaluwarsa yang lebih pendek serta menerapkan autentikasi ulang berkala dan rotasi token.

Ringkasan

Meski cookie mudah digunakan, penanganan yang keliru dapat menimbulkan kerentanan keamanan. Untuk mengelolanya dengan benar menggunakan TypeScript, penting memahami atribut seperti HttpOnly, Secure, dan SameSite, serta menegakkan pengaturan sisi server yang aman. Dengan tidak menyimpan data sensitif secara langsung dan mengombinasikan tanda tangan dengan masa kedaluwarsa singkat, Anda dapat mencapai pengelolaan sesi yang aman dan andal.

Anda dapat mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Silakan periksa juga saluran YouTube kami.

YouTube Video