`Cookie` trong TypeScript
Bài viết này giải thích về Cookie trong TypeScript.
Chúng tôi trình bày các mẫu thực tiễn để xử lý cookie một cách an toàn và đáng tin cậy ở cả trình duyệt và máy chủ.
YouTube Video
Cookie trong TypeScript
Các khái niệm cơ bản về cookie
Cookie là cơ chế lưu trữ các chuỗi nhỏ (cặp name=value) trên phía client, được tạo thông qua header HTTP Set-Cookie hoặc document.cookie. Bạn có thể kiểm soát hành vi của chúng bằng các thuộc tính bảo mật (HttpOnly, Secure, SameSite, v.v.).
Các thao tác cơ bản trong trình duyệt: document.cookie
Dưới đây là ví dụ tối thiểu để ghi một cookie trong trình duyệt. Tạo cookie bằng cách nối thêm một chuỗi vào 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');- Đoạn mã này tạo một cookie hết hạn sau 7 ngày. Dễ sử dụng ở phía trình duyệt, nhưng vì bạn không thể đặt
HttpOnly, bạn nên tránh lưu trữ thông tin nhạy cảm.
Đọc cookie (trình duyệt)
Hàm sau đây là một hàm trợ giúp dùng để lấy giá trị của cookie có tên chỉ định từ document.cookie. document.cookie được trả về dưới dạng một chuỗi được phân tách bằng dấu chấm phẩy và một khoảng trắng ('; '). Vì vậy, chúng ta tách chuỗi đó và tìm cookie mong muốn.
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);- Hàm này giải mã cả tên và giá trị cookie để so sánh và lấy chúng một cách an toàn. Nó xử lý các tên cookie trùng lặp và các ký tự đặc biệt đã được mã hóa, khiến đây là một triển khai đơn giản nhưng mạnh mẽ.
Hiểu và áp dụng các thuộc tính bảo mật
Cookie có một số thuộc tính quan trọng, mỗi thuộc tính điều khiển bảo mật và phạm vi hoạt động.
- Thuộc tính
HttpOnlyngăn không cho cookie được truy cập từ JavaScript, giúp giảm thiểu các cuộc tấn công XSS (cross-site scripting). Lưu ý bạn không thể đặtHttpOnlytừ phía trình duyệt. - Thuộc tính
Securegiới hạn cookie chỉ được gửi qua HTTPS, giảm rủi ro bị nghe lén và sửa đổi. - Thuộc tính
SameSitekiểm soát việc cookie có được gửi kèm các yêu cầu cross-site hay không và giúp ngăn chặn các cuộc tấn công CSRF (cross-site request forgery). - Thuộc tính
Pathchỉ định phạm vi các đường dẫn yêu cầu mà cookie sẽ được gửi, và thuộc tínhDomainchỉ định miền mà cookie có hiệu lực. - Bằng cách đặt thuộc tính
ExpireshoặcMax-Age, bạn có thể kiểm soát thời hạn hết hiệu lực của cookie.
Ví dụ thêm SameSite / Secure trong trình duyệt (những gì có thể)
HttpOnly không thể được thiết lập ở phía máy khách. Dưới đây là một ví dụ về việc thêm SameSite và Secure ở phía máy khách. Tuy nhiên, Secure chỉ có hiệu lực trên các trang 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=Laxlà lựa chọn an toàn và phổ biến. Để hạn chế mạnh hơn hãy dùngSameSite=Strict, nhưng các điều hướng hợp lệ từ site bên ngoài có thể ngừng hoạt động.
Thiết lập cookie trên máy chủ (Set-Cookie) (Node / TypeScript)
Trên máy chủ bạn có thể thêm HttpOnly qua header Set-Cookie, vì vậy việc quản lý phiên (session) lý tưởng là thực hiện ở phía máy chủ. Dưới đây là ví dụ sử dụng module http của 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});- Khi bạn đặt thuộc tính
HttpOnlyở phía máy chủ, cookie sẽ không thể truy cập từ JavaScript, khiến các cuộc tấn công XSS (cross-site scripting) khó đánh cắp cookie hơn. Ngoài ra, bằng cách thêm thuộc tínhSecuređể cookie luôn được gửi qua HTTPS, bạn có thể ngăn chặn nghe lén và sửa đổi, đồng thời cải thiện bảo mật giao tiếp.
Triển khai tuần tự hóa/phân tích cú pháp cookie (trợ giúp phía máy chủ)
Dưới đây là một tiện ích đơn giản phía máy chủ dùng để tạo chuỗi tiêu đề 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"
- Tiện ích này là nền tảng để tạo
Set-Cookietrên máy chủ tùy chỉnh. Các thư viện có thể xử lý nhiều trường hợp biên hơn.
Ngăn ngừa giả mạo bằng chữ ký (HMAC)
Lưu trữ trực tiếp các giá trị quan trọng trong cookie là nguy hiểm. Chúng tôi giới thiệu một phương pháp ký giá trị trên máy chủ để phát hiện giả mạo. Ở đây chúng tôi dùng HMAC-SHA256. Trong môi trường sản xuất, bạn cũng cần quản lý việc thu hồi token ở phía máy chủ.
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"
- Với phương pháp này, bạn có thể kiểm tra liệu giá trị Cookie có bị giả mạo hay không. Không đưa khóa ký vào mã nguồn; hãy quản lý nó thông qua các phương thức an toàn như biến môi trường.
Cookie và giảm thiểu CSRF (các mẫu tổng quát)
Các phiên sử dụng cookie cần có bảo vệ CSRF. Các biện pháp giảm thiểu điển hình gồm:.
- Đặt
SameSitelàLaxhoặcStrictđể ngăn gửi chéo site không cần thiết. Chỉ sử dụngSameSite=None; Securekhi cần thiết phải giao tiếp khác nguồn gốc (cross-origin). - Sử dụng mã thông báo CSRF, được máy chủ xác thực, cho các biểu mẫu và yêu cầu API. Không lưu trữ mã thông báo trong cookie
HttpOnly; thay vào đó, cung cấp nó qua phần nội dung phản hồi (response body) hoặc thẻ meta, và chỉ để JavaScript đọc khi cần thiết. - Nếu mã thông báo có thể được truy cập từ JavaScript, hãy chắc chắn cũng triển khai các biện pháp bảo vệ XSS (CSP, thoát dữ liệu đầu ra, v.v.).
- Yêu cầu xác thực lại cho các hành động nhạy cảm và khi đăng nhập, đồng thời ngăn chặn các cuộc tấn công cố định phiên (session fixation).
Dưới đây là một ví dụ client an toàn gửi mã thông báo CSRF trong tiêu đề (header). Điều này giả định rằng client đã nhận được mã thông báo từ máy chủ, ví dụ trong phần nội dung phản hồ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}- Nếu bạn chỉ định
credentials: 'same-origin', chỉ các cookie cùng nguồn gốc mới được gửi. Trên máy chủ, xác thực giá trị của tiêu đềX-CSRF-Tokenvà xác minh rằng mã thông báo khớp. Nếu cần các yêu cầu khác nguồn gốc, hãy cấu hình CORS một cách cẩn thận.
Chéo site (CORS) và mối quan hệ với credentials
Để gửi và nhận cookie trong giao tiếp khác nguồn gốc, client phải chỉ định credentials: 'include' và máy chủ phải đặt Access-Control-Allow-Credentials: true. Tuy nhiên, hãy giới hạn cấu hình này cho các nguồn gốc đáng tin cậy và không sử dụng 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}- Sự kết hợp giữa CORS và cookie rất nhạy cảm về mặt bảo mật. Quản lý các nguồn gốc được phép một cách nghiêm ngặt bằng danh sách trắng (whitelist) và tránh giao tiếp khác nguồn gốc không cần thiết. Ngoài ra, khi sử dụng
SameSite=None; Secure, hãy bắt buộc dùng HTTPS để ngăn chặn các cuộc tấn công người ở giữa (man-in-the-middle).
Ví dụ: thiết lập cookie phiên an toàn với Express (TypeScript)
Trong thực tế, hãy sử dụng Express, thư viện cookie, express-session, v.v. Dưới đây là ví dụ đơn giản sử dụng express và cookie-parser. Trong thực tế, đặt secure là true và quản lý secret qua biến môi trường.
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);- Bằng cách sử dụng tính năng
secretcủacookieParser, bạn có thể dễ dàng làm việc với các cookie đã được ký. Tuy nhiên, trong các ứng dụng thực tế, xét về bảo mật và khả năng mở rộng, bạn không nên lưu trữ dữ liệu trực tiếp trong cookie; thay vào đó, hãy dùng một bộ lưu trữ phiên chuyên dụng.
Tiền tố cookie __Host- và __Secure-
Trình duyệt áp dụng các quy tắc đặc biệt cho một số tiền tố.
- Tiền tố
__Secure-Nếu tên cookie bắt đầu bằng__Secure-, thuộc tínhSecurelà bắt buộc. - Tiền tố
__Host-Nếu bắt đầu bằng__Host-, cần cóSecure, đường dẫn phải là/(gốc), và không được đặtDomain.
Sử dụng các tiền tố này giúp giảm sai cấu hình và cải thiện bảo mật.
Thực tiễn tốt nhất về cookie
Để xử lý Cookie một cách an toàn, hãy lưu ý các điểm sau.
- Không lưu trữ trực tiếp thông tin nhạy cảm trong cookie. Ưu tiên dùng ID phiên phía máy chủ cho access token.
- Đặt cookie phiên với
HttpOnly,SecurevàSameSite=Lax(hoặc Strict). - Tận dụng các tiền tố như
__Host-và__Secure-. - Cân nhắc chữ ký (HMAC) và mã hóa để ngăn giả mạo và nghe lén.
- Bật
Securevà yêu cầu HTTPS. - Hướng tới đặc quyền tối thiểu và thời hạn ngắn.
- Sử dụng token CSRF.
- Cẩn trọng với sự khác nhau về hành vi
SameSitegiữa các trình duyệt, đặc biệt là các trình duyệt cũ.
Những hiểu lầm phổ biến về cookie
Liên quan đến cookie, hãy lưu ý các hiểu lầm phổ biến sau.
- 'Thêm
HttpOnlysẽ loại bỏ tác động của XSS.' Mặc dùHttpOnlyngăn JavaScript truy cập cookie, XSS vẫn có thể được dùng để gửi các yêu cầu tùy ý. Bạn cũng nên áp dụng xác minh token CSRF, CSP (Chính sách Bảo mật Nội dung), và làm sạch dữ liệu đầu vào. - '
Securelà không cần thiết cho môi trường phát triển cục bộ.' Việc mô phỏng HTTPS và kiểm tra hành vi ngay cả trong môi trường cục bộ giúp cải thiện độ chính xác của kiểm thử. Tối thiểu, HTTPS nên được sử dụng trong các môi trường staging và production. - 'Thời gian hết hạn dài thì tiện lợi.' Nếu bạn đặt thời gian sống của cookie quá dài, khoảng thời gian chúng có thể bị lạm dụng khi bị đánh cắp sẽ tăng lên. Bạn có thể đặt thời hạn ngắn hơn và kết hợp xác thực lại định kỳ và xoay vòng token.
Tóm tắt
Mặc dù cookie dễ sử dụng, xử lý sai có thể dẫn tới lỗ hổng bảo mật. Để quản lý đúng trong TypeScript, điều quan trọng là hiểu các thuộc tính như HttpOnly, Secure và SameSite, và áp dụng nghiêm các thiết lập an toàn ở phía máy chủ. Bằng cách không lưu trữ trực tiếp dữ liệu nhạy cảm và kết hợp chữ ký với thời hạn ngắn, bạn có thể đạt được quản lý phiên an toàn và đáng tin cậy.
Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.