Español

Lo que demuestra un JWT decodificado

Un decodificador JWT le muestra lo que afirma un token. No puede mostrarle si esas afirmaciones son ciertas. Esta guía cubre cuáles son los tres segmentos, qué establece y qué no establece la decodificación, y los ataques que viven en la brecha entre los dos.

Tres segmentos, dos de los cuales son solo JSON

Un JWT en su forma común es un JWS: tres segmentos de base64url separados por puntos. El primero es un encabezado, el segundo una carga útil y el tercero una firma.

El encabezado y la carga útil son objetos JSON normales codificados en base64url. Codificado, no cifrado. Cualquiera que tenga el token puede leer ambos, instantáneamente, sin clave; eso no es un defecto, es el diseño. Un JWT es una declaración firmada, no un sobre cerrado. La firma garantiza que la declaración no ha sido alterada; no hace nada para mantenerlo privado.

Vale la pena exponer claramente la consecuencia porque habitualmente se pasa por alto: nunca coloque nada confidencial en una carga útil de JWT. Ni una contraseña, ni un identificador nacional completo, ni detalles internos del sistema. Supongamos que la carga útil es pública, porque para cualquiera que tenga el token, lo es.

El tercer segmento es la firma, calculada sobre los dos primeros. Es la única parte que tiene algún valor de seguridad y es la parte que un decodificador no puede evaluar.

Lo que prueba la decodificación: nada

Este es el objetivo de toda la guía. La decodificación de un JWT analiza dos cadenas base64url en JSON. Confirma que el token está bien formado. No confirma que el token sea genuino, que haya sido emitido por la parte mencionada en el reclamo "iss", que los reclamos no hayan sido editados o que alguna vez haya sido válido.

Cualquiera puede construir un token. Tome cualquier JWT, cambie "role": "user" a "role": "admin", vuelva a codificar la carga útil, grape cualquier firma al final y un decodificador mostrará sus reclamos editados exactamente con la misma confianza que mostraba el original. No tiene forma de saber la diferencia, porque verificar la diferencia es una operación diferente que requiere una clave que el decodificador no tiene.

Entonces, cuando un decodificador (este o cualquier otro) le muestra "exp: 2026-01-01", lo que realmente le está diciendo es: este token contiene un reclamo de que vence en esa fecha. Que esa afirmación signifique algo depende enteramente de si la firma es válida, la cual no ha sido verificada.

Esta herramienta solo decodifica y lo dice en la página, junto a los resultados, cada vez. No en una nota a pie de página. La razón es que un decodificador que guarda silencio sobre esto está entrenando a sus usuarios para leer datos no verificados como si estuvieran verificados, y ese hábito es la raíz de toda una familia de errores de autenticación.

Por qué esta herramienta no ofrece verificación

La verificación necesita tres cosas que una página web no puede tener de manera responsable: la clave del emisor, el algoritmo fijado de antemano y una política sobre qué rechazar.

La clave es el problema obvio. Para los algoritmos HMAC (HS256 y amigos), la clave es un secreto compartido, el mismo secreto que se utiliza para crear tokens. Pegarlo en una página web significa pegar una credencial que puede generar tokens válidos en una página web. Para RSA y ECDSA, la clave pública no es secreta, pero aun así necesitarás obtener la correcta del punto final JWKS correcto y confiar en que la tienes.

El algoritmo es el problema sutil y la fuente de dos ataques bien conocidos. El primero es alg: "none": el encabezado afirma que el token no está firmado y un verificador que respeta el encabezado en lugar de su propia configuración acepta cualquier cosa. El segundo es la confusión de RS256 a HS256: el atacante toma una clave pública (que es, por definición, pública), cambia el encabezado para que diga HS256 y firma el token usando esa clave pública como secreto HMAC. Un verificador que lea el algoritmo del token y busque "la clave" lo validará.

Ambos ataques provienen del mismo error: dejar que el token le diga al verificador cómo verificarlo. Un verificador correcto ignora el algoritmo del encabezado y usa aquel con el que fue configurado. Esa es una decisión que pertenece al sistema que confía en el token, no a una herramienta de conveniencia ni a quien haya pegado algo en un formulario.

¿Por qué no pegar tokens de producción en cualquier lugar?

Un token de acceso es una credencial de portador. Eso es lo que significa "bearer" en el encabezado Autorización: quien lo porta, eres tú. No existe un segundo factor y, por lo general, no hay forma de distinguir un token robado de uno legítimo. Hasta que caduque, es una clave funcional para su cuenta.

