Buenas a todos!
A raiz de un curso que hice hace un par de meses, tomé diferentes notas e investigué acerca de qué era y cómo funcionaba JWT.
Espero que os sirva tanto como me sirvió a mi para entender más que es JWT.
JWT FOR BEGINNERS
- Introducción a autenticación y JWTs
- Estructura de JWTs
- Firma de un token JWT
- Securización de Token JWT
- Ciclo de vida de un Token JWT
- Understanding HS256
- Understanding RS256
- Recomendaciones para evitar ataques a JWT
- Diferentes ataques
- Referencias
1. Introducción a JWTs
JSON Web Token (JWT) es un estándar abierto (RFC 7519) que define una forma compacta y autónoma de transmitir información de forma segura entre las partes como un objeto JSON. Esta información puede ser verificada y confiable porque está firmada digitalmente. Los JWT pueden ser firmados usando un secreto (con el algoritmo HMAC) o un par de claves públicas/privadas usando RSA o ECDSA.
JWT se utiliza para autorizar al usuario, una vez logueado, a acceder a las rutas, servicios y recursos que se permiten con ese token a los usuarios en las aplicaciones después de un inicio de sesión válido y también para transmitir información de forma segura entre las partes
Éstos se basan en el formato JSON e incluyen una firma para asegurar la integridad del token.
2. Estructura de JWTs
Se trata de una cadena de texto formada por tres partes codificadas en Base64, donde cada una de ellas está separada por un punto, como se observa en la siguiente imagen:

Este token puede ser decodificado y mostraría la siguiente apariencia con diferentes Claims (propiedades):

El token está formado por tres partes: header, payload y signature.
Header
La primera parte «header«, hace referencia al encabezado dónde se indica, al menos, el algoritmo (alg) y el tipo de token (typ).

En la imagen, correspondería a la siguiente cadena «eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9»
Existen varios tipos de algoritmo como HS256 y RS256:
- HS256: Simétrico, usa el mismo «secret» para firmar y para verificar el token. Es más seguro (siempre y cuando el «secret» no se vea comprometido y sea robusto) , más rápido y el token es más pequeño.
- RS256: Asimétrico (un par de claves públicas/privadas), usa una clave privada para firmar el token y usa una clave pública para verificar el token.
Se puede emplear HS256 de forma segura, ya que se tiene el control sobre quién usa las claves secretas. En el caso de que no se tenga control sobre el cliente o si no se tiene forma de asegurar la «shared key», es mejor emplear RS256 ya que el consumidor solo necesita conocer la clave pública (compartida).
Payload
La segunda parte «payload«, hace referencia a los datos de usuario y privilegios de éste. Existen 3 tipos de Claims:
- Public Claim Names: son valores personalizados públicos. Para evitar colisiones, se recomienda registrarlos en IANA JSON Web Token Registry.
- Private Claim Names: son valores personalizados privados. Estos pueden estar sujetos a colisiones.
- Registered Claim Names: son valores acerca del registro. Estos proporcionan información acerca del contenido. Existen varios tipos de de Claim especificos:
- iss: Identifica al emisor del token. El uso de este claim es opcional.
- sub: Identifica el asunto del JWT. El valor del sujeto debe ser contemplado para ser locamente único en el contexto del emisor o ser globalmente único. El uso de este claim es opcional.
- aud: Identifica a los receptores del JWT. El uso de este claim es opcional.
- exp: Identifica la hora de expiración, a partir de la cual, el token no será válido para su procesamiento. El uso de este claim es opcional.
- nbf: Identifica el momento antes del cual el JWT no debe ser aceptado para su procesamiento. Requiere que la fecha/hora actual debe ser posterior o igual a la fecha/hora que figura en ‘nbf’. El uso de este claim es opcional.
- iat: Identifica la hora a la que fue emitido el token. El uso de este claim es opcional.
- jti: Identifica un ID único para el JWT. Este debe asignarse de manera que asegure que hay una probabilidad insignificante de que el mismo valor sea asignado accidentalmente a un objeto de datos diferente; si la aplicación utiliza múltiples emisores, se deben prevenir las colisiones entre los valores producido por diferentes emisores también. El uso de este claim es opcional.
En la imagen, correspondería a la siguiente cadena «eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ»

