
La popularidad de las aplicaciones móviles continúa creciendo. También lo hace el protocolo OAuth 2.0 en aplicaciones móviles. No es suficiente implementar el estándar como lo es hacer seguro el protocolo OAuth 2.0 allí. Hay que tener en cuenta los detalles de las aplicaciones móviles y aplicar algunos mecanismos de seguridad adicionales.
En este artículo, quiero compartir los conceptos de los ataques móviles OAuth 2.0 y los mecanismos de seguridad utilizados para prevenir tales problemas. Los conceptos descritos no son nuevos, pero falta información estructurada sobre este tema. El objetivo principal del artículo es llenar este vacío.
OAuth 2.0 naturaleza y propósito
OAuth 2.0 es un protocolo de
autorización que describe una forma para que un servicio de cliente obtenga un acceso seguro a los recursos del usuario en un proveedor de servicios. Gracias a OAuth 2.0, el usuario no necesita ingresar su contraseña fuera del proveedor de servicios: todo el proceso se reduce a hacer clic en el botón "Acepto proporcionar acceso a ...".
Un proveedor es un servicio que posee los datos del usuario y, con el permiso del usuario, proporciona servicios de terceros (clientes) con un acceso seguro a estos datos. Un cliente es una aplicación que quiere obtener los datos del usuario almacenados por el proveedor.
Poco después del lanzamiento del protocolo OAuth 2.0, se adaptó para la
autenticación , aunque no estaba destinado a eso. El uso de OAuth 2.0 para la autenticación desplaza un vector de ataque de los datos almacenados en el proveedor del servicio a las cuentas de usuario del servicio del cliente.
Pero la autenticación fue solo un comienzo. En tiempos de aplicaciones móviles y glorificación de conversión, acceder a una aplicación con solo un botón sonaba bien. Los desarrolladores adaptaron OAuth 2.0 para uso móvil. Por supuesto, no muchos se preocuparon por la seguridad y los detalles de las aplicaciones móviles: ¡zap y entraron en la producción! Por otra parte, OAuth 2.0 no funciona bien fuera de las aplicaciones web: existen los mismos problemas en las aplicaciones móviles y de escritorio.
Entonces, descubramos cómo hacer que el OAuth 2.0 móvil sea seguro.
Como funciona
Hay dos problemas principales de seguridad de OAuth 2.0 para dispositivos móviles:
- Cliente no confiable. Algunas aplicaciones móviles no tienen un back-end para OAuth 2.0, por lo que la parte del cliente del flujo del protocolo va en el dispositivo móvil.
- Las redirecciones de un navegador a una aplicación móvil se comportan de manera diferente según la configuración del sistema, el orden en que se instalaron las aplicaciones y otras formas de magia.
Echemos un vistazo en profundidad a estos problemas.
La aplicación móvil es un cliente público.
Para comprender las raíces y las consecuencias 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 los registros del servicio del cliente en el servicio del proveedor y recibe
client_id
y
,
en algunos casos
, client_secret. client_id
, client_secret. client_id
es un valor público, y se requiere para la identificación del servicio del cliente en
client_secret
valor
client_secret
, que es privado. Puede leer más sobre el proceso de registro en
RFC 7591 .
El siguiente esquema muestra la forma en que opera OAuth 2.0 en caso de interacción de servidor a servidor.
Origen de la imagen: https://tools.ietf.org/html/rfc6749#section-1.2El protocolo OAuth 2.0 se puede dividir en tres pasos principales:
- [pasos AC] Reciba un
code
authorization_code
(en adelante, code
).
- [pasos DE]
code
intercambio para access_token
.
- Obtenga recursos a través de
access_token
.
Vamos a desarrollar el proceso de obtener el valor del
code
:
- [Paso A] El cliente redirige al usuario al proveedor de servicios.
- [Paso B] El proveedor de servicios solicita permiso al usuario para proporcionar al cliente los datos (flecha B hacia arriba). El usuario proporciona acceso a datos (flecha B a la derecha).
- [Paso C] El proveedor de servicios devuelve el
code
al navegador del usuario que redirige el code
al cliente.
Hablemos más sobre el proceso de obtener
access_token
:
- [Paso D] El servidor del cliente envía una solicitud de
access_token
. Code
, client_secret
y redirect_uri
están incluidos en la solicitud.
- [Paso E] En caso de
code
válido, client_secret
y redirect_uri
, se proporciona access_token
.
La solicitud de
access_token
a
access_token
se realiza de acuerdo con el esquema de servidor a servidor: por lo tanto, en general, el atacante debe hackear el servidor de servicio del cliente o el servidor del proveedor de servicios para robar
access_token
.
Ahora veamos el esquema móvil OAuth 2.0 sin backend (interacción de cliente a servidor).
Origen de la imagen: https://tools.ietf.org/html/rfc8252#section-4.1El esquema principal se divide en los mismos pasos principales:
- [pasos 1-4 en la imagen] Obtenga el
code
. - [pasos 5-6 en la imagen]
code
intercambio para access_token
- Obtenga acceso a recursos a través de
access_token
Sin embargo, en este caso, la aplicación móvil también tiene funciones de servidor; por lo tanto,
client_secret
se incrustará en la aplicación. Como resultado,
client_secret
no puede mantenerse oculto del atacante en dispositivos móviles. Embedded
client_secret
se puede extraer de dos maneras: analizando el tráfico de la aplicación al servidor o mediante ingeniería inversa. Ambos se pueden implementar fácilmente, y es por eso que
client_secret
es inútil en dispositivos móviles.
Puede preguntar: "¿Por qué no obtenemos
access_token
inmediato?" Puede pensar que este paso adicional es innecesario. Además, existe un esquema de
concesión implícita que permite que un cliente reciba
access_token
inmediato. A pesar de que puede usarse en algunos casos, la
Subvención implícita no funcionaría para OAuth 2.0 móvil seguro.
Redirección en dispositivos móviles
En general, el
esquema URI personalizado y los mecanismos
AppLink se utilizan para la redirección de navegador a aplicación. Ninguno de estos mecanismos puede ser tan seguro como el navegador redirige por sí solo.
El esquema URI personalizado (o enlace profundo) se usa de la siguiente manera: un desarrollador determina un esquema de aplicación antes de la implementación. El esquema puede ser cualquiera, y un dispositivo puede tener varias aplicaciones con el mismo esquema.
Facilita las cosas cuando cada esquema en un dispositivo corresponde con una aplicación. Pero, ¿qué pasa si dos aplicaciones registran el mismo esquema en un dispositivo? ¿Cómo decide el sistema operativo qué aplicación abrir cuando se contacta a través del esquema URI personalizado? Android mostrará una ventana con la opción de una aplicación y un enlace a seguir. iOS
no tiene un procedimiento para esto y, por lo tanto, cualquiera de las aplicaciones puede abrirse. De todos modos, el atacante tiene la
oportunidad de interceptar código o access_token .
A diferencia del esquema URI personalizado,
AppLink garantiza abrir la aplicación correcta, pero este mecanismo tiene varios defectos:
- Cada cliente de servicio debe someterse al procedimiento de verificación .
- Los usuarios de Android pueden desactivar AppLink para una aplicación específica en la configuración.
- Las versiones de Android anteriores a 6.0 y las versiones de iOS anteriores a 9.0 no son compatibles con AppLink.
Todos estos defectos de AppLink aumentan la curva de aprendizaje para clientes potenciales de servicio y pueden ocasionar la falla del usuario OAuth 2.0 en algunas circunstancias. Es por eso que muchos desarrolladores no eligen el mecanismo AppLink como sustitución de la redirección del navegador en el protocolo OAuth 2.0.
OK, ¿qué hay para atacar?
Los problemas de Mobile OAuth 2.0 han creado algunos ataques específicos. Veamos qué son y cómo funcionan.
Ataque de intercepción del código de autorización
Consideremos la situación en la que el dispositivo del usuario tiene una aplicación legítima (cliente OAuth 2.0) y una aplicación maliciosa que registró el mismo esquema que el legítimo. La siguiente imagen muestra el esquema de ataque.
Origen de la imagen https://tools.ietf.org/html/rfc7636#section-1Aquí está el problema: en el cuarto paso, el navegador devuelve el
code
en la aplicación a través del esquema URI personalizado y, por lo tanto, el
code
puede ser interceptado por una aplicación maliciosa (ya que está registrado el mismo esquema que una aplicación legítima). Luego, la aplicación maliciosa cambia el
code
a
access_token
y recibe acceso a los datos del usuario.
¿Cuál es la protección? En algunos casos, puede usar la comunicación entre procesos; Hablaremos de eso más tarde. En general, necesita un esquema llamado
Clave de prueba para el intercambio de código . Se describe en el esquema a continuación.
Origen de la imagen: https://tools.ietf.org/html/rfc7636#section-1.1La solicitud del cliente tiene varios parámetros adicionales:
code_verifier
,
code_challenge
(en el esquema
t(code_verifier)
) y
code_challenge_method
(en el esquema
t_m
).
Code_verifier
: es un número aleatorio
con una longitud mínima de 256 bits ,
que se usa solo una vez . Por lo tanto, un cliente debe generar un nuevo
code_verifier
para cada solicitud de
code
.
Code_challenge_method
: este es el nombre de una función de conversión, principalmente SHA-256.
Code_challenge
: es
code_verifier
al que se
code_challenge_method
conversión
code_challenge_method
y que se codificó en URL Safe Base64.
La conversión de
code_verifier
en
code_challenge
es necesaria para rechazar 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
.
En caso de que un dispositivo de usuario
no sea compatible con SHA-256, un
client is allowed to use plain conversion of code_verifier
. En todos los demás casos, se debe utilizar SHA-256.
Así es como funciona este esquema:
- El cliente genera
code_verifier
y lo memoriza.
- El cliente elige
code_challenge_method
y recibe 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 almacena
code_challenge
y code_challenge_method
en el servidor y devuelve el code
a un cliente.
- [Paso C] El cliente solicita
access_token
, con code_verifier
agregado.
- El proveedor recibe
code_challenge
del code_challenge
entrante y luego lo compara con code_challenge
, que guardó.
- [Paso D] Si los valores coinciden, el proveedor le otorga al cliente
access_token
.
Para entender por qué
code_challenge
intercepción de código, veamos cómo se ve el flujo del protocolo desde la perspectiva del atacante.
- Primero, el
code
solicitud de aplicación legítima ( code_challenge
y code_challenge_method
se envía junto con la solicitud ).
- La aplicación maliciosa intercepta el
code
(pero no code_challenge
, ya que el código _challenge
no está en la respuesta).
- La aplicación maliciosa solicita
access_token
(con code
válido, pero sin code_verifier
válido).
- El servidor nota la falta de coincidencia de
code_challenge
y genera un mensaje de error.
Tenga en cuenta que el atacante no puede adivinar
code_verifier
(¡valor aleatorio de 256 bits!) O encontrarlo en algún lugar de los registros (ya que la primera solicitud realmente transmitió
code_challenge
).
Entonces,
code_challenge
responde la pregunta del proveedor de servicios: "¿
access_token
solicita el mismo cliente de la aplicación que solicitó el
code
o uno diferente?".
OAuth 2.0 CSRF
OAuth 2.0 CSRF es relativamente inofensivo cuando se usa OAuth 2.0 para autorización. Es una historia completamente diferente cuando se usa OAuth 2.0 para la autenticación. En este caso, OAuth 2.0 CSRF a menudo conduce a la toma de control de la cuenta.
Hablemos más sobre el ataque CSRF conforme a OAuth 2.0 a través del ejemplo del cliente de la aplicación de taxi y el proveedor provider.com. Primero, un atacante en su propio dispositivo inicia sesión en la cuenta
attacker@provider.com
y recibe el
code
para el taxi. Luego interrumpe el proceso OAuth 2.0 y genera un enlace:
com.taxi.app://oauth? code=b57b236c9bcd2a61fcd627b69ae2d7a6eb5bc13f2dc25311348ee08df43bc0c4
Luego, el atacante envía este enlace a su víctima, por ejemplo, en forma de correo o mensaje de texto de cosas de taxi. La víctima hace clic en el enlace, la aplicación de taxi se abre y recibe
access_token
. Como resultado, se encuentran en la cuenta de taxi
del atacante . Sin darse cuenta de eso, la víctima usa esta cuenta: realiza viajes, ingresa datos personales, etc.
Ahora el atacante puede iniciar sesión en la cuenta de taxi de la víctima en cualquier momento, ya que está vinculado a
attacker@provider.com
. El ataque de inicio de sesión CSRF permitió al infractor robar una cuenta.
Los ataques CSRF generalmente se rechazan con un token CSRF (también se llama
state
), y OAuth 2.0 no es una excepción. Cómo usar el token CSRF:
- La aplicación del cliente genera y guarda el token CSRF en el dispositivo móvil de un cliente.
- La aplicación del cliente incluye el token CSRF en la solicitud de acceso al
code
.
- El servidor devuelve el mismo token CSRF con
code
en su respuesta.
- La aplicación cliente compara los tokens CSRF entrantes y guardados. Si sus valores coinciden, el proceso continúa.
Requisitos de token CSRF:
nonce debe ser de al menos 256 bits y recibirse de una buena fuente de secuencias pseudoaleatorias.
En pocas palabras, el token CSRF permite que un cliente de la aplicación responda la siguiente pregunta: "¿
access_token
yo quien inició la solicitud de
access_token
o alguien estaba tratando de engañarme?".
Secreto de cliente codificado
Las aplicaciones móviles sin backend a veces almacenan valores de
client_id
y
client_secret
codificados. Por supuesto, se pueden extraer fácilmente mediante la aplicación de ingeniería inversa.
El impacto de exponer
client_id
y
client_secret
depende en gran medida de la cantidad que el proveedor de servicios de confianza ponga en el determinado par
client_id
,
client_secret
. Uno los usa solo para distinguir un cliente de otro, mientras que otros abren puntos finales de API ocultos o establecen límites de velocidad más suaves para algunos clientes.
El artículo
Por qué las claves y los secretos API de OAuth no son seguros en las aplicaciones móviles explica más sobre este tema.
Aplicación maliciosa que actúa como un cliente legítimo
Algunas aplicaciones maliciosas pueden imitar las aplicaciones legítimas y mostrar una pantalla de consentimiento en su nombre (una pantalla de consentimiento es una pantalla en la que un usuario ve: "Acepto proporcionar acceso a ..."). El usuario puede hacer clic en "permitir" y proporcionar a la aplicación maliciosa sus datos.
Android e iOS proporcionan los mecanismos de verificación cruzada de aplicaciones. Un proveedor de aplicaciones puede asegurarse de que una aplicación cliente sea legítima y viceversa.
Desafortunadamente, si el mecanismo OAuth 2.0 usa un hilo a través del navegador, es imposible defenderse de este ataque.
Otros ataques
Echamos un vistazo más de cerca a los ataques exclusivos para dispositivos móviles OAuth 2.0. Sin embargo, no nos olvidemos del OAuth 2.0 original: sustitución de
redirect_uri
, intercepción de tráfico a través de una conexión no segura, etc. Puedes leer más sobre esto
aquí .
¿Cómo hacerlo de forma segura?
Hemos aprendido cómo funciona el protocolo OAuth 2.0 y qué vulnerabilidades tiene en los dispositivos móviles. Ahora, juntemos las piezas separadas para tener un esquema móvil seguro de OAuth 2.0.
Bueno, malo OAuth 2.0
Comencemos con la forma correcta de usar la pantalla de consentimiento. Los dispositivos móviles tienen dos formas de abrir una página web en una aplicación móvil.

