Seguridad 12 min de lectura

JWT vs PASETO: Anatomía de un Token Seguro (Y por qué JWT es un peligro)

Publicado el 9 de mayo de 2026

JWT vs PASETO: Anatomía de un Token Seguro (Y por qué JWT es un peligro)

En el 99% de los tutoriales de backend, cuando llega el momento de implementar autenticación y autorización stateless, la receta es idéntica: instala una librería de JSON Web Tokens (jsonwebtoken), mete el ID del usuario en un payload, fírmalo con un secreto y envíalo al cliente. JWT se ha convertido en el estándar de facto, el martillo dorado indiscutible de la industria para manejar sesiones en APIs REST, microservicios y aplicaciones modernas.

Pero en el mundo de la ciberseguridad avanzada y la ingeniería de software de alto nivel, JWT es considerado un campo minado arquitectónico.

A lo largo de los años, vulnerabilidades masivas en implementaciones de JWT han permitido a atacantes saltarse la autenticación por completo, falsificar identidades de administradores y comprometer sistemas enteros. El problema no es que los desarrolladores sean descuidados; el problema es que el estándar JWT fue diseñado con una flexibilidad excesiva que delega decisiones criptográficas críticas a la capa equivocada.

Para solucionar este desastre nació PASETO (Platform-Agnostic Security Tokens). En este artículo vamos a destripar mecánicamente ambos formatos, analizando cómo trabajan a nivel interno, sus primitivas criptográficas, velocidad, consumo de recursos y por qué PASETO representa un diseño de ingeniería infinitamente más robusto.

1. JSON Web Tokens (JWT): El arma de doble filo

¿Qué es y cómo funciona mecánicamente?

Un JWT (definido en el RFC 7519) es una cadena de texto codificada que transporta información (“claims”) entre dos partes. Su estructura visible se compone de tres partes separadas por un punto (.):

Header.Payload.Signature

Cada una de estas partes está codificada en Base64URL (una variante de Base64 segura para URLs que omite caracteres como +, / y =).

  1. Header (Cabecera): Un objeto JSON que describe el tipo de token y el algoritmo de firma utilizado.
  2. Payload (Carga útil): Un objeto JSON que contiene los datos reales (ej. sub para el ID del usuario, exp para el tiempo de expiración, role, etc.).
  3. Signature (Firma): Un hash criptográfico generado al concatenar el Header y el Payload codificados, firmados utilizando el secreto del servidor y el algoritmo especificado en el Header.
// 1. Header decodificado
{
  "alg": "HS256",
  "typ": "JWT"
}

// 2. Payload decodificado
{
  "sub": "usr_984321",
  "role": "admin",
  "exp": 1780000000
}

El gran pecado de diseño: La Agilidad Criptográfica

Si miras el flujo de validación de un JWT, notarás un fallo conceptual masivo: el servidor lee el Header del token no verificado para saber qué algoritmo usar para verificar la firma. Esto se conoce como Cryptographic Agility (Agilidad Criptográfica).

En teoría, esto permite que un mismo sistema soporte múltiples algoritmos. En la práctica, ha sido la causa de las vulnerabilidades más infames de la historia del desarrollo web:

  • El ataque alg: "none": Muchas librerías antiguas permitían que un atacante tomara un token válido, modificara su rol a "admin", cambiara el header a {"alg": "none"} y eliminara la firma. El servidor leía "none", asumía que el token no requería validación criptográfica y concedía acceso total.
  • Ataques de Confusión de Clave (Key Confusion / Algorithm Confusion): Si un servidor utiliza asimetría RSA (RS256) para firmar tokens con una clave privada y verificar con una clave pública, un atacante puede capturar la clave pública (que suele estar expuesta para que otros servicios validen el token), firmar un payload malicioso usando HMAC (HS256) utilizando la clave pública RSA como secreto simétrico, y enviar el token. Si el backend está mal configurado, leerá HS256 en el header y usará su clave pública local para hacer un HMAC, validando exitosamente el token falsificado.

