🔐Ciberseguridad

JWT explicado visualmente: decodifica un token y comprende sus claims

Felipe••9 min de lectura
Explora header, payload y signature con un playground JWT local. Lee sus claims y fechas, detecta formatos incorrectos y distingue decodificar de verificar.

Un token puede declarar "role": "admin", incluir una fecha de expiración futura y mostrarse como un JSON perfectamente ordenado. Nada de eso demuestra quién lo emitió. La diferencia entre leer un dato y confiar en él es el punto de partida de este artículo.

Vamos a desmontar un JWT en el navegador y observar qué cambia al modificar sus claims. El playground utiliza datos ficticios, no solicita claves y no verifica firmas. Para implementar un verificador de servidor con jose, JWKS y políticas explícitas, continúa después con JWT: decodificar no es validar. Aquí nos centraremos en entender la representación.

JWT, JWS y JWE: qué estamos mirando

JWT, JSON Web Token, representa afirmaciones o claims que se intercambian entre partes. Puede transportar información de identidad o autorización dentro de un protocolo, pero el formato por sí solo no implementa un inicio de sesión. RFC 7519, sección 1.

JWS, JSON Web Signature, protege contenido mediante una firma digital o un código de autenticación de mensajes, MAC. En un JWT compacto JWS, header y payload son legibles: la protección no los cifra. Con HMAC, quien conoce el secreto compartido puede producir el MAC; en una firma asimétrica intervienen claves privada y pública. RFC 7515, secciones 1 y 3.

JWE, JSON Web Encryption, representa contenido cifrado. Su forma compacta tiene cinco segmentos: header protegido, clave cifrada, IV, ciphertext y tag de autenticación; dependiendo del algoritmo, alguno puede estar vacío. Decodificar sus segmentos no revela automáticamente los claims. Un JWT también puede combinar firma y cifrado mediante anidamiento. Este playground reconoce la cantidad de segmentos como una pista, pero no procesa ni descifra JWE. RFC 7516, sección 3.1.

Si esos términos aún se mezclan, conviene repasar hashing, cifrado y codificación. Hacer legible una representación no equivale a descifrarla.

Tres segmentos, dos puntos

La serialización compacta que exploraremos tiene esta forma:

BASE64URL(header) . BASE64URL(payload) . BASE64URL(signature)

Los espacios solo ayudan a leer el esquema; no pertenecen al token.

SegmentoQué contieneQué no demuestra al leerlo
HeaderParámetros como alg, typ o kidQue el algoritmo o la clave sean aceptables
PayloadObjeto JSON con claimsQue sus afirmaciones sean verdaderas
SignatureBytes de una firma o MACQue la protección se haya verificado

Para este formato, la entrada que se firma es el header codificado, un punto y el payload codificado. La firma no es un tercer JSON ni una copia cifrada del mensaje. Tampoco se debe volver a serializar el JSON para verificar: importan los segmentos recibidos. RFC 7515, sección 3.1.

Base64URL permite leer; no aporta secreto

Base64URL representa bytes con un alfabeto apropiado para URL. Utiliza - y _ donde Base64 convencional usa + y /; en JOSE se omite el relleno final =. No exige una contraseña para recuperar los bytes. RFC 4648, sección 5, RFC 7515, sección 2.

Nuestro lector comprueba caracteres y longitudes, recupera bytes y aplica UTF-8 al header y al payload. No interpreta la firma como texto. Un carácter como í puede ocupar varios bytes: tratar la salida de atob() directamente como texto Unicode produce errores. TextDecoder resuelve esa conversión y permite rechazar secuencias UTF-8 incorrectas. MDN: TextDecoder.

Playground: examina un token sin confiar en él

Usa exclusivamente tokens ficticios. Un token Bearer real puede dar acceso a quien lo posea; su exposición importa aunque no conozca una clave. No lo pegues en capturas, incidencias ni conversaciones. RFC 6750, introducción y sección 5.

Decodificar un JWT no verifica su firma ni garantiza su autenticidad.

La herramienta necesita JavaScript. El artículo sigue disponible sin él. No introduzcas tokens reales ni credenciales.

Los ejemplos declaran HS256, pero sus 32 bytes finales son relleno, no un HMAC calculado. Los dominios .example, sujetos y roles son ficticios. Las fechas se generan al elegir cada ejemplo para que puedas comparar un exp futuro con uno pasado sin depender del día de publicación.

La herramienta procesa la entrada en memoria del navegador, sin enviarla a un endpoint, guardarla en almacenamiento web ni incluirla en la URL. «Limpiar» retira entrada y resultados; también se limpian al abandonar la página. Copiar es una acción explícita: el JSON pasa al portapapeles y la herramienta no lo borra desde allí. Estas medidas no protegen frente a extensiones maliciosas o un dispositivo comprometido.

Text Forge ya tiene JWT Studio. Reutilizamos su lógica básica de lectura con adaptaciones para esta demostración. El playground tiene un alcance distinto: seguir la explicación, identificar las partes y comparar lo que podemos observar con lo que todavía no sabemos.

Tres recorridos para probarlo

  1. Elige «exp futuro». Localiza alg en el header y sub en el payload. Observa que la herramienta describe el tiempo, pero sigue mostrando «firma sin verificar».
  2. Elige «Claim alterado». El rol cambia de lector a admin y se mantiene la firma de relleno. La lectura sigue funcionando. Esto demuestra la facilidad de representar una afirmación, no que se haya roto una firma real ni que exista un bypass en una aplicación.
  3. Compara «exp pasado», «Sin exp» y «Malformado». Son situaciones distintas: una fecha declarada ya transcurrió, no hay claim de expiración o ni siquiera se puede interpretar el formato esperado. Ninguna salida positiva del lector autoriza una petición.

