Blog Series #2 — ¿Está protegido tu JWT?
El JWT depende de la firma para garantizar que nadie alteró su contenido. Si el secreto usado en esa firma es débil o queda expuesto, un atacante puede crear tokens válidos y manipular la aplicación.


El JWT, o JSON Web Token, es uno de los formatos más utilizados para la autenticación en aplicaciones web y APIs. Después de que el usuario inicia sesión, el servidor genera un token y se lo entrega al cliente, que pasa a enviarlo en cada petición, normalmente en la cabecera Authorization. A partir de ese token, el servidor identifica quién está haciendo la petición y qué permisos tiene esa persona.
Toda la seguridad de este modelo depende de un único punto: la firma. Es ella la que garantiza que el contenido del token fue generado por el servidor y no fue alterado en el camino. Cuando el secreto usado para firmar el token es débil o termina expuesto, un atacante puede generar sus propios tokens y la aplicación pasa a aceptarlos como si fueran legítimos.
Qué es un JWT
Un JWT está formado por tres partes separadas por punto: la cabecera, el payload y la firma.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJuYW1lIjoiTWFyaWEiLCJyb2xlIjoidXNlciJ9.<firma>
La cabecera informa qué algoritmo se utilizó para firmar el token:
{
"alg": "HS256",
"typ": "JWT"
}
El payload contiene la información que la aplicación utiliza para identificar al usuario, como el identificador, el nombre y el perfil de acceso:
{
"sub": "123",
"name": "Maria",
"role": "user"
}
Un punto importante es que estas dos primeras partes no están cifradas. Solo están codificadas en Base64, lo que significa que cualquier persona con acceso al token puede leer su contenido. El JWT no fue creado para ocultar información, sino para garantizar que no sea alterada.
Cómo funciona la firma
La tercera parte del token es la firma. En el algoritmo HS256, uno de los más comunes, el servidor toma la cabecera y el payload y calcula un HMAC usando un secreto que solo él debería conocer.
Cuando el token vuelve en una petición, el servidor repite ese cálculo. Si el resultado coincide con la firma presente en el token, el servidor considera que el contenido es confiable. Si alguien altera cualquier información del payload, por ejemplo cambiando "role": "user" por "role": "admin", la firma deja de coincidir y el token es rechazado.
Esto significa que la protección del JWT no está en el token en sí, sino en el secreto utilizado para firmarlo. Mientras ese secreto siga siendo desconocido, nadie puede generar una firma válida para un payload modificado.
El riesgo de generar firmas en sitios web
Durante el desarrollo, es habitual utilizar sitios web que decodifican, validan y generan JWTs. Basta con pegar el token, indicar el secreto y el sitio muestra el contenido y calcula la firma.
El problema aparece cuando esto se hace con tokens y secretos reales. Al pegar el secreto de producción en un sitio de terceros, deja de estar solo en el servidor de la aplicación. No hay forma de garantizar qué ocurre con esa información después de enviada: puede ser registrada, almacenada o expuesta de una manera que escapa por completo al control de quien la utilizó.
Lo mismo vale para el token. Un token de producción representa la sesión de un usuario real y, mientras sea válido, puede ser utilizado por cualquier persona que tenga acceso a él.
Generar o validar firmas debe ser responsabilidad de la propia aplicación, con el secreto almacenado de forma segura en el servidor. Para pruebas, lo ideal es utilizar tokens y secretos creados específicamente para ese fin.
Cuando el secreto puede romperse
Aunque el secreto nunca se pegue en ningún sitio, sigue existiendo otro riesgo. Un token firmado con HS256 contiene todo lo necesario para comprobar si un determinado secreto es correcto: la cabecera, el payload y la firma resultante.
Esto permite que un atacante que haya obtenido un token intente descubrir el secreto de forma offline, sin necesidad de hacer ninguna petición a la aplicación. Herramientas como Hashcat, ampliamente utilizadas para romper contraseñas, pueden probar una enorme cantidad de posibilidades en poco tiempo, usando listas de palabras comunes y combinaciones.
Si el secreto es corto, predecible o se basa en valores conocidos, como el nombre de la aplicación, secret, 123456 o un valor de ejemplo copiado de la documentación, puede descubrirse rápidamente.
El impacto
Con el secreto en la mano, el atacante pasa a tener la misma capacidad que el servidor: puede generar tokens válidos con cualquier contenido.
En la práctica, esto permite alterar el payload para hacerse pasar por otro usuario, cambiando el valor de sub, o elevar sus propios permisos, cambiando el campo role a admin. Como la firma se calcula con el secreto correcto, la aplicación no tiene forma de diferenciar ese token de un token legítimo.
A partir de ese punto, todo lo que la aplicación controla con base en el JWT queda comprometido: acceso a cuentas de otros usuarios, paneles administrativos, endpoints internos de la API y cualquier dato que dependa de esa autenticación.
Cómo protegerse
La principal medida es utilizar un secreto largo y generado de forma aleatoria, que no pueda adivinarse ni encontrarse en listas de palabras. Ese secreto debe quedar almacenado solo en el servidor, fuera del código fuente y del repositorio.
También es importante no utilizar nunca secretos ni tokens de producción en sitios de terceros para decodificar o generar firmas.
Además, el servidor debe definir explícitamente qué algoritmo acepta, en lugar de confiar en el valor indicado en la cabecera del token, y los tokens deben tener un tiempo de expiración corto. En escenarios en los que varios servicios necesitan validar tokens, los algoritmos asimétricos, como RS256, permiten que solo el emisor tenga la clave privada usada para firmar, mientras que los demás servicios utilizan solo la clave pública para validar.
Integra DevSecOps en tu flujo de trabajo con runtz ahora
Empieza a analizar riesgos de seguridad. Comienza en runtz.dev.



