OAuth 2.0 y OIDC: riesgos de la autenticación moderna y buenas prácticas
OAuth impulsa el "Iniciar sesión con Google" en toda la web, y con frecuencia está mal configurado. Aprende cómo funciona, sus vulnerabilidades comunes y una implementación segura.
Tabla de contenidos
- OAuth 2.0 en términos sencillos
- El flujo de código de autorización
- PKCE: proteger a los clientes públicos
- OpenID Connect (OIDC): añadir autenticación
- Vulnerabilidades y ataques comunes
- Redirección abierta a través de redirect_uri
- CSRF del parámetro state
- Fuga de tokens a través del Referer
- Ataques de confusión de JWT
- Validación insuficiente del alcance
- Lista de verificación para una implementación segura
OAuth 2.0 en términos sencillos
OAuth 2.0 es un marco de autorización, no un protocolo de autenticación. Esta distinción importa enormemente: OAuth responde a la pregunta "¿a qué puede acceder esta aplicación?", no a "¿quién es este usuario?". Su caso de uso estrella es el acceso delegado: permitir que una aplicación de terceros lea tus archivos de Google Drive sin ver jamás tu contraseña de Google.
Los actores principales en OAuth:
- Propietario del recurso: el usuario que posee los datos
- Cliente: la aplicación de terceros que solicita el acceso
- Servidor de autorización: emite tokens después de que el usuario da su consentimiento (por ejemplo, el servidor de autenticación de Google)
- Servidor de recursos: la API que contiene los datos del usuario (por ejemplo, la API de Google Drive)
El servidor de autorización emite un token de acceso que el cliente usa para llamar al servidor de recursos en nombre del usuario. Los tokens suelen ser JWT de corta duración o cadenas opacas.
El flujo de código de autorización
Para aplicaciones web del lado del servidor, el flujo de código de autorización es el enfoque recomendado:
- El cliente redirige al usuario al servidor de autorización con
response_type=code,client_id,redirect_uri,scopey un parámetrostatealeatorio - El usuario se autentica y otorga su consentimiento
- El servidor de autorización redirige de vuelta al
redirect_uridel cliente con uncodede corta duración y el valor destate - El cliente verifica que el
statecoincide con el que envió (protección CSRF) y luego intercambia elcodepor tokens mediante una solicitud POST por canal trasero usando suclient_secret - El servidor de autorización devuelve
access_tokeny, opcionalmente,refresh_tokeneid_token
El intercambio por canal trasero (paso 4) es lo que hace seguro este flujo: el token de acceso nunca queda expuesto en la URL o el historial del navegador.
PKCE: proteger a los clientes públicos
Las aplicaciones móviles y las SPA (aplicaciones de una sola página) no pueden mantener a salvo un client_secret, ya que sería visible en el binario de la aplicación o en el código fuente de JavaScript. PKCE (Proof Key for Code Exchange, pronunciado "pixie") resuelve esto:
- El cliente genera un
code_verifieraleatorio y derivacode_challenge = BASE64URL(SHA256(code_verifier)) - El
code_challengese envía en la solicitud de autorización - El
code_verifierse envía en el intercambio de tokens - El servidor de autorización verifica la relación: solo el cliente original puede completar el intercambio
PKCE debería usarse en todos los clientes OAuth en 2024, incluidos los clientes confidenciales. Proporciona protección incluso si el código de autorización es interceptado.
OpenID Connect (OIDC): añadir autenticación
OIDC es una fina capa de identidad construida sobre OAuth 2.0. Añade el token de identidad (ID Token): un JWT firmado por el servidor de autorización que contiene declaraciones sobre el usuario autenticado (sub, email, name, etc.). OIDC es lo que hace que "Iniciar sesión con Google" realmente autentique a los usuarios en lugar de limitarse a autorizar el acceso a sus datos.
Conceptos clave de OIDC:
- ID Token: un JWT para que el cliente verifique quién es el usuario; nunca lo envíes a las API
- Token de acceso: para llamar a las API del servidor de recursos; el cliente debe tratarlo como opaco
- Endpoint UserInfo: un endpoint de API que devuelve las declaraciones del usuario cuando se llama con el token de acceso
nonce: un valor aleatorio incluido en la solicitud de autorización y verificado en el ID token para evitar ataques de repetición
Vulnerabilidades y ataques comunes
Redirección abierta a través de redirect_uri
Si el servidor de autorización no valida estrictamente el redirect_uri, un atacante puede modificarlo para que apunte a su propio servidor, robando el código de autorización cuando la víctima concede la autorización. Defensa: registra URI de redirección exactas; no permitas comodines ni coincidencia de patrones.
CSRF del parámetro state
Omitir o no validar el parámetro state permite a un atacante engañar al navegador de una víctima para que complete un flujo de autorización iniciado por el atacante, lo que puede vincular la cuenta de la víctima a la identidad del atacante. Defensa: genera siempre un state criptográficamente aleatorio y valídalo al recibir la respuesta.
Fuga de tokens a través del Referer
El flujo implícito (ahora obsoleto) devolvía los tokens directamente en el fragmento de la URL, que podía filtrarse a través de encabezados Referer o del historial del navegador. Defensa: nunca uses el flujo implícito; usa en su lugar código de autorización + PKCE.
Ataques de confusión de JWT
Los JWT son autocontenidos y deben validarse con cuidado. Errores comunes:
- Aceptar
alg: none: deshabilita la negociación de algoritmos, especifica siempre explícitamente los algoritmos permitidos - Usar la clave pública RS256 como secreto HS256: fija el algoritmo esperado en el lado del servidor
- No validar las declaraciones
iss,audyexp
Validación insuficiente del alcance
Los tokens de acceso deben limitarse a los permisos mínimos necesarios. Las API deben validar que el token presentado tiene el alcance requerido para cada operación; no asumas que un token válido equivale a acceso total.
Lista de verificación para una implementación segura
- Usa código de autorización + PKCE en todos los clientes
- Registra URI de redirección exactas y solo por HTTPS
- Valida siempre el parámetro
state(o usa PKCE, que ofrece protección equivalente) - Valida todas las declaraciones del JWT:
iss,aud,exp,nbf, algoritmo - Almacena los tokens en memoria (no en localStorage) en las SPA; usa cookies
HttpOnlypara los tokens de actualización en aplicaciones web - Implementa la rotación de tokens: emite un nuevo token de actualización con cada uso e invalida el anterior
- Usa tokens de acceso de corta duración (5–15 minutos) con tokens de actualización para la continuidad de la sesión
- Audita periódicamente todas las aplicaciones OAuth que tienen acceso a tu servidor de autorización
- Nunca registres ni expongas tokens de acceso en URL, registros o mensajes de error