Puedes editar el texto directamente. Una entrada de cinco segmentos recibe un aviso sobre posible JWE; contar puntos no permite concluir que sea un JWE válido.

Qué significan los claims

Los nombres registrados proporcionan un vocabulario común; el perfil de cada aplicación decide cuáles exige. RFC 7519, sección 4.1.

ClaimLectura
issEmisor declarado
subSujeto al que se refieren las afirmaciones; interprétalo en el contexto del emisor
audDestinatario o destinatarios previstos; puede ser una cadena o un array de cadenas
expInstante de expiración; llegado ese momento no debe aceptarse, salvo la tolerancia de reloj prevista
iatInstante de emisión declarado
nbfInstante antes del cual no debe aceptarse

exp, iat y nbf usan NumericDate: segundos desde la época Unix, no milisegundos. Se permiten valores no enteros. iat no define por sí mismo una expiración. El RFC no obliga a usar todos estos claims en cualquier JWT. RFC 7519, secciones 2 y 4.1.

El playground expresa las fechas en UTC y las compara con el reloj de tu dispositivo en el instante indicado. No aplica tolerancia ni mantiene un contador en segundo plano. Pulsa «Decodificar / actualizar lectura» para recalcular. Si exp está ausente o no es numérico, no fabrica una fecha ni lo convierte automáticamente desde una cadena.

Leer una fecha es una operación mucho más pequeña que validar

Este fragmento TypeScript demuestra únicamente la conversión de un valor de prueba. No recibe ni verifica un token:

function fechaDeclaradaUtc(segundos: unknown): string {
  if (typeof segundos !== 'number' || !Number.isFinite(segundos)) {
    throw new Error('Se esperaba un número de segundos');
  }

  const milisegundos = segundos * 1000;

  if (Math.abs(milisegundos) > 8.64e15) {
    throw new Error('Fecha fuera del rango representable por Date');
  }

  return new Date(milisegundos).toISOString();
}

console.log(fechaDeclaradaUtc(0));
// 1970-01-01T00:00:00.000Z

El cero pertenece a una prueba ficticia y reproducible. Que la conversión funcione solo demuestra que podemos representar ese valor como fecha. Tampoco garantiza que el reloj local sea correcto. MDN: Date.

Qué debe comprobar el sistema que acepta un JWT

La política viene de configuración confiable, no de lo que el token solicite. RFC 8725 establece las prácticas de seguridad aplicables, entre ellas restringir algoritmos, vincular claves al emisor y validar destinatarios. RFC 8725, secciones 3.1, 3.8 y 3.9.

Para una API que espera tokens firmados:

  • Firma y algoritmo: usa una biblioteca mantenida, una lista permitida y claves de confianza. Leer alg no concede permiso para usarlo. alg=none indica ausencia de firma o MAC; no sirve como prueba criptográfica de identidad.
  • Issuer y audience: comprueba el emisor esperado y que el token esté destinado a esta API. No obtengas claves desde una URL arbitraria indicada por claims sin verificar.
  • Contrato de claims: exige los campos que necesites, comprueba sus tipos y aplica expiración, nbf y la política temporal acordada. Una firma correcta no sustituye estas restricciones.
  • Contexto de uso: distingue tipos de token y evita aceptar uno emitido para otra finalidad. Los permisos sobre un recurso requieren además una decisión de autorización.

Son controles distintos de este lector. El laboratorio de verificación con jose los implementa de manera explícita. Para entender por qué verificar identidad tampoco resuelve todos los permisos, sigue con RBAC, ABAC y autorización por objeto.

Los límites deliberados de la herramienta

Este componente es un inspector educativo, no una biblioteca de seguridad:

  • Limita la entrada a 16 384 caracteres antes de procesarla. Rechaza segmentos malformados, JSON que no sea un objeto, UTF-8 incorrecto y números no finitos.
  • No soporta payloads separados ni la extensión b64=false. Esta última no debe utilizarse en JWT, según RFC 7797, sección 7.
  • No decide si un algoritmo, una longitud de firma, un kid o una extensión crit son aceptables. No solicita claves públicas, privadas ni secretos.
  • Usa JSON.parse: si se repite una propiedad, se conserva la última; no es un detector exhaustivo de ambigüedades. El JSON formateado sirve para leer, no para reconstruir los bytes firmados.
  • Inserta resultados con textContent, no como HTML. Un claim que contenga etiquetas se muestra como texto, sin ejecutarlo. MDN: textContent.

La separación de responsabilidades también evita cargar un estudio criptográfico completo dentro del artículo: el contenido es estático y la lógica interactiva se carga al acercarse al playground.

De una afirmación legible a una decisión confiable

Prueba a cambiar un claim y observa qué partes siguen siendo legibles. Después identifica lo que falta para aceptar ese token: una clave confiable, una operación criptográfica correcta y que se cumplan las reglas de uso de tu aplicación.

La pregunta útil no es solo «¿puedo leer este JSON?», sino «¿qué evidencia permite a mi aplicación confiar en estas afirmaciones para esta operación?». El playground responde a la primera; tu sistema debe resolver la segunda antes de actuar.

Referencias

#JWT#JWS#JWE#Base64URL#claims#NumericDate#validación de tokens

Artículos relacionados