AldeaCode Logo
Security Dónde guardar un JWT: cookies o localStorage
Security 1 de mayo de 2026 AldeaCode Security

Dónde guardar un JWT: cookies o localStorage

Dónde guardar un JWT en 2026: cookies HttpOnly, sidecar legible, CSRF y XSS comparados. El patrón pragmático para manejar JWT en cookies, con código.

El problema de las cookies en una frase

Quieres que tu frontend sepa quién es el usuario, pero no quieres que un script que se cuela en tu página pueda robarle la sesión.

Esos dos objetivos pelean entre sí. Resolverlos de verdad cuesta más que el “mete el JWT en localStorage” que llenó los blogs hace cinco años.

Por qué localStorage siempre fue mala idea

Si guardas un token en localStorage, cualquier script que corra en tu página puede leerlo. Eso incluye scripts que no controlas: una etiqueta de analítica, un widget de terceros, un campo de comentarios que no escapó bien la entrada del usuario.

En el momento que cualquiera de esos scripts se compromete, el atacante lee el token y suplanta al usuario desde cualquier parte del mundo. El usuario no puede hacer nada, porque no hizo nada mal.

Esa es la razón entera por la que los navegadores inventaron las cookies con el flag HttpOnly. Una cookie HttpOnly se manda automáticamente con cada petición a tu dominio, pero el JavaScript de la página no puede leerla. Si entra un script malicioso, no puede robar la cookie. La superficie de fuga de sesión se reduce drásticamente.

En 2026, el patrón correcto se parece a esto:

  • El token de sesión (el JWT de larga duración o un id de sesión) vive en una cookie HttpOnly, Secure, SameSite=Lax. JavaScript no la puede tocar.
  • Un sidecar corto y no sensible guarda lo que tu UI necesita para renderizar: nombre del usuario, rol, un id corto. Este sidecar vive en una cookie normal o en localStorage. Está firmado pero no es secreto.
  • Tu backend lee la cookie HttpOnly en cada petición y decide qué hacer.

El frontend nunca tiene el token de verdad. Solo tiene lo justo para saber “este usuario se llama María, es admin, la sesión sigue viva”. Si la página se compromete, el atacante se lleva el nombre de María, no la cuenta de María.

Cuando necesites ver qué contiene el sidecar, pégalo en el decodificador JWT de AldeaCode. Todo corre en tu navegador, el sidecar no sale de tu pestaña.

CSRF: el precio de las cookies automáticas

El precio de usar cookies es que los navegadores las mandan automáticamente. Si la cookie de tu banco se manda en cada petición a banco.com, una web maliciosa puede engañar al navegador para que haga una petición a banco.com y la cookie va de paseo con la petición. Eso es falsificación de petición entre sitios (CSRF).

El arreglo son dos capas, las dos hacen falta:

  • SameSite=Lax (o Strict) en la cookie. El navegador se niega a mandar la cookie en peticiones cross-site para métodos que no son navegación. Esto bloquea el 95 por ciento de los CSRF sin que tengas que hacer nada más.
  • Un token CSRF que el backend emite, el frontend incluye en cada petición que cambie estado, y el backend verifica. Eso pilla el resto.

SameSite=Lax es el valor por defecto en navegadores modernos, pero deberías ponerlo explícito para que sobreviva a una actualización de framework que decida lo contrario.

XSS: sigue siendo la amenaza más grande

Cross-site scripting es todavía la forma en que se roban la mayoría de sesiones. Un atacante inyecta script en tu página, el script hace lo que tu página pueda hacer.

Aunque uses cookies HttpOnly, un XSS deja al atacante hacer lo que el usuario tiene permiso de hacer mientras está logueado, haciendo peticiones desde el propio navegador del usuario. No pueden leer la sesión, pero la pueden usar.

La defensa es por capas. Primero, sanitiza y escapa cada trozo de input del usuario. El limpiador HTML de AldeaCode ayuda cuando necesitas aplanar input rápido. Segundo, despliega una Content Security Policy que limite qué scripts pueden correr. Tercero, no te fíes de tu propio sistema de plantillas, vuelve a comprobar qué acaba en la página.

Una configuración práctica para 2026

El backend emite dos cookies al iniciar sesión:

  • session_token: HttpOnly, Secure, SameSite=Lax, expira entre 1 hora y 7 días según lo que toleres.
  • user_profile_jwt: cookie normal o localStorage, contiene datos de display no sensibles, firmada por el backend, vida corta.

El backend rota session_token en cada refresh, idealmente con ventana deslizante. El frontend lee user_profile_jwt para pintar nombres y roles, nunca depende de él para decisiones de seguridad. Token CSRF emitido y exigido en cualquier petición que no sea GET.

Cuando necesites inspeccionar qué está metiendo tu backend en el sidecar, el decodificador JWT y el codificador Base64 trabajan entero en tu navegador. El generador de hash es útil cuando necesitas confirmar que el contenido de la cookie no ha cambiado de forma entre despliegues.

Todo el enfoque asume que tu backend es la fuente de verdad. El frontend es una capa de presentación que recibe lo justo para parecer correcto, nunca lo suficiente para ser la frontera de seguridad. Esa es la única manera de evitar que XSS y CSRF conviertan un bug pequeño en una toma de cuenta.

Lo que hacemos

Webs honestas, sin atajos.

Ingeniería real y diseño cuidado. Si te ha gustado el post, hablemos del tuyo.

Hablemos →

Te puede interesar

Ver todos los artículos →