
Hola a todos! Soy Nikita Stupin, especialista en seguridad de la información, Mail.Ru Mail. No hace mucho tiempo, realicé una investigación de vulnerabilidad en dispositivos móviles OAuth 2.0. Para crear un esquema móvil seguro de OAuth 2.0, no es suficiente implementar el estándar en su forma pura y verificar redirect_uri. Es necesario tener en cuenta los detalles de las aplicaciones móviles y aplicar mecanismos de protección adicionales.
En este artículo, quiero compartir con ustedes conocimientos sobre ataques en dispositivos móviles OAuth 2.0, sobre métodos de protección y la implementación segura de este protocolo. Todos los componentes de protección necesarios, que analizaré a continuación, se implementan en el último SDK para los clientes móviles Mail.Ru Mail.
La naturaleza y la función de OAuth 2.0
OAuth 2.0 es un protocolo de autorización que describe cómo es seguro para un servicio de cliente acceder a los recursos del usuario en un proveedor de servicios. Al mismo tiempo, OAuth 2.0 evita que el usuario tenga que ingresar una contraseña fuera del proveedor de servicios: todo el proceso se reduce a hacer clic en el botón "Acepto otorgar acceso a ...".
Un proveedor en términos de OAuth 2.0 es un servicio que posee los datos del usuario y, con el permiso del usuario, proporciona servicios de terceros (clientes) con acceso seguro a estos datos. Un cliente es una aplicación que desea recibir datos de usuario de un proveedor.
Algún tiempo después del lanzamiento del protocolo OAuth 2.0, los desarrolladores comunes lo adaptaron para la autenticación, aunque originalmente no estaba destinado a esto. La autenticación cambia el vector de ataque de los datos del usuario que se almacenan en el proveedor de servicios a las cuentas de usuario del servicio del usuario.
No se limitó solo a la autenticación. En la era de las aplicaciones móviles y la exaltación de la conversión, ingresar a la aplicación con un solo botón se ha vuelto muy tentador. Los desarrolladores pusieron OAuth 2.0 en rieles móviles. Naturalmente, pocas personas pensaban en la seguridad y las características específicas de las aplicaciones móviles: una y otra vez, y en producción. Sin embargo, OAuth 2.0 generalmente no funciona bien fuera de las aplicaciones web: se observan los mismos problemas tanto en aplicaciones móviles como de escritorio.
Veamos cómo hacer un OAuth 2.0 móvil seguro.
Como funciona
Recuerde que en los dispositivos móviles, el cliente puede no ser un navegador, sino una aplicación móvil sin backend. Por lo tanto, nos enfrentamos a dos problemas de seguridad principales para OAuth 2.0 móvil:
- El cliente no es de confianza.
- El comportamiento de una redirección desde un navegador a una aplicación móvil depende de la configuración y las aplicaciones que el usuario haya instalado.
La aplicación móvil es un cliente público.
Para comprender la raíz del primer problema, veamos cómo funciona OAuth 2.0 en caso de interacción de servidor a servidor, y luego compárelo con OAuth 2.0 en caso de interacción de cliente a servidor.
En ambos casos, todo comienza con el hecho de que el cliente del servicio se registra con el proveedor del servicio y recibe
client_id
y, en algunos casos,
client_secret
. El valor
client_id
es público y es necesario para identificar el servicio del cliente, a diferencia de
client_secret
, cuyo valor es privado. El proceso de registro se describe con más detalle en
RFC 7591 .
El siguiente diagrama muestra el funcionamiento de OAuth 2.0 en la comunicación de servidor a servidor.
Imagen tomada de https://tools.ietf.org/html/rfc6749#section-1.2Hay 3 etapas principales del protocolo OAuth 2.0:
- [Pasos de CA] Obtenga el código de autorización (en adelante, simplemente
code
). - [Pasos DE]
code
intercambio para access_token
. - Acceda al recurso utilizando
access_token
.
Examinemos el recibo del código con más detalle:
- [Paso A] El cliente del servicio redirige al usuario al proveedor del servicio.
- [Paso B] El proveedor de servicios solicita permiso al usuario para proporcionar datos al servicio del cliente (flecha B hacia arriba). El usuario proporciona acceso a los datos (flecha B a la derecha).
- [Paso C] El proveedor de servicios devuelve el
code
al navegador del usuario, que redirige el code
servicio del cliente.
Veamos
access_token
obtener
access_token
más detalle:
- [Paso D] El servidor del cliente envía una solicitud de
access_token
. La solicitud incluye: code
, client_secret
y redirect_uri
. - [Paso E] En el caso de
code
válido, client_secret
y redirect_uri
, client_secret
proporciona client_secret
.
La solicitud de
access_token
a
access_token
se realiza de acuerdo con el esquema de servidor a servidor, por lo tanto, en general, para robar
client_secret
atacante debe piratear el servidor servidor-cliente o el servidor del proveedor de servicios.
Ahora veamos cómo se ve el esquema OAuth 2.0 en un dispositivo móvil sin backend (interacción de cliente a servidor).
Imagen tomada de https://tools.ietf.org/html/rfc8252#section-4.1El esquema general se divide en los mismos 3 pasos principales:
- [pasos 1-4 en la imagen] Obtenga el
code
. - [pasos 5-6 en la imagen]
code
intercambio para access_token
. - Acceda al recurso utilizando
access_token
.
Sin embargo, en este caso, la aplicación móvil también actúa como un servidor, lo que significa que
client_secret
estará
client_secret
dentro de la aplicación. Esto lleva al hecho de que en dispositivos móviles es imposible mantener el secreto
lient_secret
de un atacante.
client_secret
dos formas de
client_secret
a la aplicación: filtrar el tráfico de la aplicación al servidor o aplicar ingeniería inversa a la aplicación. Ambos métodos son fáciles de implementar, por lo que
client_secret
inútil en dispositivos móviles.
Con respecto al esquema de cliente a servidor, es posible que tenga una pregunta: "¿por qué no obtener
access_token
inmediatamente?". Parecería, ¿por qué necesitamos un paso adicional? Además, hay un esquema de
concesión implícita en el que el cliente recibe inmediatamente un
access_token
. Aunque puede usarse en algunos casos, veremos a continuación que la
Subvención implícita no
es adecuada para OAuth 2.0 móvil seguro.
Redireccionar en dispositivos móviles
En general, para una redirección desde un navegador a una aplicación en dispositivos móviles, se utilizan el
esquema URI personalizado y los mecanismos
AppLink . Ninguno de estos mecanismos en su forma pura es tan confiable como una redirección de navegador.
El esquema URI personalizado (o enlace profundo) se utiliza de la siguiente manera: el desarrollador define el esquema de la aplicación antes del ensamblaje. El esquema puede ser arbitrario, mientras que en el mismo dispositivo se pueden instalar varias aplicaciones con el mismo esquema. Todo es bastante simple cuando cada aplicación en el dispositivo corresponde a una aplicación. Pero, ¿qué pasa si dos aplicaciones registraron el mismo circuito en el mismo dispositivo? ¿Cómo puede determinar el sistema operativo cuál de las dos aplicaciones se abrirá al acceder al esquema URI personalizado? Android mostrará una ventana con la elección de la aplicación en la que desea abrir un enlace. En iOS, el
comportamiento no está definido , lo que significa que cualquiera de las dos aplicaciones se puede abrir. En ambos casos, un atacante
puede interceptar código o access_token .
AppLink, en contraste con el esquema URI personalizado, está garantizado para abrir la aplicación correcta, pero este mecanismo tiene varias desventajas:
- Cada cliente de servicio debe pasar independientemente el procedimiento de verificación .
- Los usuarios de Android pueden desactivar AppLink para una aplicación específica en la configuración.
- Android por debajo de 6.0 e iOS por debajo de 9.0 no son compatibles con AppLink.
Las desventajas anteriores de AppLink, en primer lugar, aumentan el umbral de entrada para posibles servicios de cliente y, en segundo lugar, pueden llevar al hecho de que, en determinadas circunstancias, el usuario no trabajará con OAuth 2.0. Esto hace que AppLink no sea adecuado para reemplazar los redireccionamientos del navegador en el protocolo OAuth 2.0.
¿A qué atacar?
Los problemas de OAuth 2.0 móvil también dieron lugar a ataques específicos. Veamos qué son y cómo funcionan.
Ataque de intercepción del código de autorización
Datos iniciales: una aplicación legítima (cliente OAuth 2.0) y una aplicación maliciosa que registró el mismo esquema que el legítimo se instalan en el dispositivo del usuario. La siguiente figura muestra el esquema de ataque.
Imagen tomada de https://tools.ietf.org/html/rfc7636#section-1Aquí está el problema: en el paso 4, el navegador devuelve el
code
a la aplicación a través del Esquema de URI personalizado, por lo que el
code
puede ser interceptado por el malware (porque registró el mismo esquema que la aplicación legítima). Después de eso, el malware cambia el
code
a
access_token
y obtiene acceso a los datos del usuario.
¿Cómo protegerte? En algunos casos, se pueden utilizar mecanismos de comunicación entre procesos; hablaremos de ellos a continuación. En el caso general, debe aplicar un esquema llamado
Clave de prueba para el intercambio de código . Su esencia se refleja en el siguiente diagrama.
Imagen tomada de https://tools.ietf.org/html/rfc7636#section-1.1En las solicitudes del cliente, hay varios parámetros adicionales:
code_verifier
,
code_challenge
(en el
t(code_verifier)
) y
code_challenge_method
(en el diagrama
t_m
).
Code_verifier
es un número aleatorio
de al menos 256 bits de longitud que se
usa solo una vez . Es decir, para
cada solicitud de
code
cliente debe generar un nuevo
code_verifier
.
Code_challenge_method
es el nombre de una función de conversión, con mayor frecuencia SHA-256.
Code_challenge
es un
code_verifier
al que se ha
code_challenge_method
y codificado la conversión
code_challenge_method
en la URL Safe Base64.
La conversión de
code_verifier
a
code_challenge
necesaria para proteger contra los vectores de ataque basados en la intercepción de
code_verifier
(por ejemplo, desde los registros del sistema del dispositivo) cuando se solicita el
code
.
Si el dispositivo del usuario
no admite SHA-256,
se permite una
degradación hasta que falte la conversión code_verifier . En todos los demás casos, debe usar SHA-256.
El esquema funciona de la siguiente manera:
- El cliente genera un
code_verifier
y lo recuerda. - El cliente selecciona
code_challenge_method
y obtiene code_challenge
de code_verifier
. - [Paso A] El cliente solicita el
code
, con code_challenge
y code_challenge_method
agregado a la solicitud. - [Paso B] El proveedor recuerda el
code_challenge
y code_challenge_method
en el servidor y devuelve el code
cliente. - [Paso C] El cliente solicita
access_token
, y se agrega access_token
a la code_verifier
. - El proveedor recibe el
code_challenge
del code_challenge
entrante, y luego lo code_challenge
el code_challenge
, que recordaba. - [Paso D] Si los valores coinciden, el proveedor emite un
access_token
cliente.
Veamos por qué
code_challenge
permite protegerte de un ataque de intercepción de código. Para hacer esto, pasaremos por las etapas de obtener
access_token
.
- Primero, una aplicación legítima solicita el
code
( code_challenge
y code_challenge_method
envían junto con la solicitud ). - El malware intercepta el
code
(pero no code_challenge
, porque no hay code_challenge
en la respuesta ). - El malware solicita
access_token
(con code
válido, pero sin code_verifier
válido). - El servidor nota la
code_challenge
coincidencia code_challenge
y arroja un error.
Tenga en cuenta que el atacante no tiene la capacidad de adivinar el
code_verifier
(al azar 256 bits!) O encontrarlo en algún lugar de los registros (
code_verifier
se transmite una vez).
Si todo esto se reduce a una frase,
code_challenge
permite al proveedor de servicios responder la pregunta: "
access_token
solicita
access_token
por la misma aplicación cliente que solicitó el
code
o por otra?"
OAuth 2.0 CSRF
En dispositivos móviles, OAuth 2.0 a menudo se usa como mecanismo de autenticación. Como recordamos, la autenticación a través de OAuth 2.0 difiere de la autorización en que las vulnerabilidades de OAuth 2.0 afectan los datos del usuario del lado del cliente del servicio y no del proveedor del servicio. Como resultado, el ataque CSRF en OAuth 2.0 le permite robar la cuenta de otra persona.
Considere un ataque CSRF contra OAuth 2.0 utilizando el ejemplo de la aplicación cliente de taxi y el proveedor provider.com. Primero, un atacante inicia sesión en attacker@provider.com en su dispositivo y recibe un
code
para el taxi. Después de eso, el atacante interrumpe el proceso OAuth 2.0 y genera un enlace:
com.taxi.app://oauth?
code=b57b236c9bcd2a61fcd627b69ae2d7a6eb5bc13f2dc25311348ee08df43bc0c4
Luego, el atacante envía un enlace a la víctima, por ejemplo, bajo la apariencia de una carta o SMS de la administración del taxi. La víctima
access_token
enlace, se abre una aplicación de taxi en su teléfono, que recibe
access_token
, y como resultado, la víctima termina en la cuenta de taxi del
atacante . Sin darse cuenta de la captura, la víctima usa esta cuenta: hace viajes, ingresa sus datos, etc.
Ahora, un atacante puede iniciar sesión en la cuenta de taxi de la víctima en cualquier momento porque está vinculado a
attacker@provider.com
. El ataque CSRF al iniciar sesión le permitió robar una cuenta.
Los ataques CSRF generalmente están protegidos con un token CSRF (también llamado
state
), y OAuth 2.0 no es una excepción. Cómo usar el token CSRF:
- La aplicación cliente genera y almacena el token CSRF en el dispositivo móvil del usuario.
- La aplicación cliente incluye el token CSRF en la solicitud de
code
. - El servidor devuelve el mismo token CSRF en la respuesta junto con el código.
- La aplicación cliente compara el token CSRF entrante y almacenado. Si los valores coinciden, entonces el proceso continúa.
Requisitos del token CSRF:
nonce de al menos 256 bits de largo, obtenido de una buena fuente de secuencias pseudoaleatorias.
En resumen, el token CSRF permite que la aplicación cliente responda la pregunta: "¿Estaba empezando a obtener
access_token
, o alguien está tratando de engañarme?"
Malware que finge ser un cliente legítimo
Algunos programas maliciosos pueden imitar aplicaciones legítimas y generar una pantalla de consentimiento en su nombre (la pantalla de consentimiento es una pantalla en la que el usuario ve: "Acepto otorgar acceso a ..."). El usuario desatento puede hacer clic en "permitir" y, como resultado, el malware obtiene acceso a los datos del usuario.
Android e iOS proporcionan mecanismos para la verificación mutua de aplicaciones. La aplicación del proveedor puede verificar la legitimidad de la aplicación del cliente y viceversa.
Desafortunadamente, si el mecanismo OAuth 2.0 usa una transmisión a través de un navegador, entonces no puede defenderse contra este ataque.
Otros ataques
Examinamos los ataques que son exclusivos de OAuth 2.0 móvil. Sin embargo, no te olvides de los ataques en OAuth 2.0 regular:
redirect_uri
, intercepción de tráfico a través de una conexión insegura, etc. Puedes leer más sobre ellos
aquí .
Que hacer
Aprendimos cómo funciona el protocolo OAuth 2.0 y descubrimos qué vulnerabilidades existen en las implementaciones de este protocolo en dispositivos móviles. Ahora, creemos un esquema seguro de OAuth 2.0 móvil a partir de piezas individuales.
Bueno, malo OAuth 2.0
Comencemos con cómo elevar correctamente la pantalla de consentimiento. En dispositivos móviles, hay dos formas de abrir una página web desde una aplicación nativa (ejemplos de aplicaciones nativas: Mail.Ru Mail, VK, Facebook).