Signature
Por último, la tercera parte «signature«, permite verificar si el token es válido. En la imagen, correspondería a la siguiente cadena «SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c»
3. Firma de un token JWT
La firma de un token JWT garantiza la validez del mensaje y de remitente.
La firma se construye como el HMACSHA256, que son las siglas de Hash-Based Message Authentication Code (Código de Autenticación de Mensajes), cifrado con el algoritmo SHA de 256. Esta función es aplicada a:
- Codificación en Base64 de header.
- Codificación en Base64 de payload.
- Un secreto (establecido por la aplicación).

La firma se genera, en primer lugar, cuando la aplicación concatena la cadena de «header» y «payload» en Base64url. Después, se usa el secreto y se aplica el algoritmo presente en «header» para generar la «signature».
Todo este proceso no garantiza que el Token JWT sea completamente seguro.
Con las medidas mencionadas, en el caso de que se trate de modificar algún dato, se podría verificar la integridad del mensaje y denegar la solicitud de datos que hayan sido solicitados. Siempre se debería de verificar con esta firma si el token es válido.
4. Securización de Token JWT
Es necesario tener en cuenta la securización del token debido a que los datos de «header» y «payload» se codifican en Base64url y con lo cual, no van cifrados sino codificados.
Se recomienda que la comunicación entre el cliente y el servidor vaya cifrada con el protocolo HTTPS.
5. Ciclo de vida de un Token JWT
A continuación, se explica el ciclo de vida de un Token JWT.
En primer lugar, el cliente hace una petición POST al servidor enviando el usuario y contraseña. De esta manera, se realiza el proceso de login.
A continuación, en el servidor se comprueba que el usuario y contraseña son correctos, y si lo son, se genera un token JWT que es enviado al cliente.
A partir de ese momento, la aplicación cliente, con el token generado, hace peticiones al cliente solicitando recursos. Este token JWT siempre va en las cabeceras de las peticiones; se mostraría de la siguiente forma «Authorization: Bearer XXXXXXX», siendo Bearer el prefijo que precede al contenido del token.
En la parte del servidor, se comprueba la validez del token mediante la firma para verificar que el token es válido.
Una vez verificado el token e identificado al usuario que ha realizado la petición, se aplica el control de acceso a los recursos que solicite.

6. Understanding HS256
HS256 es un algoritmo de firma SHA256 que codifica «Header«,»Payload» y «Secret» en base64url.

- En primer lugar, se mandan las credenciales del login desde «Client» a «Authorization Server» y este comprueba si las credenciales son válidas.
- En el caso de que las credenciales sean válidas, «secret key» es empleada para la generación de un token JWT válido. (Esa «secret key» es distribuida a los diferentes «Resource Server» presentes en la aplicación).
- Una vez que «Client» tiene el token JWT válido e intenta acceder a «Resource Server» este comprueba que la «secret key» con la que se ha firmado el token JWT es la misma que el posee.
Un ataque no tendría acceso a «Resource Server» debido a que, en primer lugar, comprueba la existencia de un token JWT válido. Para poder obtener «secret key» tendría que darse alguna de las siguientes casuísticas:
- Dado que «secret key» es distribuida a todos los servidores, si se compromete un servidor, se podría obtener dicha «secret key«.
- Si el «Header» de «secret key» que se emplea con HS256 es débil, ésta puede ser obtenida a través de fuerza bruta.
.
Ejemplo
En primer lugar, se ejecuta la máquina virtual y se observa la IP que nos asigna.
Seguidamente, se ejecuta en nuestro entorno «Postman» y se lanza la petición de login (esta se facilita en el pdf de la documentación del curso).

Se observa la respuesta de la petición, donde devuelve el token JWT de acceso generado a partir del algoritmo de tipo HS256.

Una vez generado el token JWT, se accede vía ssh a la máquina donde está desplegada la aplicación. Se abre «api.php» que es el archivo donde se encuentra el código fuente asociado al endpoint del login y del getccinfo

7. Understanding RS256
RS256 es la «signature» del hash generado a partir del «Header» y del «Payload«. Dicho hash se firma utilizando la clave privada de RSA. Esta firma generada es la tercera parte del JWT.
RS256 usa algoritmos de cifrado RSA y SHA256.
Solamente se puede emplear este tipo de firma si se tiene la «secret key» para descifrar el hash generado. Esta «secret key» solamente se encuentra disponible en «Authorization Server», que es donde se generan los token JWT y no se encuentra disponible en ningún «Resource Server».