Entonces, pegar un token activo en cualquier página web es entregar una credencial a esa página. Éste decodifica todo localmente y no realiza ninguna solicitud de red después de que se carga la página; puede confirmarlo en el panel de red de su navegador, y debería hacerlo, porque demora diez segundos. Pero observe cuál es realmente ese argumento: una afirmación, en un sitio web, de que el sitio web es confiable. Cada sitio que exfiltra tokens hace exactamente la misma afirmación, y un visitante no puede notar la diferencia de un vistazo.

El hábito seguro no depende de juzgar los sitios correctamente. Utilice tokens caducados, tokens de entorno de prueba o tokens que haya acuñado para ese propósito. Si ya ha pegado un token de producción en algún lugar (en cualquier lugar), gírelo. La revocación es barata; un incidente no lo es.

Lo mismo se aplica con más fuerza a la firma de claves. No existe una razón legítima para escribir un secreto HMAC o una clave privada en una página web, y cualquier sitio que solicite uno para "verify" su token solicita la capacidad de falsificar tokens. Esa es la razón concreta por la que esta herramienta no tiene función de verificación: la función requiere la solicitud.

Leer las afirmaciones que importan

RFC 7519 registra un pequeño conjunto de nombres de reclamos. "iss" es el emisor, "sub" el tema del que trata el token, "aud" la audiencia prevista, "exp" el vencimiento, "nbf" el momento válido más temprano, "iat" el momento de emisión y "jti" una identificación única para la detección de repetición. Todo lo demás es específico de la aplicación.

Las declaraciones de tiempo son valores de NumericDate: segundos desde la época de Unix, no milisegundos. Esto hace tropezar a la gente constantemente, porque la mayoría de los valores de tiempo de JavaScript son milisegundos. A un token que parece caducar en 1970 normalmente se le ha asignado un valor de milisegundos; uno que parece caducar en el año 55000 generalmente ha tenido un segundo valor multiplicado por 1000 en alguna parte.

"aud" merece especial atención al depurar. Un token que es perfectamente válido aún puede ser el token incorrecto porque fue emitido para una audiencia diferente. Un verificador que verifica la firma pero no la audiencia aceptará un token creado para otro servicio completamente, lo cual es una ruta real de escalada de privilegios en sistemas que comparten un proveedor de identidad.

Esta herramienta representa los reclamos de tiempo en UTC, marca un token vencido como vencido y lo combina con un recordatorio de que el reclamo de vencimiento solo significa algo si la firma es válida. El recordatorio está ahí porque "dice que no ha caducado" y es el momento exacto en que el hábito de los datos no verificados hace su daño.

Una breve lista de verificación para el sistema que confía

Si está escribiendo el código que acepta tokens en lugar de simplemente inspeccionar uno, la siguiente es la versión corta de lo que hace un verificador correcto.

  1. Primero verifique la firma, con una clave que obtuvo fuera de banda, antes de leer cualquier reclamo.
  2. Fije el algoritmo en su propia configuración. Nunca lo leas desde el encabezado del token. Rechazar "none" incondicionalmente.
  3. Verifique "exp" y "nbf" con un reloj confiable, con como máximo una pequeña tolerancia a la desviación.
  4. Verifique "iss" y "aud" con los valores que espera. Una firma válida en un token destinado a otra persona sigue siendo un token incorrecto.
  5. Utilice una biblioteca examinada para su plataforma en lugar de ensamblarla usted mismo. Todos los elementos de esta lista están ahí porque las implementaciones se equivocaron.
  6. Mantenga corta la vida útil de los tokens y tenga una ruta de revocación. Los tokens de corta duración limitan el daño de la fuga que aún no has notado.