La primera forma es a través de la pestaña personalizada del navegador (a la izquierda de la imagen).
Nota : La pestaña personalizada del navegador para Android se denomina pestaña personalizada de Chrome y para iOS: SafariViewController. Es solo una pestaña del navegador que se muestra en la aplicación: no hay cambio visual entre las aplicaciones.
La segunda forma es a través de WebView (a la derecha en la imagen) y lo considero malo con respecto al móvil OAuth 2.0.
WebView es un navegador integrado para una aplicación móvil.
"
Navegador incorporado " significa que el acceso a cookies, almacenamiento, caché, historial y otros datos de Safari y Chrome está prohibido para WebView. Lo contrario también es correcto: Safari y Chrome no pueden acceder a los datos de WebView.
"
Navegador de aplicaciones móviles " significa que una aplicación móvil que ejecuta WebView tiene acceso
completo a cookies, almacenamiento, caché, historial y otros datos de WebView.
Ahora, imagine: un usuario hace clic en "entrar con ..." y un WebView de una aplicación maliciosa solicita su nombre de usuario y contraseña al proveedor de servicios.
Fracaso épico:
- El usuario ingresa su nombre de usuario y contraseña para la cuenta del proveedor de servicios en la aplicación, que puede robar fácilmente estos datos.
- OAuth 2.0 se desarrolló inicialmente para no ingresar el nombre de usuario y la contraseña del proveedor de servicios.
El usuario se acostumbra a ingresar su nombre de usuario y contraseña en cualquier lugar, lo que aumenta la posibilidad de pesca .
Teniendo en cuenta todos los inconvenientes de WebView, se ofrece una conclusión obvia: use la pestaña personalizada del navegador para la pantalla de consentimiento.
Si alguien tiene argumentos a favor de WebView en lugar de la pestaña personalizada del navegador, agradecería que escribiera al respecto en los comentarios.
Esquema móvil seguro de OAuth 2.0
Vamos a utilizar el esquema de autorización de concesión de código, ya que nos permite agregar
code_challenge
, así como
state
y defendernos contra un ataque de intercepción de código y OAuth 2.0 CSRF.
Origen de la imagen: https://tools.ietf.org/html/rfc8252#section-4.1La solicitud de acceso al código (pasos 1-2) tendrá el siguiente aspecto:
https://o2.mail.ru/code? redirect_uri=com.mail.cloud.app%3A%2F%2Foauth& state=927489cb2fcdb32e302713f6a720397868b71dd2128c734181983f367d622c24& code_challenge=ZjYxNzQ4ZjI4YjdkNWRmZjg4MWQ1N2FkZjQzNGVkODE1YTRhNjViNjJjMGY5MGJjNzdiOGEzMDU2ZjE3NGFiYw%3D%3D& code_challenge_method=S256& scope=email%2Cid& response_type=code& client_id=984a644ec3b56d32b0404777e1eb73390c
3D% 3D y https://o2.mail.ru/code? redirect_uri=com.mail.cloud.app%3A%2F%2Foauth& state=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 obtiene una respuesta con redireccionamiento:
com.mail.cloud.app://outh? code=b57b236c9bcd2a61fcd627b69ae2d7a6eb5bc13f2dc25311348ee08df43bc0c4& state=927489cb2fcdb32e302713f6a720397868b71dd2128c734181983f367d622c24
En el paso 4, el navegador abre el esquema URI personalizado y pasa el token CSRF a una 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 trae una respuesta con
access_token
.
Este esquema es generalmente seguro, pero hay algunos casos especiales en los que OAuth 2.0 puede ser más simple y más seguro.
Android IPC
Android tiene un mecanismo de comunicación de datos bidireccional entre procesos: IPC (comunicación entre procesos). IPC es mejor que el esquema URI personalizado por dos razones:
- Una aplicación que abre el canal IPC puede confirmar la autenticidad de una aplicación que está abriendo mediante su certificado. Lo contrario también es cierto: la aplicación abierta puede confirmar la autenticidad de la aplicación que la abrió.
- Si un remitente envía una solicitud a través del canal IPC, puede recibir una respuesta a través del mismo canal. Junto con la verificación cruzada (elemento 1), significa que ningún proceso externo puede interceptar
access_token
.

