# JWT / JWS / JWE

> JSON Web Token, Signature y Encryption — tokens y cifrado a nivel de aplicación

Familia de estándares IETF (RFC 7515-7519) que define un formato compacto para transportar claims firmados o cifrados. Pieza fundamental de la autenticación moderna (OAuth, OIDC, FAPI), de la firma a nivel de aplicación y del transporte seguro de datos estructurados.

_Categoría: technique_

---

## Definición

Los **JWT / JWS / JWE** forman una familia de estándares IETF (RFC 7515 a 7519, 2015) que define un formato compacto, URL-safe y portable para transportar _claims_ (aserciones) firmados o cifrados en formato JSON.

Es la pieza fundamental de la autenticación moderna (access tokens OAuth, ID Tokens OIDC), de la firma a nivel de aplicación y del transporte seguro de datos estructurados (webhooks firmados, atestaciones).

| Estándar | RFC | Función |
|----------|-----|------|
| JWT | 7519 | Formato general (claims JSON codificados) |
| JWS | 7515 | Firma (integridad, autenticidad) |
| JWE | 7516 | Cifrado (confidencialidad) |
| JWA | 7518 | Algoritmos (HS256, RS256, A256GCM…) |
| JWK | 7517 | Representación de las claves en JSON |

## Formato JWT (firmado = JWS)

Tres partes separadas por puntos:

```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNzE0MDAwMDAwfQ
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```

- **Header** (base64url): `{"alg":"HS256","typ":"JWT"}`
- **Payload** (base64url): `{"sub":"1234567890","name":"Alice","iat":1714000000}`
- **Firma**: HMAC o RSA/ECDSA según el `alg`

Los claims estándar: `iss` (emisor), `sub` (sujeto), `aud` (destinatario), `exp` (expiración), `nbf` (not before), `iat` (issued at), `jti` (identificador único anti-replay), además de claims privados (`email`, `roles`, `scope`).

## Algoritmos de firma (JWS)

| Algo | Tipo | Uso típico |
|------|------|----------------|
| `HS256` | HMAC simétrico | Pruebas, monolitos |
| `RS256` | RSA asimétrico | OAuth, OIDC clásico |
| `PS256` | RSA-PSS | Recomendado para FAPI (Open Banking) |
| `ES256` | ECDSA P-256 | Recomendado para FAPI, móvil |
| `EdDSA` | Ed25519 | Moderno, rápido |
| `none` | Sin firma | Prohibido (fallo histórico) |

Buenas prácticas: preferir el asimétrico (clave pública compartible) para las API públicas, priorizar PS256/ES256, no aceptar nunca `alg: none` y verificar siempre el `alg` esperado en el servidor en lugar de confiar en el header.

## JWE (cifrado)

JWE cifra el payload (confidencialidad) además de la integridad, en 5 partes: `header.encrypted_key.iv.ciphertext.tag`. Key encryption común: RSA-OAEP, ECDH-ES, A256KW; content encryption: A256GCM, A128CBC-HS256. Uso: transporte de datos secretos (tarjeta tokenizada, datos de KYC) entre servicios.

## JWT vs sesión tradicional

| Aspecto | Cookie de sesión | JWT |
|--------|----------------|-----|
| Estado | Servidor | Cliente (stateless) |
| Escalado | Sticky session o BD compartida | Trivial |
| Revocación | Eliminación en el servidor | Difícil (blacklist) |
| Legibilidad | Opaco | Legible (base64url) |
| Riesgo de robo | Cookie httpOnly | Bearer = portador |

## Casos de uso

- **OAuth / OIDC**: access token (a menudo JWT; FAPI exige un JWT firmado y vinculado), ID Token (siempre JWT firmado), Request Object JAR (JWT firmado).
- **API**: un bearer JWT en `Authorization`, verificado mediante la clave pública (endpoint JWKS).
- **Webhooks**: Stripe, GitHub y Slack firman en JWS.
- **Identidad / KYC**: Verifiable Credentials de la W3C y SD-JWT (divulgación selectiva) para el EUDI Wallet.
- **eIDAS**: JAdES, perfil de firma cualificada basado en JWS.

## Ataques conocidos

- **`alg: none`**: algunas bibliotecas antiguas aceptaban un payload no verificado.
- **Confusión de algoritmo (HS256 vs RS256)**: firmar con la clave pública RSA como secreto HMAC, si el servidor no fija el `alg`.
- **Inyección de `kid`**: un `kid` mal validado puede abrir una SQL injection o un path traversal.
- **Replay**: un JWT robado sigue siendo válido hasta `exp` (mitigación: `jti` + caché, tokens cortos).
- **Fuga de información**: el payload es legible — no poner en él ningún dato sensible, cifrar en JWE si hace falta.

## Buenas prácticas

Preferir PS256/ES256, tokens cortos (5-15 min, refresh largo), tokens vinculados al cliente (mTLS, DPoP) en FAPI; verificar sistemáticamente `iss`, `aud`, `exp`, `nbf` y `alg`; rotar las claves (JWKS); evitar las PII en claro (JWE si es necesario); auditar con regularidad las dependencias (CVE).

## Lo que JWT / JWS / JWE no es

- **No es un cifrado de transporte**: es a nivel de aplicación, TLS sigue siendo necesario.
- **No es una sesión**: un token, generalmente corto y stateless.
- **No es una contraseña**: un token de acceso emitido tras la autenticación.
- **No es revocable de forma nativa**: válido hasta la expiración, salvo blacklist mantenida.

## En el ecosistema PSD2 / Open Finance

JWT y JWS están por todas partes: access tokens OAuth en la PSD2, ID Tokens OIDC (FranceConnect, EUDI Wallet), Request Objects JAR en FAPI, webhooks firmados (Bridge, Tink, Plaid) y Verifiable Credentials EUDI en SD-JWT.

## Ejemplos concretos

- **Webhooks de Stripe**: firmados en HMAC SHA-256, un formato cercano al JWS.
- **Auth0, Okta, Cognito**: emiten JWT.
- **OBIE UK**: access tokens e ID Tokens en JWT PS256.
- **EUDI Wallet**: credentials en SD-JWT (probar "mayor de edad" sin revelar la fecha de nacimiento).
- **FranceConnect**: ID Tokens JWT.
- **Vulnerabilidad de 2018**: el fallo de "confusión de algoritmo" corregido globalmente en las bibliotecas.
- **Herramientas**: jwt.io (debugger), `jose` (JS), PyJWT, java-jwt, jose4j.

## Fuentes

- [RFC 7519 — JSON Web Token (JWT)](https://datatracker.ietf.org/doc/html/rfc7519) — IETF, 2015-05
- [RFC 7515 — JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515) — IETF, 2015-05
- [RFC 7516 — JSON Web Encryption (JWE)](https://datatracker.ietf.org/doc/html/rfc7516) — IETF, 2015-05

---

Fuente: https://openfinanceguide.com/es/glossary/jwt-jws-jwe