¿Qué pasa con lo que pegas?

  • Cada conversión, hash, decodificación y diferenciación se ejecuta en la pestaña de su navegador. Ninguna entrada se carga, registra o almacena en un servidor, porque no hay ningún servidor involucrado una vez que la página se ha cargado.
  • Los hashes provienen de la implementación Web Crypto del propio navegador y los UUID de su generador aleatorio criptográficamente seguro. Ninguno de los dos implica una llamada de red.
  • Nada de lo que escribe se escribe en el almacenamiento local o en una cookie. Al recargar la página se descarta; cerrar la pestaña la descarta.
  • La analítica de todo el sitio se ejecutan únicamente en el host de producción canónico configurado y se describen en la Política de Privacidad; Los hosts locales y de vista previa lo rechazan. Los valores, tokens, URL y contenidos de archivos pegados están excluidos de los eventos analíticos propios de ToolAcre. La publicidad está deshabilitada en la configuración actual.
  • Dicho esto: una clave JWT o API es una credencial activa. El hábito seguro es nunca pegar uno en una página web que usted no escribió, por muy confiables que sean sus afirmaciones, incluida ésta.

Preguntas

¿Esta herramienta verifica la firma?

No, y nunca lo será. Decodifica el encabezado y la carga útil y te muestra lo que contienen. No verifica la firma, por lo que nada de lo que muestra demuestra que el token es auténtico, inalterado o emitido por quienquiera que nombre.

Entonces, ¿cómo sé que una ficha es auténtica?

Verificando la firma con la clave del emisor, utilizando una biblioteca examinada, con el algoritmo fijado en su propia configuración en lugar de leerlo del token. Eso es trabajo para el servicio que confía en el token, en un entorno que legítimamente posee la clave.

¿Mi token se envía a algún lugar cuando lo decodifico aquí?

No. La decodificación se realiza en la pestaña de su navegador utilizando el propio JavaScript de la página, y la página no realiza solicitudes de red después de cargarse. Puedes verificar esto en el panel de red de tu navegador. Aún así, no debes pegar tokens de producción en herramientas web como una cuestión de hábito, porque ese hábito tiene que funcionar en sitios que no son honestos al respecto.

¿Por qué alguien puede leer mi carga útil JWT?

Porque la carga útil está codificada en base64url, no cifrada. Un JWS es una declaración firmada, no sellada. Si necesita que el contenido sea ilegible, necesita JWE, el formato de token cifrado, y luego un decodificador no puede mostrarle nada sin la clave.

¿Qué es alg: "none"?

Un valor de encabezado que declara que el token no está firmado. Existe en la especificación para contextos donde la integridad está garantizada por otros medios, y es una trampa permanente: un verificador que confía en el algoritmo del encabezado aceptará cualquier token que afirme "none". Esta herramienta lo marca cada vez que aparece.

Mi token tiene cinco segmentos y no se decodifica. ¿Por qué?

Cinco segmentos significan un JWE (un token cifrado) en lugar de un JWS firmado. Su contenido no se puede leer sin la clave de descifrado, por lo que realmente no hay nada que pueda mostrar un decodificador. Esta herramienta identifica ese caso explícitamente en lugar de informar un error de análisis vago.

El vencimiento parece incorrecto por un factor de 1000.

Los reclamos de tiempo de JWT son NumericDate: segundos desde la época, no milisegundos. Un valor producido por Date.now() es mil veces demasiado grande. La utilidad de marca de tiempo de este kit de herramientas convierte entre los dos y siempre le indica qué unidad utilizó.

¿Es seguro almacenar un JWT en localStorage?

Es una compensación, no un sí o un no. localStorage es legible por cualquier JavaScript que se ejecute en su origen, por lo que una única vulnerabilidad XSS filtra el token. Una cookie httpOnly no es legible por JavaScript pero necesita protección CSRF. El resumen honesto es que ninguno de los dos es gratuito y la decisión corresponde al modelo de amenaza de su aplicación.

Limitaciones

  • Esta herramienta solo decodifica. No verifica las firmas, y esa es una decisión de diseño permanente en lugar de una característica faltante; consulte la guía anterior para saber por qué.
  • Los tokens cifrados (JWE, cinco segmentos) no se pueden decodificar en absoluto sin la clave. La herramienta los identifica y se detiene.
  • Los JWT anidados (un token cuya carga útil es en sí mismo un token) no se desenvuelven automáticamente. Decodifica el token interno como un paso separado.
  • Los significados de las reclamaciones más allá del conjunto registrado definido en RFC 7519 son específicos de la aplicación, por lo que la herramienta muestra sus valores sin interpretarlos.
  • El vencimiento que se muestra aquí refleja solo lo que el token afirma sobre sí mismo. Que esa afirmación sea significativa depende de una firma que esta herramienta no verifica.
  • Se rechazan los tokens de más de 200,000 caracteres. Cualquier JWT real es órdenes de magnitud más pequeño.