El primer método se llama la pestaña personalizada del navegador (en la imagen de la izquierda).
Nota : La pestaña personalizada del navegador en Android se denomina pestaña personalizada de Chrome y en iOS SafariViewController. De hecho, esta es una pestaña normal del navegador, que se muestra directamente en la aplicación, es decir No hay cambio visual entre aplicaciones.
El segundo método se llama "raise WebView" (en la imagen de la derecha), en relación con OAuth 2.0 móvil, considero que es malo.
WebView es un navegador independiente para una aplicación nativa.
Un "
navegador independiente" significa que WebView no permite el acceso a cookies, almacenamiento, caché, historial y otros datos de los navegadores Safari y Chrome. Lo contrario también es cierto: Safari y Chrome no pueden acceder a los datos de WebView.
"
Navegador para una aplicación nativa " significa que la aplicación nativa que generó WebView tiene acceso
completo a cookies, almacenamiento, caché, historial y otros datos de WebView.
Ahora imagine: el usuario presiona el botón "iniciar sesión usando ..." y el WebView de la aplicación maliciosa le pide su nombre de usuario y contraseña al proveedor de servicios.
Fracaso a la vez en todos los frentes:
- El usuario ingresa el nombre de usuario y la contraseña de la cuenta del proveedor de servicios en la aplicación, que puede robar fácilmente estos datos.
- OAuth 2.0 se desarrolló originalmente para no ingresar un nombre de usuario y contraseña de un proveedor de servicios.
- El usuario se acostumbra a ingresar el nombre de usuario y la contraseña en cualquier lugar, la probabilidad de phishing aumenta.
Dado que todos los argumentos están en contra de WebView, la conclusión se sugiere a sí misma: abrir la pestaña personalizada del navegador para la pantalla de consentimiento.
Si alguno de ustedes tiene argumentos a favor de WebView en lugar de la pestaña personalizada del navegador, escríbalo en los comentarios, lo agradeceré.
Esquema seguro de Mobile OAuth 2.0
Utilizaremos el esquema de concesión de código de autorización porque nos permite agregar un
code_challenge
y protegernos de un ataque de intercepción de código.
Imagen tomada de https://tools.ietf.org/html/rfc8252#section-4.1La solicitud de código (pasos 1-2) se verá así:
https://o2.mail.ru/code?
redirect_uri=com.mail.cloud.app%3A%2F%2Foauth&
anti_csrf=927489cb2fcdb32e302713f6a720397868b71dd2128c734181983f367d622c24& code_challenge=ZjYxNzQ4ZjI4YjdkNWRmZjg4MWQ1N2FkZjQzNGVkODE1YTRhNjViNjJjMGY5MGJjNzdiOGEzMDU2ZjE3NGFiYw%3D%3D&
code_challenge_method=S256&
scope=email%2Cid&
response_type=code&
client_id=984a644ec3b56d32b0404777e1eb73390c
En el paso 3, el navegador recibe una respuesta redirigida:
com.mail.cloud.app://outh?
code=b57b236c9bcd2a61fcd627b69ae2d7a6eb5bc13f2dc25311348ee08df43bc0c4&
anti_csrf=927489cb2fcdb32e302713f6a720397868b71dd2128c734181983f367d622c24
En el paso 4, el navegador abre el esquema URI personalizado y pasa el
code
y el token CSRF a la aplicación cliente.
Solicitud de
access_token
(paso 5):
https://o2.mail.ru/token?
code_verifier=e61748f28b7d5daf881d571df434ed815a4a65b62c0f90bc77b8a3056f174abc&
code=b57b236c9bcd2a61fcd627b69ae2d7a6eb5bc13f2dc25311348ee08df43bc0c4&
client_id=984a644ec3b56d32b0404777e1eb73390c
El último paso devuelve una respuesta con
access_token
.
En general, el esquema anterior es seguro, pero también hay casos especiales en los que OAuth 2.0 puede hacerse más simple y un poco más seguro.
Android IPC
Android tiene un mecanismo para el intercambio de datos bidireccional entre procesos: IPC (comunicación entre procesos). Se prefiere IPC sobre el esquema URI personalizado por dos razones:
- Una aplicación que abre un canal IPC puede verificar la autenticidad de una aplicación abierta mediante su certificado. Lo contrario también es cierto: una aplicación abierta puede verificar la autenticidad de la aplicación que la abrió.
- Al enviar una solicitud a través de un canal IPC, el remitente puede recibir una respuesta a través del mismo canal. Junto con la verificación mutua (elemento 1), esto significa que ningún proceso de terceros puede interceptar
access_token
.

