El RFC 9700, publicado en enero de 2025 como BCP 240, distingue qué medidas de endurecimiento de OAuth 2.0 son MUST y cuáles son solo SHOULD. Un redirect URI con comodín olvidado en la consola de un proveedor de identidad es el tipo de configuración que el documento descarta, y un código de cliente impecable no lo compensa. El RFC actualiza el RFC 6749, el RFC 6750 y el RFC 6819, y advierte de que seguirlo puede romper la interoperabilidad con ecosistemas antiguos. Aun así, recomienda actualizar en cuanto sea factible.
Los requisitos, por nivel de exigencia
| Ajuste | Nivel | Postura del RFC 9700 |
|---|---|---|
| Comparación de redirect URI | MUST | Los servidores de autorización usan coincidencia exacta de cadenas |
| Open redirectors | MUST NOT | Ni los clientes ni los servidores de autorización deben exponerlos |
| Protección frente a CSRF | MUST | Los clientes protegen el endpoint de redirección: PKCE, nonce o un state de un solo uso |
| Defensa contra mix-up, varios servidores de autorización | REQUIRED | Parámetro iss o valor iss equivalente; redirect URI distintos como alternativa |
| PKCE, clientes públicos | MUST | Obligatorio en el authorization code grant |
| PKCE, clientes confidenciales | RECOMMENDED | Los clientes OpenID Connect MAY usar nonce en su lugar, con precauciones adicionales |
Verificación del code_verifier | MUST | Se exige en el token endpoint siempre que se haya enviado un code_challenge válido |
| Método del PKCE challenge | SHOULD | Uno que no exponga el verifier; actualmente solo S256 |
Implicit grant (response_type=token) | SHOULD NOT | Usar code u otro tipo que emita los tokens en el token endpoint |
| Access tokens sender-constrained | SHOULD | Mutual TLS o DPoP |
| Refresh tokens, clientes públicos | MUST | Sender-constrained o con rotación |
Redirect URI: coincidencia exacta y sin open redirectors
Cuando el servidor de autorización compara el redirect_uri de una petición con los valores registrados, debe usar coincidencia exacta de cadenas. La única excepción es el número de puerto en los redirect URI de localhost de las apps nativas. Los comodines, las coincidencias por prefijo y los registros del tipo «cualquier ruta bajo este host» incumplen esta regla.
La lista de URI registrados también importa, porque cada entrada es un destino permitido para los authorization codes. Elimine las entradas obsoletas, sobre todo las de hosts que ya no controla, como un dominio de staging dado de baja que otra persona podría registrar.
Los open redirectors son la otra mitad del problema. Un endpoint que reenvía el navegador a un URI arbitrario tomado de un parámetro de la query puede filtrar authorization codes y access tokens, y el RFC establece que ni los clientes ni los servidores de autorización pueden exponer uno. El ejemplo de OWASP http://example.com/example.php?url=http://malicious.example.com muestra el problema: el usuario ve el host de confianza y no advierte la redirección.
La hoja de referencia de OWASP sobre redirecciones no validadas ofrece la solución. Lo mejor es asociar en el servidor un ID corto a un destino, en lugar de aceptar una URL. Si no tiene más remedio que aceptarla, analícela con un parser mantenido y compare esquema, host canónico y puerto efectivo con una lista de permitidos explícita. Rechace el userinfo y no compare nunca por prefijos o sufijos en bruto.
PKCE en todos los clientes, obligatorio en los públicos
Los clientes públicos deben usar PKCE. En los clientes confidenciales se recomienda, porque además frena la inyección de códigos y, como efecto secundario, el CSRF. Los clientes OpenID Connect confidenciales pueden usar nonce y el claim correspondiente del ID Token en su lugar, pero solo con las precauciones de la sección 4.5.3.2. El consejo vale también para las aplicaciones web, aunque PKCE se diseñó para apps nativas.
- El valor del challenge debe cambiar en cada transacción. Un valor constante anula el propósito.
- Use
S256. El RFC 9700 lo describe como el único método que, hoy por hoy, no expone el verifier en la petición de autorización; de lo contrario, un atacante que pueda leer esa petición puede romper PKCE. El RFC 7636 añade queplainsolo mitiga a un atacante que ve únicamente las respuestas. - El verifier es una cadena aleatoria de entre 43 y 128 caracteres del conjunto de caracteres no reservados. El RFC 7636 recomienda 32 octetos aleatorios codificados en base64url, que dan 43 caracteres.
El servidor debe bloquear también el downgrade: una petición de token con code_verifier solo se acepta si la petición de autorización incluía un code_challenge. Publicar code_challenge_methods_supported en los metadatos del servidor permite a los clientes detectar si hay soporte de PKCE.
Implicit grant, CSRF y mix-up
Los clientes no deberían usar response_type=token ni ningún tipo de respuesta que devuelva access tokens en la respuesta de autorización. El RFC 9700 los considera vulnerables a la filtración y al replay de tokens, y señala que no existe ningún método estandarizado para aplicar sender-constraining a un token emitido de ese modo. Use code, o un tipo que emita los tokens en el token endpoint, como code id_token.
Contra el CSRF en el endpoint de redirección hace falta PKCE (si el cliente ha confirmado que el servidor lo admite), el nonce en los flujos de OpenID Connect, o un valor state de un solo uso vinculado al user agent. Si su cliente habla con más de un servidor de autorización, use el parámetro iss del RFC 9207 o un valor iss equivalente, como el claim del ID Token.
Tokens: sender-constraining siempre que sea posible
Los servidores de autorización y de recursos deberían aplicar sender-constraining a los access tokens con mutual TLS (RFC 8705) o DPoP (RFC 9449), de modo que un token robado sea inútil sin el secreto correspondiente. Los refresh tokens de los clientes públicos deben ser sender-constrained o rotarse, tal como describe la sección 4.14 del RFC.
Por dónde empezar
- Liste todos los redirect URI registrados de cada cliente. Elimine comodines, rutas parciales y hosts que ya no controle. Después añada una ruta o cambie un subdominio en una petición de autorización real; el servidor debería rechazarla.
- Busque parámetros de redirección como
url,nextoreturnUrlen el cliente y en el servidor de autorización. Sustituya las URL de formato libre por una asociación de ID en el servidor o por una lista de permitidos basada en URL analizadas. - Busque
response_type=tokenen el código de los clientes y en la configuración del servidor. Migre esos clientes al authorization code flow con PKCE usandoS256. - Pruebe PKCE directamente. Envíe una petición de autorización sin
code_challengey canjee después el código con uncode_verifier. A continuación, canjee un código protegido con PKCE sin el verifier. Ambas pruebas deberían fallar en el token endpoint; si la respuesta contiene un token, el servidor no supera la prueba. - Pruebe
state: reutilice un valor ya usado y envíe después uno de otra sesión del navegador; el cliente debe rechazar ambos. Si su proveedor devuelveiss, manipúlelo y confirme que el cliente rechaza la respuesta. Inspeccione los claims del ID Token con nuestra herramienta JWT y, si desea una revisión externa, un test de intrusión puede cubrir estos flujos. - En los clientes públicos, elija entre sender-constraining y rotación. Si opta por la rotación, presente dos veces un refresh token ya usado en una prueba y observe qué hace el servidor. Localice ese intento en los logs del servidor de autorización, ya que los nombres de los eventos varían según el producto, y configure una alerta.