- En primer lugar, se mandan las credenciales del login desde «Client» a «Authorization Server» y este comprueba si las credenciales son válidas.
- En el caso de que las credenciales sean válidas, «secret key» es empleada para la generación de un token JWT válido.
- Una vez obtenido un token JWT válido, cuando se quiera acceder a «Resource Server», este tiene que comprobar si la «signature» del JWT es válido o no. Usa la «public key» (presente en los «Resource Server») para validar si la «signature» presente en el JWT es válida o no.
Si el atacante quiere acceder a «Resource Server» no podrá acceder debido a que no tiene un token JWT válido. Para poder acceder a «Resource Server», el atacante debe tener las credenciales del login y un token JWT válido o tener acceso a «secret key«, habiendo comprometido previamente «Authoriztion Server» que es donde se localiza «secret key«, para generarse sus propios tokens JWT válidos.
El empleo de RS256 implica:
- «Private key» solamente está presente en «Authorization Server».
- Solamente aquel servidor que tenga la «private key» puede crear nuevos tokens JWT válidos.
- El empleo de cifrados de tipos RSA son lo suficientemente largos para prevenir los ataques de fuerza bruta.
8. Recomendaciones para evitar ataques a JWT
No se recomienda el uso del algoritmo ‘none’ en la generación de JWT.
Se recomienda el uso de claves compartidas robustas para evitar, de esta forma, que sea fácil crackear esta clave.
Se recomienda el uso de un algoritmo en vez de una whitelist donde permita, por ejemplo, el algoritmo ‘none’. En el caso de que se necesite emplear algoritmos como RS256 y HS256, se recomienda que se separen ambos algoritmos en funciones diferentes para tener un control sobre las claves que se emplean en cada caso.
9. Diferentes ataques
None algorithm attack
JWT soporta el algoritmo de tipo «none». Si el campo «alg» está configurado como «none», cualquier token puede estar considerado valido dejando la sección «signature» vacía.
Esto permitirá a los atacantes utilizar un token falso y hacerse pasar por otro usuario utilizando un token suplantado.
En primer lugar, se instala la extensión llamada «JSON Web Tokens» para manipular los tokens JWT «on the fly», comprobar su validez y automatizar los ataques comunes.

Se acceder a https://jwt-lab.herokuapp.com/users/new y se crea un usuario con el cual se van a realizar las pruebas:


En segundo lugar, se accede a la web: https://jwt-lab.herokuapp.com/authentication/none, se activa el «Intercept» del Burp y se introducen los datos que se han generado: test_es/test.
Se deja pasar la petición, se intercepta la siguiente, se observa que nos devuelve un parámetro llamado «challenge» donde viene el JWT y nos indica que nuestro usuario actual es «test_es»:


Se envía la petición al «Repeater» para analizarla:
Una vez esté la petición en el apartado «Repeater», se selecciona el JWT que la aplicación ha devuelto y se analiza. Existen varias formas de analizarlo: La primera es mandarlo al «Decoder» y la segunda con la extensión «JSON Web Tokens«, instalada anteriormente.
Opción «Decoder»:
Se manda el JWT al apartado «Decoder».
Una vez en el apartado «Decoder», se selecciona la opción «Base64».
Cuando se codifica, se observa la información que contiene el JWT.
Opción «JSON Web Tokens»:
Se pincha en la opción «JSON Web Tokens«.

La extensión devuelve la información del JWT de una forma sencilla de interpretar la información que tiene el token.
En este caso, se quiere comprobar que permite un ataque al algoritmo dejando este vacío. Se modifica el tipo de algoritmo a «none» y en valor del parámetro «name» del Payload a «admin», que es el usuario que se pide.
Se lanza la petición y se comprueba en la respuesta que el usuario actual es «admin».
Cabe destacar que el valor del parámetro «challenge» solamente contiene «header» y «payload«. El algoritmo, al ser de tipo «none» no requiere de «signature«.
The signature is not checked
Un fallo de seguridad en JWT suele ser que no se verifica el token cuando lo recibe la aplicación. Un atacante podría eludir este paso de verificación proporcionando a la aplicación una «signature» invalida.
Esto permitirá a los atacantes suplantar cualquier token que quieran y hacerse pasar por otro usuario usando un token suplantado.
Se accede a la web https://jwt-lab.herokuapp.com/authentication/signature y se observa que el usuario creado anteriormente está logueado.
Se recarga la web, se intercepta y se manda al «Repeater«.
Se emplea la extensión «JSON Web Tokens«.

