Ataques de Downgrade en PKCE: Por qué OAuth 2.1 ya no es opcional 🔑📉

En el panorama en rápida evolución de la ciberseguridad, los protocolos que alguna vez consideramos “suficientemente seguros” están siendo desmantelados por vectores de ataque modernos. Durante más de una década, OAuth 2.0 (RFC 6749) sirvió como la base de la autorización web y móvil. Sin embargo, a partir de enero de 2026, la industria ha llegado a un punto de inflexión. Con la estabilización de OAuth 2.1 y la publicación de RFC 9700 (Mejor Práctica de Seguridad Actual), el flujo de autorización “estándar” sin PKCE (Proof Key for Code Exchange) ya no es solo “obsoleto”—es una responsabilidad.
Este artículo explora la mecánica técnica de los Ataques de Downgrade en PKCE, por qué confiar en valores estáticos de client_secret en clientes públicos es “teatro de seguridad”, y por qué migrar a OAuth 2.1 es la única forma de proteger a tus usuarios de técnicas sofisticadas de robo de tokens.
1. La muerte de OAuth 2.0: ¿Qué es OAuth 2.1?
OAuth 2.1 no es un protocolo completamente nuevo. En cambio, es una consolidación de varias extensiones de seguridad y “Mejores Prácticas Actuales” (BCPs) que se han publicado desde 2012. Efectivamente, “limpia” OAuth 2.0 eliminando funciones inseguras y haciendo obligatorias las medidas de seguridad que antes eran opcionales.
Cambios clave en OAuth 2.1:
PKCE es obligatorio: Cada flujo de Código de Autorización—ya sea para un servidor backend (cliente confidencial) o una app móvil/SPA (cliente público)—debe usar PKCE.
Eliminación del Grant Implícito: El flujo “Implícito”, que devuelve tokens directamente en el fragmento de URL, está oficialmente muerto.
Eliminación de ROPC: El grant “Resource Owner Password Credentials” (donde la app maneja la contraseña del usuario) ya no existe.
Coincidencia exacta de URI de redirección: Los comodines en las URIs de redirección ya no están permitidos; el servidor debe hacer una coincidencia bit a bit.
La transición a OAuth 2.1 está impulsada por una realización principal: los códigos de autorización son vulnerables a la interceptación en el “canal frontal”.
2. Anatomía de un ataque de interceptación
Para entender por qué PKCE es necesario, primero debemos ver cómo los atacantes roban los códigos de autorización en aplicaciones móviles y de página única.
Apps móviles on esquemas URL personalizadas
En entornos móviles, las aplicaciones a menudo se comunican mediante esquemas URL personalizadas (por ejemplo, my-app://callback). Cuando un usuario termina de autenticarse en un navegador móvil, el Servidor de Autorización (AS) redirige al usuario de vuelta a la app usando este esquema.
La vulnerabilidad: En muchos sistemas operativos, varias apps pueden registrarse para el mismo esquema URL personalizado. Si una app maliciosa está instalada en el dispositivo y se registra para my-app://, puede “ganar” la carrera y recibir la redirección en lugar de la app legítima. La app maliciosa ahora tiene el Código de Autorización.
SPAs y historial del navegador
En aplicaciones de página única (SPAs), el código de autorización se entrega mediante un parámetro en la URL en el navegador. Este código puede filtrarse a través de:
- Historial del navegador: Si un atacante obtiene acceso al dispositivo, puede simplemente revisar el historial.
- Encabezados Referer: Si la SPA realiza una solicitud a un script de terceros (como un rastreador de anuncios) inmediatamente después de la redirección, la URL que contiene el código puede enviarse en el encabezado Referer.
- Registros: Los servidores proxy o extensiones del navegador pueden registrar la URL completa.
3. ¿Qué es PKCE? (La protección moderna)
PKCE (Proof Key for Code Exchange), definido en RFC 7636, fue originalmente diseñado para resolver el problema del esquema URL personalizado en apps móviles. Introduce tres componentes nuevos en el flujo:
- Code Verifier: Una cadena aleatoria criptográficamente generada por el cliente para cada solicitud.
- Code Challenge: Una versión hash del verifier (usualmente usando SHA-256).
- Code Challenge Method: El algoritmo usado (por ejemplo, S256).
Cómo funciona PKCE:
Inicio: El cliente genera un
code_verifier, lo hashea para crear uncode_challenge, y envía el desafío al Servidor de Autorización.El Código: El AS emite un código de autorización pero lo “fija” al desafío.
El Intercambio: Cuando el cliente intercambia el código por un token, debe enviar el
code_verifieroriginal, sin hash. El AS lo hashea y verifica si coincide con el desafío proporcionado en el paso 1.
Por qué esto previene el robo: Incluso si un atacante roba el Código de Autorización, no tiene el code_verifier (que nunca salió del cliente). Sin el verifier, el código no sirve.
4. El ataque de Downgrade en PKCE: La trampa oculta
Un ataque de Downgrade en PKCE ocurre cuando un Servidor de Autorización soporta PKCE pero no lo aplica a todos los clientes.
Escenario del ataque:
La configuración: Un atacante se sitúa en medio (por ejemplo, mediante una extensión de navegador comprometida o una app maliciosa).
La modificación: Cuando el cliente legítimo inicia un flujo OAuth, envía una solicitud que contiene un
code_challenge.El Downgrade: El atacante intercepta la solicitud inicial y elimina los parámetros
code_challengeycode_challenge_methodantes de que llegue al Servidor de Autorización.El error del servidor: Si el AS está configurado para ser “compatible hacia atrás” con OAuth 2.0, ve una solicitud sin parámetros PKCE y asume que el cliente es un “cliente antiguo”. Continúa con un flujo estándar, sin PKCE.
El robo: El AS emite un código. El atacante intercepta este código mediante un esquema URL personalizado o historial del navegador.
La recompensa: Como el AS piensa que esto es un flujo sin PKCE, no requiere un
code_verifierdurante el intercambio del token. El atacante intercambia el código robado por un token de acceso válido.
La solución en OAuth 2.1: Haciendo obligatorio PKCE, OAuth 2.1 exige que el Servidor de Autorización rechace cualquier solicitud sin un code_challenge. Esto elimina completamente la vía de “downgrade”.
5. Por qué client_secret es “teatro de seguridad”
Muchos desarrolladores creen que usar un client_secret los protege. Aunque esto es cierto para Clientes Confidenciales (servidores donde el secreto está oculto en variables de entorno), es completamente falso para Clientes Públicos (móviles y SPAs).
La falacia del SPA
Si colocas un client_secret en un SPA basado en JavaScript, es público. Cualquiera puede presionar F12 en Chrome, ir a la pestaña “Fuentes” y encontrarlo.
La falacia móvil
Las apps móviles a menudo están compiladas, pero son fácilmente descompilables. Herramientas como apktool o Ghidra permiten a los atacantes extraer cadenas estáticas (como secretos) de un binario en segundos.
El problema con secretos estáticos:
Un client_secret es una “Prueba de Identidad”, no una “Prueba de Posesión” para una transacción específica. Si un secreto se filtra una vez, un atacante puede hacerse pasar por ese cliente para siempre. Sin embargo, PKCE usa un secreto dinámico y de un solo uso (el code_verifier) para cada transacción. Por eso OAuth 2.1 prefiere PKCE sobre secretos estáticos para la seguridad en el canal frontal.
6. Robo de tokens en la era moderna: Más allá del código
Confiar en flujos legados te expone a técnicas modernas de robo de tokens que evaden defensas tradicionales:
Inyección de Código de Autorización: Los atacantes pueden inyectar un código robado en su propia sesión. PKCE (cuando se usa con el parámetro iss o nonce) previene esto asegurando que el código pertenezca a la sesión específica del navegador que inició el flujo.
Robo de información: Malware dirigido a la memoria del navegador puede extraer tokens. OAuth 2.1 fomenta el uso de Tokens Constrainidos por el remitente (a través de DPoP - Demonstrating Proof-of-Possession), que aseguran que un token solo pueda usarse por el dispositivo que lo solicitó.
Abuso de Refresh Tokens: Antes, los refresh tokens para SPAs se consideraban peligrosos. OAuth 2.1 los permite pero requiere Rotación de Refresh Tokens. Cada vez que se usa un refresh token, se emite uno nuevo y el anterior se invalida. Si un atacante usa un refresh token filtrado, el AS detecta la “repetición” y termina toda la sesión.
7. Estrategia SEO: Implementando OAuth 2.1 hoy
Para desarrolladores y arquitectos de seguridad que buscan optimizar su postura de seguridad (y su ranking en búsquedas por “Implementación Segura de OAuth”), aquí la lista de verificación para 2026:
Actualiza tu biblioteca: Asegúrate de que tu SDK (por ejemplo, AppAuth, MSAL, OIDC-client-ts) esté configurado por defecto para PKCE.
Forzar S256: Nunca uses el método “plain” en PKCE. Siempre usa S256 (SHA-256).
Audita tu AS: Configura tu Servidor de Autorización (Auth0, Okta, Keycloak, etc.) para requerir PKCE para todos los clientes. Desactiva cualquier modo “Legado” o “Compatibilidad”.
Quita secretos del frontend: Si tienes un client_secret en tu código SPA o móvil, elimínalo. Solo da una falsa sensación de seguridad.
Coincidencia exacta: Revisa tus URIs de redirección. Elimina las que usan comodines (por ejemplo, https://*.myapp.com).
Conclusión: La nueva línea base
La finalización de OAuth 2.1 marca el fin de una era donde la “seguridad suficiente” era aceptable. Los ataques de Downgrade en PKCE representan una amenaza real para cualquier aplicación que aún se aferre a implementaciones OAuth 2.0 de 2012.
Al exigir PKCE, eliminar el Grant Implícito y aplicar coincidencias estrictas en las URIs de redirección, OAuth 2.1 ofrece una defensa sólida contra ataques de interceptación e inyección. Es hora de dejar de tratar la seguridad como una “extensión opcional” y comenzar a considerarla como la base de tu aplicación.
Related InstaTunnel pages
Continue from this article into the most relevant product guides and workflows.
Related Topics
Keep building with InstaTunnel
Read the docs for implementation details or compare plans before you ship.