Ir al contenido principal

Cookies

Introducción

Las cookies son pequeños fragmentos de datos que los sitios web almacenan en el navegador del usuario. Fueron uno de los primeros mecanismos de almacenamiento web, existiendo mucho antes que localStorage y sessionStorage. A pesar de tener algunas limitaciones en comparación con las tecnologías más modernas, las cookies siguen siendo fundamentales en el desarrollo web actual debido a sus características únicas y su capacidad para ser enviadas automáticamente con las peticiones HTTP. Esto las hace especialmente útiles para mantener estados de sesión, realizar seguimiento y personalizar la experiencia del usuario entre diferentes visitas a un sitio web.

Concepto y propósito de las cookies

Las cookies fueron diseñadas para solucionar un problema fundamental: HTTP es un protocolo sin estado, lo que significa que cada petición es independiente y el servidor no puede "recordar" información de peticiones anteriores. Las cookies permiten mantener información entre peticiones, haciendo posible implementar:

  • Sesiones de usuario persistentes
  • Preferencias y configuración personalizada
  • Seguimiento del comportamiento del usuario
  • Autenticación y autorización
  • Carros de compra en tiendas online

A diferencia de localStorage y sessionStorage, que solo son accesibles desde JavaScript en el navegador, las cookies se envían automáticamente al servidor con cada petición HTTP al dominio que las creó.

Creación y lectura de cookies

Crear cookies con JavaScript

Para crear una cookie, asignamos un valor a la propiedad document.cookie. Esta propiedad tiene un comportamiento especial: escribir en ella no sobrescribe todas las cookies existentes, sino que añade o actualiza una cookie específica.

// Sintaxis básica para crear una cookie
document.cookie = "nombre=valor";

// Ejemplos prácticos
document.cookie = "usuario=Carlos";
document.cookie = "idioma=es";

Cada asignación a document.cookie añade o actualiza solo la cookie especificada, dejando intactas las demás cookies.

Leer cookies

// Obtener todas las cookies como string
const todasLasCookies = document.cookie;
console.log(todasLasCookies); // "usuario=Carlos; idioma=es"

Para acceder a una cookie específica, necesitamos parsear esta cadena:

// Función para obtener el valor de una cookie específica
function obtenerCookie(nombre) {
  const cookieString = document.cookie;
  const cookies = cookieString.split('; ');
  
  for (const cookie of cookies) {
    const [cookieNombre, cookieValor] = cookie.split('=');
    if (cookieNombre === nombre) {
      return decodeURIComponent(cookieValor);
    }
  }
  
  return null; // Cookie no encontrada
}

// Ejemplo de uso
const usuario = obtenerCookie('usuario'); // "Carlos"
const idioma = obtenerCookie('idioma');   // "es"

Codificación de valores

Es importante codificar los valores de las cookies para evitar problemas con caracteres especiales:

Opciones de configuración para cookies

Las cookies pueden tener varias opciones que controlan su duración, ámbito y seguridad:

Expiración (expires o max-age)

Define cuándo la cookie debe ser eliminada automáticamente:

Si no especificamos una fecha de expiración, se crea una "cookie de sesión" que se elimina cuando el usuario cierra el navegador.

Path

Define la ruta en el dominio para la que la cookie es válida:

Si no especificamos una ruta, la cookie solo será válida para la ruta actual y sus subrutas.

Domain

Especifica el dominio al que se aplica la cookie:

Si no se especifica, la cookie solo es válida para el dominio actual (sin incluir subdominios).

Secure

HttpOnly

Esta opción hace que la cookie sea inaccesible para JavaScript, añadiendo una capa de seguridad contra ataques XSS. Sin embargo, solo puede ser establecida desde el servidor, no con JavaScript del lado del cliente:

// Esto solo funciona cuando la cookie es establecida por el servidor
Set-Cookie: id=a3fWa; HttpOnly

SameSite

Controla cuándo se envían las cookies en peticiones cross-site:

Función para crear cookies con todas las opciones

Para simplificar la creación de cookies con diferentes opciones, podemos usar una función como esta:

/**
 * Crea una cookie con las opciones especificadas
 * @param {string} nombre - Nombre de la cookie
 * @param {string} valor - Valor de la cookie
 * @param {Object} opciones - Opciones de configuración
 */
function crearCookie(nombre, valor, opciones = {}) {
  // Opciones por defecto
  const opcionesCompletas = {
    path: '/',           // Por defecto, cookie válida para todo el sitio
    expires: null,       // Por defecto, cookie de sesión
    maxAge: null,        // Alternativa a expires
    domain: null,        // Por defecto, solo dominio actual
    secure: false,       // Por defecto, se envía también por HTTP
    sameSite: 'Lax',     // Valor predeterminado en navegadores modernos
    ...opciones
  };
  
  // Codificar el valor
  const valorCodificado = encodeURIComponent(valor);
  
  // Iniciar la construcción de la cookie
  let cookieString = `${nombre}=${valorCodificado}`;
  
  // Añadir path
  if (opcionesCompletas.path) {
    cookieString += `; path=${opcionesCompletas.path}`;
  }
  
  // Añadir expires si existe
  if (opcionesCompletas.expires) {
    if (opcionesCompletas.expires instanceof Date) {
      cookieString += `; expires=${opcionesCompletas.expires.toUTCString()}`;
    } else {
      throw new Error('La opción expires debe ser un objeto Date');
    }
  }
  
  // Añadir max-age si existe
  if (opcionesCompletas.maxAge !== null) {
    cookieString += `; max-age=${opcionesCompletas.maxAge}`;
  }
  
  // Añadir domain si existe
  if (opcionesCompletas.domain) {
    cookieString += `; domain=${opcionesCompletas.domain}`;
  }
  
  // Añadir secure si es true
  if (opcionesCompletas.secure) {
    cookieString += '; secure';
  }
  
  // Añadir SameSite
  if (opcionesCompletas.sameSite) {
    cookieString += `; SameSite=${opcionesCompletas.sameSite}`;
  }
  
  // Crear la cookie
  document.cookie = cookieString;
  
  return cookieString;
}

// Ejemplos de uso
// Cookie que expira en 7 días
const expiracion = new Date();
expiracion.setDate(expiracion.getDate() + 7);

crearCookie('usuario', 'Maria', {
  expires: expiracion,
  secure: true,
  sameSite: 'Strict'
});

// Cookie temporal que dura 1 hora
crearCookie('tempData', 'abc123', {
  maxAge: 3600,
  path: '/dashboard'
});

Eliminar cookies

Para eliminar una cookie, debemos establecerla de nuevo con la misma ruta y dominio, pero con una fecha de expiración en el pasado:

/**
 * Elimina una cookie específica
 * @param {string} nombre - Nombre de la cookie a eliminar
 * @param {Object} opciones - Opciones como path y domain (deben coincidir con la cookie original)
 */
function eliminarCookie(nombre, opciones = {}) {
  const opcionesEliminar = {
    ...opciones,
    expires: new Date(0), // Fecha en el pasado
    maxAge: 0             // Alternativa: expirar inmediatamente
  };
  
  crearCookie(nombre, '', opcionesEliminar);
}

// Ejemplo de uso
eliminarCookie('usuario', { path: '/' });

Es crucial especificar el mismo path y domain que se usaron al crear la cookie, o el navegador la tratará como una cookie diferente y no eliminará la original.

Cookies vs. almacenamiento web

Es importante entender las diferencias entre cookies y las tecnologías más modernas como localStorage y sessionStorage:

Característica Cookies localStorage sessionStorage
Capacidad máxima ~4KB por cookie, ~80KB total ~5-10MB ~5-10MB
Envío al servidor Automático con cada petición HTTP No se envían No se envían
Expiración Configurable o al cerrar sesión No expiran Al cerrar la pestaña
Accesibilidad JavaScript (excepto HttpOnly) y servidor Solo JavaScript Solo JavaScript
Ámbito Configurable (domain/path) Origen (dominio+protocolo+puerto) Pestaña/ventana específica
API Complicada (string único) Simple (clave-valor) Simple (clave-valor)

