Los tres mecanismos de almacenamiento
**Cookies**: pares de clave y valor que se envían con cada petición HTTP al dominio correspondiente. Las crea el servidor con la cabecera Set-Cookie o JavaScript con document.cookie.
**localStorage**: pares de clave y valor que se guardan de forma permanente en el navegador hasta que alguien los borra. Solo se acceden desde JavaScript y nunca se envían al servidor automáticamente.
**sessionStorage**: idéntico a localStorage, pero limitado a la pestaña del navegador y se borra al cerrarla.
Persistencia
| Se borra al cerrar la pestaña | No (salvo cookie de sesión) | No | Sí |
|---|---|---|---|
| Se borra al cerrar el navegador | No (salvo cookie de sesión) | No | Sí |
| Se borra al limpiar los datos | Sí | Sí | Sí |
| Tiene caducidad | Sí (`Expires` o `Max-Age`) | No | No |
| Tamaño máximo | Unos 4 KB | Entre 5 y 10 MB | Entre 5 y 10 MB |
Propiedades de seguridad
**Las cookies pueden ser httpOnly**: esta marca impide que JavaScript acceda a la cookie, que solo puede crear y leer el servidor. Es fundamental para los tokens de sesión: si tu cookie de autenticación es httpOnly, un ataque XSS con éxito no puede robarla, porque JavaScript no puede leerla.
**Las cookies pueden ser Secure**: solo se transmiten por conexiones HTTPS.
**Las cookies pueden llevar SameSite**: controla si la cookie se envía en peticiones desde otros sitios. SameSite=Strict solo la envía en peticiones del mismo origen (lo que evita el CSRF) y SameSite=Lax permite las peticiones GET de navegación principal.
**localStorage y sessionStorage no tienen protecciones equivalentes**: cualquier JavaScript que se ejecute en la página accede a todo su contenido. Una vulnerabilidad XSS en cualquier parte de tu sitio puede leer todo lo que haya en localStorage.
El problema de guardar el token de sesión
Guardar los tokens de sesión o los JWT en localStorage es un patrón muy extendido (seguro que has visto tutoriales que lo hacen). Resulta cómodo porque se accede fácilmente desde JavaScript y se puede inspeccionar en las herramientas de desarrollo.
El problema: cualquier ataque XSS, aunque venga de un script publicitario de terceros o de una dependencia de npm comprometida, puede leer todo lo que hay en localStorage. El ataque ni siquiera necesita encontrar un fallo en tu propio código: le basta con que un script cualquiera se ejecute en tu página.
**El enfoque seguro:** guarda los tokens de sesión en cookies con httpOnly; Secure; SameSite=Lax, que JavaScript nunca puede leer. La contrapartida es que tendrás que gestionar la protección contra CSRF, normalmente con un token secundario en una cookie que sí sea accesible.
Para qué sirve bien cada uno
- Tokens de autenticación de sesión (siempre con
httpOnly; Secure) - Estado entre peticiones que el servidor necesita leer
- Rastreo y analítica (requiere consentimiento donde se aplica el RGPD)
- Preferencias de quien usa la web (tema, idioma, estado de la interfaz)
- Borradores (datos de formulario sin guardar)
- Estado de la aplicación que no tenga que ver con la autenticación
- Caché de datos no sensibles
- Estado propio de una pestaña (formularios de varios pasos, progreso de un asistente)
- Datos que quieres borrar expresamente al cerrar la pestaña
- Información temporal de un único flujo de trabajo
Implicaciones de privacidad
Los tres mecanismos entran dentro del RGPD y normativas similares cuando se usan para rastrear. Las cookies funcionales (gestión de sesión, preferencias) no suelen requerir consentimiento; las de analítica y publicidad sí exigen una aceptación explícita en la Unión Europea.
Usar localStorage para rastrear está sujeto exactamente a los mismos requisitos de consentimiento que las cookies. "No usamos cookies" no significa "no te rastreamos" si se está usando localStorage en su lugar.
La regla: cualquier identificador persistente guardado en el navegador que sirva para seguir a una persona a lo largo del tiempo o entre páginas exige informar y, en muchos países, pedir consentimiento.