El problema de la visibilidad

Un error clásico de desarrolladores junior es asumir que un JWT es opaco o seguro frente a lectura. El payload de un JWT estándar es texto plano codificado en Base64URL. Cualquiera que intercepte el token (en un log, en el almacenamiento del navegador o en tránsito) puede decodificarlo instantáneamente y leer correos electrónicos, identificadores internos o metadatos sensibles.

Para cifrar un JWT necesitas usar JWE (JSON Web Encryption), un estándar secundario de una complejidad monumental que transforma el token en una bestia de cinco partes con una cabecera gigantesca, destruyendo su ligereza.

2. PASETO: El “Pozo del Éxito” de la seguridad

¿Qué es y cuál es su filosofía?

PASETO fue diseñado por Scott Arciszewski (de Paragon Initiative Enterprises) bajo un principio de usabilidad fundamental en ingeniería de software de alta criticidad: el Pit of Success (el pozo del éxito). Una API debe estar diseñada de tal forma que lo más fácil sea usarla correctamente, y lo más difícil (o imposible) sea introducir una vulnerabilidad.

A diferencia de los RFCs de JOSE (JSON Object Signing and Encryption) que ocupan cientos de páginas de opciones combinables, PASETO elimina por completo la agilidad criptográfica. El desarrollador no elige el algoritmo.

Estructura y Funcionamiento Interno

Un token PASETO tiene una estructura estandarizada y delimitada por puntos, pero sus partes tienen propósitos drásticamente distintos a los de JWT:

version.purpose.payload.footer
  1. Version: Define la especificación exacta y el conjunto inmutable de primitivas criptográficas que se utilizarán. Actualmente, el estándar de la industria es la Versión 4 (v4). (Las versiones 1 y 2 están deprecadas, y la 3 se reserva para casos específicos).
  2. Purpose (Propósito): Define si el token es de clave simétrica o asimétrica. Solo existen dos valores posibles:
    • local: El payload está completamente cifrado y autenticado mediante una clave simétrica compartida.
    • public: El payload está en texto plano, pero firmado digitalmente con una clave asimétrica pública/privada.
  3. Payload: Los datos reales del token (cifrados o codificados según el propósito).
  4. Footer (Opcional): Metadatos no cifrados pero autenticados criptográficamente (útil para pasar identificadores de clave o Key IDs sin descifrar el token principal).
// Ejemplo de un token PASETO v4 local (Cifrado simétrico)
v4.local.cu12X9b3...an813Jh

Criptografía Inmutable por Versión

Si recibes un token que empieza por v4.local, el motor interno de PASETO sabe con absoluta certeza matemática qué algoritmos usar, sin leer ningún JSON ni aceptar sugerencias del cliente:

  • Cifrado y Autenticación (v4.local): Utiliza XChaCha20-Poly1305, un algoritmo de cifrado autenticado con datos asociados (AEAD) de última generación. Genera un nonce aleatorio por cada token para garantizar que dos payloads idénticos produzcan cadenas completamente diferentes.
  • Firma Digital (v4.public): Utiliza Ed25519 (Curvas Elípticas de Edwards), una primitiva criptográfica ultrarrápida, de longitud de clave pequeña e inmune a ataques de canal lateral (Side-Channel Attacks).

Al no existir un campo alg en el token, los ataques de confusión de algoritmos o degradación a texto plano son estructuralmente imposibles.

3. Comparativa Técnica a Fondo

Para decidir qué estándar utilizar en la arquitectura de tu próximo backend, analicemos cómo compiten en los vectores críticos de producción:

A. Robustez y Seguridad Criptográfica

  • JWT: Es un estándar frágil. Requiere que la librería subyacente esté perfectamente programada para rechazar cabeceras maliciosas y que el desarrollador valide explícitamente en su código que el algoritmo recibido coincide con el esperado (ej. jwt.verify(token, secret, { algorithms: ['RS256'] })). Si olvidas esa configuración explícita, tu sistema es potencialmente vulnerable.
  • PASETO: Es robusto por diseño. Implementa cifrado autenticado (AEAD) de forma nativa en su modo local. Esto significa que un token PASETO local garantiza Confidencialidad (nadie puede leer el contenido) e Integridad (nadie puede alterar un solo bit sin invalidar el tag de autenticación). En JWT, obtener confidencialidad requiere implementar JWE, lo cual rara vez se hace en producción debido a su enorme fricción.

B. Velocidad de Procesamiento (Performance)

El rendimiento de un token bajo alta concurrencia (miles de requests por segundo validando sesiones en un API Gateway) depende de las primitivas matemáticas subyacentes:

  • Firma Asimétrica: JWT suele utilizar RSA con claves de 2048 o 4096 bits (RS256). La verificación de firmas RSA es razonablemente rápida, pero la generación de firmas es extremadamente pesada para la CPU. PASETO v4.public utiliza Ed25519, cuyas operaciones sobre curvas elípticas son drásticamente más rápidas y eficientes en consumo de ciclos de reloj que RSA, permitiendo un throughput significativamente mayor en microservicios de autenticación.
  • Cifrado Simétrico: JWT utiliza típicamente HMAC-SHA256 para firmas simétricas. PASETO v4.local cifra y firma simultáneamente usando XChaCha20-Poly1305. En procesadores modernos (especialmente en entornos móviles, ARM o servidores sin instrucciones AES nativas dedicadas), ChaCha20 es masivamente más rápido y predecible que los algoritmos de bloque tradicionales.
  • Costos de Parsing: Para validar un JWT, el servidor primero debe hacer un split de la cadena, decodificar en Base64URL el Header, parsear ese JSON en memoria, leer el campo alg y luego ejecutar la criptografía. PASETO lee directamente el prefijo de texto plano (v4.local.), valida la versión en microsegundos y pasa el bloque binario directo al motor criptográfico, evitando el overhead y los vectores de ataque de denegación de servicio por parsing JSON en fases tempranas.

C. Consumo de Recursos y Tamaño (Payload Overhead)

  • Tamaño Base: Ambos formatos utilizan codificación Base64URL para ser transportados de forma segura en cabeceras HTTP (Authorization: Bearer <token>). Por lo tanto, el tamaño crece linealmente según la cantidad de datos que inyectes.
  • Overhead Estructural: Un JWT básico tiene un header pequeño (aprox. 36 bytes codificados). Un PASETO v4.local añade un overhead fijo predecible para almacenar el nonce criptográfico (32 bytes para XChaCha20) y el tag de autenticación Poly1305 (16 bytes).
  • Eficiencia Real: Aunque un PASETO local puede ser ligeramente más largo en bytes puros que un JWT sin cifrar debido al almacenamiento del nonce de alta seguridad, la diferencia es insignificante en el ancho de banda moderno (decenas de bytes). A cambio, obtienes un payload completamente encriptado. Si comparamos PASETO con un JWT cifrado (JWE), PASETO es inmensamente más compacto y eficiente en memoria.

4. Ventajas y Desventajas en el Mundo Real

JSON Web Tokens (JWT)

✅ Ventajas:

  • Adopción Universal: Absolutamente todos los lenguajes de programación, frameworks y plataformas de la industria tienen librerías maduras para JWT.
  • Interoperabilidad Estándar: Es la base de protocolos globales de federación de identidad como OAuth2 y OpenID Connect (OIDC). Si integras el login de Google, Apple o Auth0, consumirás JWTs obligatoriamente.
  • Facilidad de Depuración: Herramientas visuales como jwt.io permiten inspeccionar el contenido de un token al instante durante el desarrollo local.

❌ Desventajas:

  • Inseguro por defecto: Diseñado con un exceso de opciones que facilita implementaciones vulnerables si no se audita el código rigurosamente.
  • Falta de Privacidad Nativa: Exponer claims en texto plano fomenta fugas de información interna o PII (Personally Identifiable Information) hacia el frontend.