Esta comparación nos ayuda a elegir la tecnología adecuada según el caso de uso:

  • Cookies: Ideal para datos que necesitan enviarse al servidor, como identificadores de sesión.
  • localStorage: Perfecto para datos persistentes que solo necesita el cliente.
  • sessionStorage: Óptimo para datos temporales específicos de una sesión de navegación.

Limitaciones y tamaño máximo

Las cookies tienen varias limitaciones importantes:

  • Tamaño máximo: Aproximadamente 4KB por cookie.
  • Número máximo: La mayoría de los navegadores limitan a 50-150 cookies por dominio.
  • Límite total: Entre 4KB y 80KB dependiendo del navegador.
  • Overhead: Se envían con cada petición HTTP, consumiendo ancho de banda.

Para manejar objetos más grandes, podemos usar un enfoque similar al de localStorage:

/**
 * Guarda un objeto como cookie utilizando JSON
 * @param {string} nombre - Nombre de la cookie
 * @param {Object} objeto - Objeto a guardar
 * @param {Object} opciones - Opciones de la cookie
 */
function guardarObjetoComoCoookie(nombre, objeto, opciones = {}) {
  try {
    const jsonString = JSON.stringify(objeto);
    if (jsonString.length > 4000) {
      console.warn('El objeto es demasiado grande para una cookie (>4KB)');
    }
    crearCookie(nombre, jsonString, opciones);
  } catch (error) {
    console.error('Error al guardar objeto como cookie:', error);
  }
}

/**
 * Recupera un objeto almacenado como cookie
 * @param {string} nombre - Nombre de la cookie
 * @returns {Object|null} El objeto recuperado o null si no existe
 */
function obtenerObjetoDeCookie(nombre) {
  const cookieValor = obtenerCookie(nombre);
  if (!cookieValor) return null;
  
  try {
    return JSON.parse(cookieValor);
  } catch (error) {
    console.error('Error al parsear objeto desde cookie:', error);
    return null;
  }
}

// Ejemplo de uso
const preferenciasUsuario = {
  tema: 'oscuro',
  tamanoFuente: 14,
  notificaciones: true,
  configuracionPanel: {
    modulosVisibles: ['estadisticas', 'mensajes', 'tareas'],
    ordenModulos: ['tareas', 'mensajes', 'estadisticas']
  }
};

guardarObjetoComoCoookie('preferencias', preferenciasUsuario, {
  maxAge: 30 * 24 * 60 * 60, // 30 días en segundos
});

// Más tarde, recuperar el objeto
const preferenciasGuardadas = obtenerObjetoDeCookie('preferencias');
if (preferenciasGuardadas) {
  console.log('Tema:', preferenciasGuardadas.tema);
  console.log('Módulos visibles:', preferenciasGuardadas.configuracionPanel.modulosVisibles);
}

Cookies de terceros

Las cookies se clasifican en dos tipos según su origen:

  • Cookies de origen (first-party): Creadas por el dominio que visita el usuario.
  • Cookies de terceros (third-party): Creadas por dominios diferentes al visitado.

Las cookies de terceros han sido ampliamente utilizadas para seguimiento y publicidad, pero los navegadores modernos están restringiendo su uso:

  • Safari bloquea completamente las cookies de terceros.
  • Firefox bloquea cookies de terceros de rastreadores conocidos.
  • Chrome planea eliminar el soporte para cookies de terceros en el futuro.

Consideraciones de privacidad

El uso de cookies, especialmente para seguimiento, está sujeto a regulaciones de privacidad como el RGPD (Reglamento General de Protección de Datos) en Europa y la CCPA (Ley de Privacidad del Consumidor de California) en EE.UU.

Estas normativas generalmente requieren:

  1. Consentimiento informado: Informar claramente a los usuarios sobre qué cookies se utilizan y para qué.
  2. Opción de rechazar: Permitir a los usuarios rechazar cookies no esenciales.
  3. Acceso y control: Dar a los usuarios acceso a sus datos y control sobre ellos.

Ejemplo básico de banner de cookies

