`Cookie` en TypeScript
Este artículo explica Cookie en TypeScript.
Recorremos patrones prácticos para manejar cookies de forma segura y confiable tanto en el navegador como en el servidor.
YouTube Video
Cookie en TypeScript
Conceptos básicos de las cookies
Una cookie es un mecanismo para almacenar cadenas pequeñas (pares nombre=valor) en el cliente, creada mediante la cabecera HTTP Set-Cookie o document.cookie. Puedes controlar su comportamiento con atributos de seguridad (HttpOnly, Secure, SameSite, etc.).
Operaciones básicas en el navegador: document.cookie
A continuación, el ejemplo mínimo para escribir una cookie en el navegador. Crea una cookie añadiendo una cadena a 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');- Este código crea una cookie que expira en 7 días. Es fácil de usar en el lado del navegador, pero como no puedes establecer
HttpOnly, deberías evitar almacenar información sensible.
Lectura de cookies (navegador)
La siguiente función es una función auxiliar que recupera el valor de la cookie con un nombre especificado de document.cookie. document.cookie se devuelve como una cadena delimitada por un punto y coma y un espacio ('; '). Por lo tanto, la dividimos y buscamos la cookie deseada.
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);- Esta función decodifica tanto el nombre como el valor de la cookie para compararlos y recuperarlos de forma segura. Maneja nombres de cookies duplicados y caracteres especiales codificados, lo que la convierte en una implementación simple pero robusta.
Comprender y aplicar atributos de seguridad
Las cookies tienen varios atributos importantes, y cada uno controla la seguridad y el alcance de su comportamiento.
- El atributo
HttpOnlyimpide que la cookie sea accesible desde JavaScript, lo que ayuda a mitigar los ataques XSS (cross-site scripting). Ten en cuenta que no puedes establecerHttpOnlydesde el lado del navegador. - El atributo
Securerestringe el envío de cookies únicamente a través de HTTPS, reduciendo el riesgo de intercepción y manipulación. - El atributo
SameSitecontrola si las cookies se envían con solicitudes entre sitios y ayuda a prevenir ataques CSRF (cross-site request forgery). - El atributo
Pathespecifica el alcance de las rutas de solicitud para las que se envía la cookie, y el atributoDomainespecifica el dominio donde la cookie es válida. - Al establecer el atributo
ExpiresoMax-Age, puedes controlar la caducidad de la cookie.
Ejemplo de añadir SameSite / Secure en el navegador (lo que es posible)
HttpOnly no puede configurarse en el lado del cliente. A continuación se muestra un ejemplo de cómo añadir SameSite y Secure en el lado del cliente. Sin embargo, Secure solo surte efecto en páginas 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=Laxes una opción segura y común. Para restricciones más estrictas usaSameSite=Strict, pero las navegaciones legítimas desde sitios externos pueden dejar de funcionar.
Configurar cookies en el servidor (Set-Cookie) (Node / TypeScript)
En el servidor puedes añadir HttpOnly mediante la cabecera Set-Cookie, por lo que la gestión de sesiones debería hacerse idealmente en el lado del servidor. A continuación, un ejemplo usando el módulo http de 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});- Cuando estableces el atributo
HttpOnlyen el lado del servidor, la cookie deja de ser accesible desde JavaScript, lo que dificulta que sea robada por ataques XSS (cross-site scripting). Además, al añadir el atributoSecurepara que las cookies siempre se envíen mediante HTTPS, puedes evitar la intercepción y la manipulación y mejorar la seguridad de la comunicación.
Implementación de serialización/análisis de cookies (un auxiliar del lado del servidor)
A continuación se muestra una utilidad sencilla del lado del servidor que construye una cadena de cabecera 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"
- Esta utilidad forma la base para crear
Set-Cookieen un servidor personalizado. Las bibliotecas pueden manejar muchos más casos límite.
Prevenir la manipulación con firmas (HMAC)
Es peligroso almacenar valores importantes directamente en cookies. Presentamos un método que firma valores en el servidor para detectar manipulaciones. Aquí usamos HMAC-SHA256. En producción, también debe gestionar la revocación de tokens en el lado del servidor.
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"
- Con este método, puede comprobar si el valor de la cookie ha sido manipulado. No incluya la clave de firma en el código fuente; gestiónela mediante medios seguros, como variables de entorno.
Cookies y mitigación de CSRF (patrones generales)
Las sesiones que usan cookies necesitan protección CSRF. Las mitigaciones típicas incluyen:.
- Configura
SameSiteenLaxoStrictpara evitar envíos entre sitios innecesarios. UtiliceSameSite=None; Securesolo cuando la comunicación entre orígenes sea necesaria. - Utilice tokens CSRF, validados por el servidor, para formularios y solicitudes de API. No almacene el token en una cookie
HttpOnly; en su lugar, proporciónelo mediante el cuerpo de la respuesta o etiquetas meta, y haga que JavaScript lo lea solo cuando sea necesario. - Si el token es accesible desde JavaScript, asegúrese de implementar también protecciones contra XSS (CSP, escapado de salida, etc.).
- Exija la reautenticación para las acciones sensibles y en el inicio de sesión, y prevenga los ataques de fijación de sesión.
A continuación se muestra un ejemplo de cliente seguro que envía el token CSRF en un encabezado. Esto supone que el cliente ya ha recibido el token del servidor, p. ej., en el cuerpo de la respuesta.
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}- Si especifica
credentials: 'same-origin', solo se envían las cookies del mismo origen. En el servidor, valide el valor del encabezadoX-CSRF-Tokeny verifique que el token coincida. Si son necesarias solicitudes entre sitios, configure CORS con cuidado.
Entre sitios (CORS) y la relación con credentials
Para enviar y recibir cookies en la comunicación entre orígenes, el cliente debe especificar credentials: 'include' y el servidor debe establecer Access-Control-Allow-Credentials: true. Sin embargo, restrinja esta configuración a orígenes de confianza y no use 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}- La combinación de CORS y cookies es muy delicada desde el punto de vista de la seguridad. Gestione estrictamente los orígenes permitidos con una lista blanca y evite la comunicación entre sitios innecesaria. Además, al usar
SameSite=None; Secure, exija HTTPS para prevenir ataques de intermediario (MITM).
Ejemplo: configuración segura de cookies de sesión con Express (TypeScript)
En la práctica, usa Express, la biblioteca cookie, express-session, entre otros. A continuación, un ejemplo sencillo usando express y cookie-parser. En la práctica, establece secure en true y gestiona secret mediante variables de entorno.
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);- Al usar la función
secretdecookieParser, puedes trabajar fácilmente con cookies firmadas. Sin embargo, en aplicaciones reales, desde las perspectivas de seguridad y escalabilidad, no deberías almacenar datos directamente en cookies; en su lugar, usa un almacén de sesiones dedicado.
Prefijos de cookies __Host- y __Secure-
Los navegadores aplican reglas especiales para ciertos prefijos.
- El prefijo
__Secure-Si un nombre de cookie empieza con__Secure-, el atributoSecurees obligatorio. - El prefijo
__Host-Si empieza con__Host-,Securees obligatorio, la ruta debe ser/(raíz) y no se debe establecerDomain.
Usarlos reduce la mala configuración y mejora la seguridad.
Buenas prácticas para cookies
Para manejar las cookies de forma segura, tenga en cuenta los siguientes puntos.
- No almacenes información sensible directamente en cookies. Prefiere un ID de sesión del lado del servidor para los tokens de acceso.
- Configura cookies de sesión con
HttpOnly,SecureySameSite=Lax(o Strict). - Aprovecha prefijos como
__Host-y__Secure-. - Considera firmas (HMAC) y cifrado para prevenir manipulación y espionaje.
- Habilita
Securey exige HTTPS. - Apunta al mínimo privilegio y a expiraciones cortas.
- Usa tokens CSRF.
- Cuidado con las diferencias en el comportamiento de
SameSiteentre navegadores, especialmente los más antiguos.
Conceptos erróneos comunes sobre las cookies
Con respecto a las cookies, ten en cuenta los siguientes malentendidos comunes.
- 'Añadir
HttpOnlyelimina el impacto de XSS.' Si bienHttpOnlyimpide que JavaScript acceda a las cookies, XSS aún puede utilizarse para realizar solicitudes arbitrarias. También deberías emplear verificación de tokens CSRF, CSP (Content Security Policy) y sanitización de entradas. - '
Securees innecesario para el desarrollo local.' Simular HTTPS y verificar el comportamiento incluso en entornos locales mejora la precisión de las pruebas. Como mínimo, se debe usar HTTPS en los entornos de staging y producción. - 'Los periodos de caducidad largos son convenientes.' Si estableces vidas útiles largas para las cookies, aumenta el periodo durante el cual pueden ser utilizadas indebidamente si son robadas. Puedes establecer caducidades más cortas e incorporar reautenticación periódica y rotación de tokens.
Resumen
Aunque las cookies son fáciles de usar, un manejo incorrecto puede introducir vulnerabilidades de seguridad. Para gestionarlas correctamente con TypeScript, es importante comprender atributos como HttpOnly, Secure y SameSite, y aplicar configuraciones seguras en el servidor. Al no almacenar datos sensibles directamente y combinar firmas con expiraciones cortas, puedes lograr una gestión de sesiones segura y confiable.
Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.