Se elimina el valor del campo «Signature» y se modifica el valor del campo «Payload» de «test_es» a «admin».

Por último, se lanza la petición y se comprueba que se tiene un JWT válido con el usuario «admin».
The signature is weak
En este ataque se verá cómo se puede obtener, en texto claro, la firma obtenida mediante fuerza bruta.
En primer lugar, acceder a la URL donde está el reto: https://jwt-lab.herokuapp.com/authentication/weak.
A continuación, se realiza el login con el usuario creado en el reto inicial: test_es/test. Una vez logueado, con una extensión del navegador, como «Cookie Editor«, se observa que una flag se llama «challenge«, se copia el valor, que es el token válido y se genera un fichero llamado «secretkey.hash» que posteriormente será usado.
Con la herramienta hashcat, se crackea la «signature» del token anteriormente guardado en el archivo «secretkey.hash» facilitándole un diccionario existente como «rockyou.txt» o generando uno con la herramienta Crunch, por ejemplo.
Una vez finalizado el crackeo de la contraseña, hashcat muestra el valor de la firma en texto claro.
Una vez obtenido el valor de la clave, se captura una sesión de login y se manda al Repeater.
Una vez mandada la petición al Repeater, se emplea la extensión «JSON Web Tokens«.
Si se ejecuta la petición original, siendo el usuario ‘test_es’ el que está logueado, la aplicación indicaría que el usuario actual es ‘test_es’.
Como se ha conseguido el valor de la firma, se recalcula la firma introduciendo el valor de la key y modificando el usuario ‘test_es’ por el usuario ‘admin’.
Una vez modificados estos parámetros, se lanza la petición y se comprueba que se ha realizado el inicio de sesión como ‘admin’.
El token válido del usuario ‘test_es’ es: eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidGVzdF9lcyJ9.DZSgsFtFQWMePcXMpuOfEU3ouJKFUcr0vWRzyHAB8vw
El token válido del usuario ‘admin’ es: eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYWRtaW4ifQ.gDs9V-6by-T2sF5CVXQn7WPOZ_YkAfg0nx-pzLMQk
Brute forcing the signature
En algunos casos cuando se genera los JWT, en la fase de implementación se ha emplea una «key» débil para generar la «signature«. Esto deja expuesto a que esa «signature» pueda ser descubierta utilizando la técnica «brute force» y emplearla para generar «signatures» propias.
Esto permitirá a los atacantes suplantar cualquier token que quieran y hacerse pasar por otro usuario usando un token suplantado.
Se recomienda el uso de «keys» robustas y largas o tokens RS256.
Si se dispone de un token válido, por ejemplo «eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDc4MDIzMTQsImlzcyI6ImxvY2FsaG9zdCIsImV4cCI6MTYwNzgxMTMxNCwidXNlcklkIjoiYWRtaW4iLCJpc0FkbWluIjoidHJ1ZSJ9.iGnnxauj6mCBnXtCuwQl-uP1LRLCyGrwArZ8xF7sWsY», se pueden emplear herramientas como «jwt-cracker» para obtener el valor de la «signature«.
En primer lugar, se instala la aplicación ejecutando el comando «npm install –global jwt-cracker».
Después empleamos la herramienta. Su sintaxis es la siguiente: jwt-cracker
De esta forma se obtendría la secret key.
10. Referencias:
Debugger online: https://jwt.io/
Laboratorio: https://jwt-lab.herokuapp.com/
https://adamc95.medium.com/json-web-token-lab-guide-c402857fa44c
https://medium.com/swlh/hacking-json-web-tokens-jwts-9122efe91e4a
JWT: https://openwebinars.net/blog/que-es-json-web-token-y-como-funciona/
RFC7519 – https://tools.ietf.org/html/rfc7519
https://www.developerro.com/2019/03/12/jwt-api-authentication/
jwt-cracker – https://github.com/lmammino/jwt-cracker
https://theoffensivelabs.com/p/hacking-and-securing-json-web-tokens-jwt
La imagen de cabecera se ha compuesto con una foto cortesía de Pixabay
Firmado: Carol
Sh3llCON no se hace responsable de las opiniones vertidas por sus colaboradores ni por las actuaciones que, fruto del conocimiento transmitido, puedan realizar terceras personas.