PASETO

✅ Ventajas:

  • Seguridad Infranqueable por Diseño: Elimina de raíz clases enteras de vulnerabilidades criptográficas. No hay negociación de algoritmos.
  • Cifrado Opaco por Defecto: En su modo local, el cliente no tiene forma de saber qué hay dentro del token, protegiendo la lógica interna y los datos del backend.
  • Primitivas de Vanguardia: Utiliza exclusivamente criptografía moderna, optimizada y resistente (Ed25519, XChaCha20-Poly1305).

❌ Desventajas:

  • Ecosistema más Joven: Aunque tiene un soporte excelente en Go, Rust, Node.js, PHP y Python, algunas plataformas corporativas heredadas (Legacy) carecen de librerías nativas oficiales de PASETO.
  • No Reemplaza a OIDC: Si estás construyendo un Proveedor de Identidad público que debe cumplir con el estándar OpenID Connect para que terceros se conecten a tu sistema, el estándar te exigirá emitir JWTs.

Resumen y Comparativa Práctica (Cheat Sheet)

Aquí tienes la referencia rápida para contrastar ambos formatos y tomar decisiones de arquitectura fundamentadas:

CaracterísticaJWT (JSON Web Tokens)PASETO (v4)
Enfoque de DiseñoMáxima flexibilidad (Agilidad Criptográfica)Máxima seguridad (“Pit of Success”)
Cabecera (Header)JSON dinámico (Define el algoritmo a usar)Prefijo estático simple (v4.local. o v4.public.)
Criptografía SimétricaHMAC-SHA256 (Típicamente)XChaCha20-Poly1305 (Cifrado Autenticado AEAD)
Criptografía AsimétricaRSA / ECDSA (Típicamente)Ed25519 (Curvas Elípticas de alta velocidad)
Visibilidad del PayloadTexto plano (Base64URL) a menos que uses JWECifrado y Opaco en modo local / Legible en public
Prevención alg: noneDepende de la validación estricta de la libreríaInmune por diseño (No existe el concepto de alg)
Velocidad en ServidorMedia (Penalizada por firmas RSA y parsing JSON)Alta (Primitivas modernas ultra optimizadas)
Caso de Uso IdealInteroperabilidad pública (OAuth2, OIDC, SSO de terceros)Comunicaciones internas, APIs propias, Microservicios

Mi perspectiva personal

Como ingeniero de software, la elección entre JWT y PASETO se reduce a una sola pregunta sobre el contexto y los límites de tu sistema: ¿Quién va a consumir y validar este token?

Si estás diseñando un sistema cerrado —por ejemplo, el backend de tu propia plataforma SaaS, la comunicación interna entre tus microservicios o los tokens de sesión que tu propia Single Page Application (SPA) o app móvil envía a tu API— usar JWT hoy en día es asumir riesgos innecesarios. Implementar PASETO en este escenario es una decisión de ingeniería infinitamente superior. Te otorga cifrado nativo sin esfuerzo, un rendimiento superior en CPU y te blinda contra ataques de confusión de claves desde el día cero.

Por otro lado, si estás construyendo una API pública que debe integrarse con el ecosistema global de OAuth2 o interoperar con proveedores de identidad externos (como Auth0, Keycloak o Azure AD), el estándar te obligará a usar JWT. Si te encuentras en esta situación, la regla de oro inquebrantable es: trata el header del JWT como datos no confiables introducidos por el usuario. Jamás permitas que tu código decida qué clave o método usar basándose en el campo alg del token; fuerza siempre una lista blanca estricta de algoritmos permitidos en tu lógica de validación.

La seguridad real no consiste en elegir la herramienta más popular de GitHub, sino en utilizar abstracciones que reduzcan la superficie de error humano al mínimo absoluto. Y en ese terreno, PASETO ha ganado la batalla.