JWT Tokens Explained: Structure, Security & Decoding

Understand JSON Web Tokens from the inside out. Learn the header, payload, and signature structure, common claims, signing algorithms, and security best practices.

Equipo JSONTechFebruary 1, 202510 min read

¿Qué es un JWT?

Un JSON Web Token (JWT, pronunciado «jot») es una cadena compacta y segura para URL que representa afirmaciones (claims) entre dos partes. Es la forma más habitual de manejar autenticación y autorización en aplicaciones web modernas.

Cuando inicias sesión en una aplicación web y el servidor te devuelve un token en lugar de crear una sesión del lado del servidor, lo más probable es que ese token sea un JWT. Tu navegador lo envía en cada solicitud posterior, y el servidor verifica la firma sin consultar una base de datos.

Dónde se usan los JWT

  • Autenticación. Tras iniciar sesión, el servidor emite un JWT. El cliente lo almacena y lo envía en el encabezado Authorization en cada solicitud.
  • Inicio de sesión único (SSO). Los JWT permiten que los usuarios se autentiquen una vez y accedan a varios servicios. Proveedores de identidad como Auth0, Okta y Keycloak emiten JWT que los servicios posteriores pueden verificar de forma independiente.
  • Autorización de API. Los microservicios intercambian JWT entre sí para demostrar que una solicitud se hizo en nombre de un usuario autenticado con permisos concretos.
  • Intercambio de información. Al estar firmados, los JWT pueden transportar datos confiables entre partes sin un paso adicional de verificación.

Las tres partes de un JWT

Todo JWT consta de tres partes codificadas en Base64URL separadas por puntos:

header.payload.signature

Aquí hay un JWT real (acortado para facilitar la lectura):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Decodifiquemos cada parte.

Parte 1: Encabezado

El encabezado describe el tipo de token y el algoritmo de firma. Decodificado desde Base64URL:

{
  "alg": "HS256",
  "typ": "JWT"
}

alg indica al verificador qué algoritmo usar al comprobar la firma. typ confirma que es un JWT (en oposición a un JWE u otro tipo de token).

Parte 2: Carga útil (claims)

La carga útil contiene los claims: los datos reales que transporta el token. Decodificada:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

Los claims son pares clave-valor. Algunos están estandarizados (registered claims) y puedes añadir los claims personalizados que necesites.

La carga útil está codificada en Base64URL, no cifrada. Cualquiera que tenga el token puede decodificarla y leer la carga útil. Nunca pongas secretos, contraseñas ni datos personales sensibles en el payload de un JWT.

Parte 3: Firma

La firma se calcula tomando el encabezado codificado, un punto, la carga útil codificada, y firmándolo con una clave secreta:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

La firma garantiza que el token no ha sido alterado. Si alguien cambia un solo carácter en el encabezado o en la carga útil, la firma no coincidirá y el verificador rechazará el token.

Pruébalo tú mismo: Pega cualquier JWT en nuestro decodificador JWT para inspeccionar las tres partes al instante.

Claims comunes en JWT

La especificación JWT (RFC 7519) define un conjunto de registered claims. Ninguno es obligatorio, pero están muy extendidos:

ClaimNombre completoDescripción
subSujetoSobre quién trata el token (normalmente un ID de usuario)
issEmisorQuién emitió el token (p. ej., la URL de tu servidor de autenticación)
audAudienciaDestinatario previsto (p. ej., la URL de tu API)
expFecha de expiraciónMarca de tiempo Unix tras la cual el token no es válido
iatEmitido enMarca de tiempo Unix en que se creó el token
nbfNo válido antes deEl token no es válido antes de esta marca de tiempo Unix
jtiID del JWTIdentificador único para evitar el reuso del token

Los claims personalizados son cualquier otro que añadas. Ejemplos habituales: role, permissions, email y org_id. Mantén los claims personalizados al mínimo: cada byte extra se envía en cada solicitud.

Algoritmos de firma comparados

La elección del algoritmo determina cómo se crea y verifica la firma. Estas son las tres opciones más comunes:

AlgoritmoTipoClaveIdeal para
HS256Simétrico (HMAC)Un único secreto compartidoConfiguraciones sencillas donde emisor y verificador son el mismo servicio
RS256Asimétrico (RSA)La clave privada firma, la pública verificaSistemas distribuidos, SSO: los verificadores nunca necesitan la clave privada
ES256Asimétrico (ECDSA)La clave privada firma, la pública verificaIgual que RS256 pero con claves más pequeñas y operaciones más rápidas

Simétrico (HS256) es más sencillo de configurar: emisor y verificador comparten el mismo secreto. La desventaja es que todo servicio que deba verificar tokens debe tener acceso al secreto, lo que amplía la superficie de ataque.

Asimétrico (RS256, ES256) encaja mejor en arquitecturas distribuidas. El servidor de autenticación firma con una clave privada y cualquier servicio puede verificar con la clave pública correspondiente, que es seguro distribuir. ES256 es la opción moderna: seguridad equivalente a RS256 con tokens más pequeños y verificación más rápida.

Buenas prácticas de seguridad

