JWT (JSON Web Tokens) - Autenticacion basada en tokens
Los JWT (JSON Web Tokens) son tokens autocontenidos para transmitir informacion de sesion de forma estandarizada. Se usan en autenticacion basada en tokens, muy comun en APIs, y su…
Definicion
Los JWT (JSON Web Tokens) son tokens autocontenidos para transmitir informacion de sesion de forma estandarizada. Se usan en autenticacion basada en tokens, muy comun en APIs, y su seguridad depende de como se firman, validan y expiran.
Contexto
- Se apoya en gestion de sesiones: Gestion de sesiones (Session Management).
- Relacionado con fallos de autenticacion: Authentication Failures (OWASP Top 10 2025 - A07).
- Relevante para APIs: OWASP API Security Top 10 (2019).
- OAuth usa tokens de acceso; ver OAuth 2.0 y 2.1 (OAuth Security).
Desarrollo (MIT-style)
1) Autenticacion basada en tokens (API context)
Con el auge de las API, la autenticacion basada en cookies no siempre encaja bien (multi‑cliente: web + mobile). Por eso se usa token-based session management:
- El servidor entrega un token tras autenticacion.
- El cliente lo guarda (p. ej., LocalStorage).
- En cada request, el cliente adjunta el token en
Authorization: Bearer.
Documentacion de API (cuando existe):
- Postman (proyectos)
- Swagger/OpenAPI (spec)
2) Estructura de un JWT
Un JWT tiene 3 partes codificadas en Base64Url y separadas por puntos:
- Header: tipo (JWT) y algoritmo.
- Payload: claims (registrados + publicos/privados).
- Signature: asegura autenticidad.
Algoritmos de firma clave:
- None: sin firma (riesgoso si se acepta).
- HS256 (simetrico): secreto compartido.
- RS256 (asimetrico): clave privada firma, clave publica verifica.
Nota: JWT puede cifrarse (JWE), pero su fuerza principal es la firma.
3) API de laboratorio (estructura comun)
- Endpoint base:
http://MACHINE_IP/api/v1.0/exampleX. - POST: autenticacion y obtencion de JWT.
- GET: verificacion/consulta de usuario.
Credenciales base:
username: userpassword: passwordX
cURL autenticacion:
curl -H 'Content-Type: application/json' -X POST -d '{ "username" : "user", "password" : "passwordX" }' http://MACHINE_IP/api/v1.0/exampleXcURL verificacion:
curl -H 'Authorization: Bearer [JWT token]' http://MACHINE_IP/api/v1.0/example2?username=Y4) Vulnerabilidades comunes y ejemplos
A) Divulgacion de informacion en claims
Problema: se incluyen datos sensibles dentro del JWT (p. ej., password, hash, flag, IP interna).
Ejemplo vulnerable:
payload = { "username" : username, "password" : password, "admin" : 0, "flag" : "[redacted]"}access_token = jwt.encode(payload, self.secret, algorithm="HS256")Solucion: guardar datos sensibles en backend y consultar con username.
payload = jwt.decode(token, self.secret, algorithms="HS256")
username = payload['username']flag = self.db_lookup(username, "flag")B) No verificar firma
Problema: el servidor acepta tokens sin firma valida.
Ejemplo vulnerable:
payload = jwt.decode(token, options={'verify_signature': False})Solucion: siempre verificar firma con secreto o clave publica.
payload = jwt.decode(token, self.secret, algorithms="HS256")C) Downgrade a None
Problema: aceptar alg = None en header.
Ejemplo vulnerable:
header = jwt.get_unverified_header(token)signature_algorithm = header['alg']payload = jwt.decode(token, self.secret, algorithms=signature_algorithm)Solucion: limitar algoritmos permitidos con lista fija.
payload = jwt.decode(token, self.secret, algorithms=["HS256", "HS384", "HS512"])D) Secretos simetricos debiles (HS256)
Problema: secreto corto o predecible permite cracking offline.
Ejemplo practico (solo laboratorio):
# guardar token en jwt.txt# descargar lista comunwget https://raw.githubusercontent.com/wallarm/jwt-secrets/master/jwt.secrets.list
# crackear secretohashcat -m 16500 -a 0 jwt.txt jwt.secrets.listAlternativas: John the Ripper.
Solucion: usar secreto largo y aleatorio.
E) Confusion de algoritmos (RS256 -> HS256)
Problema: mezclar algoritmos simetricos y asimetricos.
Ejemplo vulnerable:
payload = jwt.decode(token, self.secret, algorithms=["HS256", "HS384", "HS512", "RS256", "RS384", "RS512"])Ataque: usar clave publica (conocida) como secreto en HS256.
Ejemplo (forjar token):
import jwt
public_key = "ADD_KEY_HERE"
payload = { 'username' : 'user', 'admin' : 0}
access_token = jwt.encode(payload, public_key, algorithm="HS256")print(access_token)Nota de laboratorio: en PyJWT puede requerir editar jwt/algorithms.py (lineas 143-146 y 258) para reproducir el bug en entornos vulnerables. Alternativa: usar jwt.io.
Solucion: separar algoritmos y validar segun tipo.
header = jwt.get_unverified_header(token)algorithm = header['alg']payload = ""
if "RS" in algorithm: payload = jwt.decode(token, self.public_key, algorithms=["RS256", "RS384", "RS512"])elif "HS" in algorithm: payload = jwt.decode(token, self.secret, algorithms=["HS256", "HS384", "HS512"])5) Vida util del token (exp)
Problema: JWT sin exp o con vida demasiado larga.
Ejemplo vulnerable: token sin expiracion.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MX0.ko7EQiATQQzrQPwRO8ZTY37pQWGLPZWEvdWH0tVDNPUSolucion: incluir exp razonable.
lifetime = datetime.datetime.now() + datetime.timedelta(minutes=5)
payload = { 'username' : username, 'admin' : 0, 'exp' : lifetime}
access_token = jwt.encode(payload, self.secret, algorithm="HS256")Nota: tokens de actualizacion pueden mitigar sesiones largas.
6) Ataque de retransmision entre servicios (audience)
Problema: no validar aud en cada aplicacion.
Escenario: token emitido para appB aceptado en appA y hereda permisos.
Solucion: validar audiencia al decodificar:
payload = jwt.decode(token, self.secret, audience=["appA"], algorithms="HS256")7) Resumen de defensas
- No incluir datos sensibles en claims.
- Verificar firma siempre.
- Bloquear
alg = None. - No mezclar algoritmos HS/RS sin logica estricta.
- Usar secretos fuertes (HS) o claves correctas (RS).
- Definir
expy caducidad acorde a riesgo. - Validar
auden cada servicio.
No se cubre aqui la suplantacion de JWKS; ver room:
hammer.
Ejemplos
Decode JWT (manual o jwt.io).
Authorization header:
Authorization: Bearer <jwt>Pitfalls / Errores comunes
- Guardar passwords/hashes en claims.
- Aceptar tokens sin firma valida.
- Secretos debiles reutilizados.
- Tokens sin expiracion o demasiado largos.
- No verificar
auden arquitecturas multi‑app.
Diagrama
flowchart LR A[Login] --> B[JWT firmado] B --> C[Cliente guarda token] C --> D[Authorization: Bearer] D --> E[API valida firma + exp + aud]
Referencias
- RFC 7519 (JWT): https://tools.ietf.org/html/rfc7519
- jwt.io: https://jwt.io/
- CyberChef: https://gchq.github.io/CyberChef/
- PyJWT docs: https://pyjwt.readthedocs.io/en/stable/
- Hashcat: https://hashcat.net/hashcat/
- John the Ripper: https://www.openwall.com/john/
- JWT secrets list (Wallarm): https://raw.githubusercontent.com/wallarm/jwt-secrets/master/jwt.secrets.list
- Postman: https://www.postman.com/
- Swagger: https://swagger.io/
- THM Intro to Web Hacking: https://tryhackme.com/module/intro-to-web-hacking
- THM Enumeration & Brute Force: https://tryhackme.com/r/room/enumerationbruteforce
- THM Session Management: https://tryhackme.com/r/room/sessionmanagement
- THM Hammer (JWKS): https://tryhackme.com/r/room/hammer