Vulnerabilidades JWT
JSON Web Tokens
General
Es un estándar basado en JSON (Javascript Object Notation) para la creación de tokens de acceso que permiten la propagación de identidad y privilegios. El token está firmado por la clave del servidor, así que el cliente y el servidor son ambos capaces de verificar que el token es legítimo.
Los JSON Web Tokens están diseñados para ser compactos, poder ser enviados en las URLs -URL-safe- y ser utilizados en escenarios de Single Sign-On (SSO).
El estándar de JWT se basa en otros estándares basados en JSON JSON Web Signature (RFC 7515) y JSON Web Encryption (RFC 7516)
JWT = Datos en JSON criptográficamente firmados
JWE = JWT encriptado
Consideraciones:
Utilizar JWT previene de los ataques CSRF. Pero puede seguir siendo vulnerable si se almacena en una cookie.
Si se almacena en local storage, es susceptible a ataques XSS.
Los JWTs son buenos contra ataques de dominio cruzado (cross domain).
Estructura
Están formados por 3 partes:
Una cabecera o header. Indentifica el algoritmo utilizado para generar la firma.
Algoritmos aceptados:
RSA.
HMAC.
Elliptic Curve.
None.
Un contenido o payload. Contiene los privilegios del token.
Valores:
iss: issuer
iat: issued at
nbf: “not before” (start date)
sub: subject
exp: expires at
Puede contener cualquier información, como: Nombre de usuario, email, rol, permisos, contraseñas.
Una firma digital o signature. Que se calcula codificando la cabecera y el contenido en base64, concatenándose con un punto.
Generalmente los algoritmos de cifrado utilizados son HMAC con SHA-256 (HS256) y firma digital con SHA-256 (RS256).
Cómo detectar tokens JWT
Normalmente empiezan con eyJ0eXAi
o eyJhbGci
.
eyJ0eXAi
={"typ"
eyJhbGci
={"alg"
Y un punto en los primeros 40-60 caracteres.
Convertir de B64String to B64URL
Sustitución de caracteres:
+
-> -
/
-> _
=
-> ` ` (se elimina)
Obtener información en texto plano
Ej:
Ataques comunes
Fallo en la verificación de la firma
Muchas librerías JWT tienen un método para decodificar y otro para verificar el token:
decode()
. Decodifica el token sin verificar la firma.verify()
. Decodifica y verifica el token.
Durante el desarrollo suele utilizarse el método decode()
y cuando se pasa la aplicación a producción se suele olvidar cambiar el método por verify()
.
Algoritmo none permitido
Si se cambia el algoritmo por none en alguna de sus siguientes variantes y se elimina el tercer campo del JWT dejando el punto final.
Ej:
Confusión de algoritmos
Esta vulnerabilidad se da cuando la aplicación no comprueba que el algoritmo coincida con el algoritmo que se espera.
Muchas librerías no comprueban por defecto si el algoritmo coincide, por eso se utiliza la siguiente Comprobación del algoritmo para verificarlo.
ALGORITMOS
Algoritmo asimétrico: Cuando se utiliza una encriptación asimétrica, se publica la clave pública y se mantiene la clave privada en secreto. Esto permite firmar el token con la clave privada y que cualquiera pueda verificarla con la clave pública.
RSA
Si la clave pública está disponible, un atacante puede falsificar tokens:
Cambiando el algoritmo del token por HMAC.
Manipulando el resultado con el payload.
Firmar el token con la clave pública detectada en la aplicación o en el JWT.
Enviar el JWT de vuelta a la aplicación.
HMAC
La fortaleza de la firma vendrá determinada por la fortaleza de la contraseña. Si la aplicación utiliza una contraseña débil, se podrá llegar a obtener por medio de fuerza bruta.
COMPROBACIÓN DEL ALGORITMO
Normalmente las librerías comprueban el algoritmo de la siguiente manera:
HMAC.
verify(token, secret)
.RSA o similar.
verify(token, secret)
.
Ataques contra JWT
Inyección en parámetro kid
Si la cabecera contiene el parámetro kid
, este normalmente es usado para obtener la clave de una base de datos o sistema de ficheros. La aplicación verifica la firma utilizando la clave obtenida en este parámetro.
Si el parámetro es inyectable, este podría ser utilizado para saltarse la firma o realizar ataques de RCE, SQLi o LFI.
EJEMPLO
JWT Original
JWT con inyección
Bypass de la firma
INYECCIÓN EN PARÁMETRO KID + DIRECTORY TRAVERSAL
Si la aplicación utiliza el parámetro kid como valor de la clave utilizada para validar la firma, un atacante podría modificarlo por un fichero con el valor de la clave, por ejemplo, /dev/null
y con ello saltársela.
Ejemplo
INYECCIÓN EN PARÁMETRO KID + INYECCIÓN SQL
Si la aplicación utiliza el parámtro kid para obtener la información de la base de datos como la clave de la firma, se podría intentar inyectar código SQL.
Ejemplo de consulta SQL
Ejemplo de inyección SQL en kid
Consulta con inyección en kid
La inyección ' UNION SELECT 'aaaa
permite a un atacante sustituir la clave por aaaa
y con ello firmar el token con dicha clave para saltarse la firma.
Manipulación del parámetro jku
Este parámetro se utiliza para especificar el JSON Web Key Set URL (RFC7515), que indica donde se encuentra el JSON Web Key utilizado para verificar la firma.
Ejemplo de JWT con jku
Ejemplo de JSON Web Key (fichero key.json)
FORMAS DE SALTARSE LAS LIMITACIONES
Utilizando
https://xtormin
si la aplicación solo comprueba el inicio de la URL (ej:https://xtormin@attacker.com/key.json
).Utilizando fragmentos con el caracter
#
.Utilizando la jerarquía de nombres DNS.
Concatenándolo con open redirect, header injection o ssrf.
### Prevención
Para prevenirlo, se debe:
Filtrar la URL correctamente.
Añadir los hosts permitidos en whitelist.
Prevenir otros ataques que puedan ser concatenados para saltarse las restricciones.
Herramientas
JWT Tool
https://github.com/ticarpi/jwt_tool.git
Extensión de Burp
JSON Web Token Attacker - JOSEPH
Securización
Recursos
Vídeos
Última actualización