Por lo tanto, podemos usar la
concesión implícita para simplificar el esquema móvil OAuth 2.0. Sin
code_challenge
y
state
también significa menos superficie de ataque. También podemos reducir los riesgos de que aplicaciones maliciosas actúen como clientes legítimos que intentan robar las cuentas de los usuarios.
SDK para clientes
Además de implementar este esquema seguro de OAuth 2.0 móvil, un proveedor debe desarrollar SDK para sus clientes. Simplificará la implementación de OAuth 2.0 en el lado del cliente y al mismo tiempo reducirá la cantidad de errores y vulnerabilidades.
Conclusiones
Déjame resumirlo para ti. Aquí está la
lista de verificación (básica)
para OAuth 2.0 seguro para proveedores de OAuth 2.0:
- Una base sólida es crucial. En el caso de OAuth 2.0 móvil, la base es un esquema o un protocolo elegido para su implementación. Es fácil cometer errores al implementar su propio esquema OAuth 2.0 . Otros ya recibieron golpes y aprendieron su lección; No hay nada de malo en aprender de sus errores y hacer una implementación segura de una vez. El esquema OAuth 2.0 móvil más seguro se describe en ¿Cómo hacerlo de forma segura ?
Access_token
y otros datos confidenciales deben almacenarse en Keychain para iOS y en Internal Storage para Android. Estos almacenes se desarrollaron específicamente solo para eso. El proveedor de contenido se puede usar en Android, pero se debe configurar de forma segura.
Client_secret
es inútil , a menos que esté almacenado en el backend. No lo regale a los clientes públicos.
- No use WebView para la pantalla de consentimiento; use la pestaña personalizada del navegador.
- Para defenderse del ataque de intercepción de código, use
code_challenge
.
- Para defenderse contra OAuth 2.0 CSRF, use
state
.
- Use HTTPS en todas partes , con la degradación prohibida a HTTP. Aquí hay una demostración de 3 minutos que explica por qué (con un ejemplo de una recompensa por errores).
- Siga los estándares de criptografía (elección del algoritmo, longitud de los tokens, etc.). Puede copiar los datos y descubrir por qué se hace de esta manera, pero no haga rodar su propia criptografía.
Code
debe usarse solo una vez, con una vida útil corta.
- Desde el lado del cliente de una aplicación, verifique qué abre para OAuth 2.0; y desde el lado de un proveedor de aplicaciones, verifique quién lo abre para OAuth 2.0.
- Tenga en cuenta las vulnerabilidades comunes de OAuth 2.0 . Mobile OAuth 2.0 amplía y completa el original, por lo tanto, la verificación de
redirect_uri
para una coincidencia exacta y otras recomendaciones para el OAuth 2.0 original todavía están vigentes.
- Debe proporcionar a sus clientes SDK. Tendrán menos errores y vulnerabilidades y les será más fácil implementar su OAuth 2.0.
Lectura adicional
- "Vulnerabilidades del móvil OAuth 2.0" https://www.youtube.com/watch?v=vjCF_O6aZIg
- Investigación de la condición de carrera de OAuth 2.0 https://hackerone.com/reports/55140
- Casi todo sobre OAuth 2.0 en un solo lugar https://oauth.net/2/
- Por qué las claves y los secretos de la API de OAuth no son seguros en las aplicaciones móviles https://developer.okta.com/blog/2019/01/22/oauth-api-keys-arent-safe-in-mobile-apps
- [RFC] OAuth 2.0 para aplicaciones nativas https://tools.ietf.org/html/rfc8252
- [RFC] Clave de prueba para el intercambio de código por parte de clientes públicos de OAuth https://tools.ietf.org/html/rfc7636
- [RFC] Modelo de amenaza OAuth 2.0 y consideraciones de seguridad https://tools.ietf.org/html/rfc6819
- [RFC] Protocolo de registro de cliente dinámico OAuth 2.0 https://tools.ietf.org/html/rfc7591
- Google OAuth 2.0 para aplicaciones móviles y de escritorio https://developers.google.com/identity/protocols/OAuth2InstalledApp
Créditos
Gracias a todos los que me ayudaron a escribir este artículo. Especialmente a Sergei Belov, Andrei Sumin, Andrey Labunets por los comentarios sobre detalles técnicos, a Pavel Kruglov por la traducción al inglés y a Daria Yakovleva por la ayuda con el lanzamiento de la versión rusa de este artículo.