Por lo tanto, podemos usar la
Subvención implícita y simplificar enormemente el esquema móvil OAuth 2.0. Sin
code_challenge
y tokens CSRF. Además, podremos protegernos del malware que imita a clientes legítimos para robar cuentas de usuario.
SDK del cliente
Además de implementar el esquema seguro de OAuth 2.0 móvil descrito anteriormente, el proveedor debe desarrollar un SDK para sus clientes. Esto facilitará la implementación de OAuth 2.0 en el lado del cliente y al mismo tiempo reducirá la cantidad de errores y vulnerabilidades.
Sacar conclusiones
Para los proveedores de OAuth 2.0, compilé la "Lista de verificación de Secure Mobile OAuth 2.0":
- Una base sólida es vital. En el caso de OAuth 2.0 móvil, la base es el esquema o protocolo que elegimos implementar. Al implementar su propio esquema OAuth 2.0, es fácil cometer un error. Otros ya han llenado los baches y han llegado a conclusiones, no hay nada de malo en aprender de sus errores e inmediatamente hacer una implementación segura. En general, el esquema OAuth 2.0 móvil más seguro es el de la sección ¿Qué hacer?
Access_token
y otros datos confidenciales: en iOS, en Llavero, en Android, en Almacenamiento interno. Estos repositorios están diseñados específicamente para tales fines. Si es necesario, puede usar el proveedor de contenido en Android, pero debe configurarse de manera segura.Code
debe ser de una sola vez, con un tiempo de vida corto.- Para protegerse contra la intercepción de código, use
code_challenge
. - Para protegerse contra un ataque CSRF en el inicio de sesión, use tokens CSRF.
- No use WebView para la pantalla de consentimiento, use la pestaña personalizada del navegador.
Client_secret
inútil si no está almacenado en el backend. No se lo dé a clientes públicos.- Use HTTPS en todas partes , con la prohibición de rebajar a HTTP.
- Siga las recomendaciones de criptografía (selección de cifrado, longitud del token, etc.) de los estándares . Puede copiar los datos y descubrir por qué se hizo de esa manera, pero no puede hacer su criptografía .
- Desde la aplicación del cliente, verifique a quién abre para OAuth 2.0, y desde la aplicación del proveedor, verifique quién lo abre para OAuth 2.0.
- Tenga en cuenta las vulnerabilidades habituales de OAuth 2.0 . Mobile OAuth 2.0 amplía y complementa el normal, por lo que nadie canceló la verificación de
redirect_uri
para ver las coincidencias exactas y otras recomendaciones para OAuth 2.0 regular. - Asegúrese de proporcionar SDK a los clientes. El cliente tendrá menos errores y vulnerabilidades en el código, y será más fácil para él implementar su OAuth 2.0.
Que leer
- [RFC] OAuth 2.0 para aplicaciones nativas https://tools.ietf.org/html/rfc8252
- Google OAuth 2.0 para aplicaciones móviles y de escritorio https://developers.google.com/identity/protocols/OAuth2InstalledApp
- [RFC] Clave de prueba para el intercambio de código por parte de clientes públicos de OAuth https://tools.ietf.org/html/rfc7636
- Condición de carrera OAuth 2.0 https://hackerone.com/reports/55140
- [RFC] Modelo de amenaza OAuth 2.0 y consideraciones de seguridad https://tools.ietf.org/html/rfc6819
- Ataques en OAuth 2.0 normal https://sakurity.com/oauth
- [RFC] Protocolo de registro de cliente dinámico OAuth 2.0 https://tools.ietf.org/html/rfc7591
Agradecimientos
Gracias a todos los que ayudaron a escribir este artículo, especialmente Sergey Belov, Andrey Sumin, Andrey Labunts (
@isciurus ) y Daria Yakovleva.