function mostrarBannerCookies() {
  // Comprobar si el usuario ya ha dado su consentimiento
  if (obtenerCookie('consentimientoCookies')) {
    // Si ya hay consentimiento, configurar cookies permitidas
    configurarCookiesSegunConsentimiento();
    return;
  }
  
  // Crear elementos del banner
  const banner = document.createElement('div');
  banner.className = 'banner-cookies';
  banner.innerHTML = `
    <div class="contenido-banner">
      <p>Utilizamos cookies para mejorar tu experiencia en nuestro sitio. 
      Para saber más, consulta nuestra <a href="/politica-cookies">política de cookies</a>.</p>
      <div class="botones-banner">
        <button id="aceptar-cookies">Aceptar todas</button>
        <button id="rechazar-cookies">Solo esenciales</button>
        <button id="personalizar-cookies">Personalizar</button>
      </div>
    </div>
  `;
  
  // Añadir banner al DOM
  document.body.appendChild(banner);
  
  // Eventos de los botones
  document.getElementById('aceptar-cookies').addEventListener('click', () => {
    guardarConsentimiento({
      esenciales: true,
      preferencias: true,
      estadisticas: true,
      marketing: true
    });
    banner.remove();
  });
  
  document.getElementById('rechazar-cookies').addEventListener('click', () => {
    guardarConsentimiento({
      esenciales: true,
      preferencias: false,
      estadisticas: false,
      marketing: false
    });
    banner.remove();
  });
  
  document.getElementById('personalizar-cookies').addEventListener('click', () => {
    mostrarOpcionesPersonalizadas(banner);
  });
}

function guardarConsentimiento(preferencias) {
  // Guardar preferencias como cookie
  guardarObjetoComoCoookie('consentimientoCookies', preferencias, {
    maxAge: 180 * 24 * 60 * 60, // 180 días
    sameSite: 'Strict'
  });
  
  // Configurar cookies según consentimiento
  configurarCookiesSegunConsentimiento();
}

function configurarCookiesSegunConsentimiento() {
  const consentimiento = obtenerObjetoDeCookie('consentimientoCookies');
  if (!consentimiento) return;
  
  // Ejemplo: configurar cookies de análisis si están permitidas
  if (consentimiento.estadisticas) {
    configurarCookiesAnalytics();
  }
  
  // Ejemplo: configurar cookies de marketing si están permitidas
  if (consentimiento.marketing) {
    configurarCookiesMarketing();
  }
}

// Funciones de ejemplo para configurar distintos tipos de cookies
function configurarCookiesAnalytics() {
  console.log('Configurando cookies de análisis');
  // Código para inicializar herramientas de análisis
}

function configurarCookiesMarketing() {
  console.log('Configurando cookies de marketing');
  // Código para inicializar herramientas de marketing
}

// Mostrar el banner cuando se carga la página
document.addEventListener('DOMContentLoaded', mostrarBannerCookies);

Este es un ejemplo básico que debería adaptarse según las necesidades específicas de cada proyecto y los requisitos legales aplicables.

La implementación correcta de un sistema de gestión de consentimiento de cookies va más allá del código JavaScript y debe considerar:

Resumen

Las cookies son una tecnología fundamental en el desarrollo web que permite mantener información entre distintas peticiones HTTP. Aunque tienen limitaciones en comparación con tecnologías más modernas como localStorage, siguen siendo esenciales para funcionalidades que requieren comunicación con el servidor.

El manejo de cookies en JavaScript requiere conocer su sintaxis específica para creación, lectura y eliminación, así como comprender las diferentes opciones de configuración como expiración, path, domain y atributos de seguridad.

Es crucial también entender las implicaciones de privacidad asociadas con el uso de cookies, especialmente en el contexto de las normativas actuales que requieren transparencia y consentimiento informado. Implementar un sistema adecuado de gestión de consentimiento no solo es una obligación legal en muchas jurisdicciones, sino también una buena práctica que respeta la privacidad de los usuarios.

Al utilizar cookies en nuestras aplicaciones web, debemos evaluar cuidadosamente si son la tecnología más adecuada para cada caso de uso específico, o si otras alternativas como localStorage o sessionStorage podrían ser más apropiadas para datos que no necesitan ser enviados al servidor.