Los JWT son una herramienta potente, pero es fácil usarlos mal. Sigue estas reglas:

  • Nunca guardes secretos en la carga útil. La carga útil está codificada, no cifrada. Cualquiera puede decodificarla. Si necesitas tokens cifrados, usa JWE (JSON Web Encryption).
  • Usa tiempos de expiración cortos. Los access tokens deberían caducar en 5–15 minutos. Usa refresh tokens para sesiones más largas. Los tokens de vida corta limitan el daño si uno es robado.
  • Usa siempre HTTPS. Los JWT enviados por HTTP pueden interceptarse en tránsito. HTTPS no es negociable.
  • Valida todos los claims. Comprueba siempre exp, iss y aud. No basta con verificar la firma: un token expirado o mal dirigido debe rechazarse.
  • No aceptes alg: none. Algunas bibliotecas aceptan tokens sin firmar si el encabezado dice "alg": "none". Rechaza explícitamente este algoritmo en la configuración del verificador.
  • Usa una lista permitida de algoritmos. Configura el verificador para aceptar solo el o los algoritmos que esperas (p. ej., solo RS256). Esto previene ataques de confusión de algoritmo.
  • Almacena los tokens de forma segura. Las cookies HttpOnly son más seguras que localStorage porque no son accesibles desde JavaScript (mitigan ataques XSS). Si usas localStorage, entiende los compromisos.

Errores frecuentes con JWT

Son patrones que aparecen una y otra vez en auditorías de seguridad:

  • Poner el JWT en la URL. Las cadenas de consulta acaban en registros del servidor, historial del navegador y encabezados referrer. Usa el encabezado Authorization o una cookie.
  • No validar la firma en absoluto. Algunos desarrolladores decodifican la carga útil con una biblioteca Base64 y omiten la verificación. Eso permite que un atacante falsifique cualquier token.
  • Usar un secreto débil para HS256. Si el secreto compartido es "secret" o "password123", puede forzarse por fuerza bruta en segundos. Usa una cadena criptográficamente aleatoria de al menos 256 bits.
  • Tokens que nunca expiran. Si un token no tiene claim exp, es válido para siempre. Si ese token se filtra, no hay forma de revocarlo sin cambiar la clave de firma (lo que invalida todos los tokens).
  • Guardar demasiados datos. Los JWT se envían en cada solicitud HTTP. Un token de 4 KB añade sobrecarga a cada llamada a la API. Mantén la carga útil ligera.

Flujo de renovación de tokens

Los access tokens de vida corta caducan pronto, así que necesitas una forma de obtener otros sin obligar al usuario a iniciar sesión de nuevo. Ese es el flujo del refresh token:

  1. El usuario inicia sesión. El servidor devuelve un access token (de vida corta, p. ej., 15 minutos) y un refresh token (de vida larga, p. ej., 7 días).
  2. El cliente envía el access token en cada solicitud a la API en el encabezado Authorization: Bearer.
  3. Cuando el access token expira, la API responde con 401 Unauthorized.
  4. El cliente envía el refresh token a un endpoint dedicado /refresh.
  5. El servidor valida el refresh token, emite un nuevo access token (y opcionalmente un nuevo refresh token: esto se llama rotación de refresh tokens) y los devuelve.
  6. El cliente reintenta la solicitud original con el nuevo access token.

La rotación de refresh tokens es una buena práctica de seguridad: cada refresh token solo puede usarse una vez. Si un atacante roba un refresh token y el usuario legítimo también intenta usarlo, el servidor detecta el reuso e invalida toda la sesión.

// Simplified refresh flow (client-side)
const handleApiRequest = async (url, options) => {
  let response = await fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      Authorization: `Bearer ${getAccessToken()}`,
    },
  });

  if (response.status === 401) {
    const refreshResponse = await fetch("/api/refresh", {
      method: "POST",
      body: JSON.stringify({ refresh_token: getRefreshToken() }),
    });

    if (refreshResponse.ok) {
      const { access_token, refresh_token } = await refreshResponse.json();
      saveTokens(access_token, refresh_token);

      response = await fetch(url, {
        ...options,
        headers: {
          ...options.headers,
          Authorization: `Bearer ${access_token}`,
        },
      });
    } else {
      redirectToLogin();
    }
  }

  return response;
};

JWT frente a sesiones: cuándo usar cada uno

Es uno de los temas más debatidos en el desarrollo web. Ambos enfoques tienen usos legítimos:

AspectoJWTSesiones del lado del servidor
Almacenamiento de estadoCliente (servidor sin estado)Servidor (almacén de sesiones / base de datos)
EscalabilidadSencilla: no hay estado compartido entre servidoresRequiere almacén de sesiones compartido (Redis, BD)
RevocaciónDifícil: hace falta lista de bloqueo o expiración cortaSencilla: borrar la sesión del almacén
Tamaño de la cargaMayor (lleva claims en cada solicitud)Menor (solo un ID de sesión en la cookie)
Entre dominiosFunciona bien (enviado como Bearer)Requiere configuración de cookies y CORS
MicroserviciosIdeal: cada servicio verifica de forma independienteCada servicio debe consultar el almacén de sesiones
SimplicidadMás piezas (firma, flujo de refresh)Más sencillo implementarlo bien

Usa JWT cuando necesites autenticación sin estado entre servicios distribuidos, apps móviles o consumidores de API de terceros.

Usa sesiones cuando necesites revocación sencilla (p. ej., «cerrar sesión en todos los dispositivos»), ejecutes una sola app monolítica o quieras la opción segura más simple para una aplicación web tradicional.

Pruébalo tú mismo: Decodifica e inspecciona cualquier JWT al instante con nuestro decodificador JWT: los datos no salen de tu navegador.

Herramientas relacionadas