
Bonjour à tous! Je suis Nikita Stupin, spécialiste de la sécurité de l'information, Mail.Ru Mail. Il n'y a pas si longtemps, j'ai mené une recherche de vulnérabilité sur OAuth 2.0 mobile. Pour créer un schéma OAuth 2.0 mobile sécurisé, il ne suffit pas d'implémenter la norme dans sa forme pure et de vérifier redirect_uri. Il est nécessaire de prendre en compte les spécificités des applications mobiles et d'appliquer des mécanismes de protection supplémentaires.
Dans cet article, je veux partager avec vous des connaissances sur les attaques contre OAuth 2.0 mobile, sur les méthodes de protection et la mise en œuvre sûre de ce protocole. Tous les composants de protection nécessaires, dont je parlerai ci-dessous, sont implémentés dans le dernier SDK pour les clients mobiles Mail.Ru Mail.
La nature et la fonction d'OAuth 2.0
OAuth 2.0 est un protocole d'autorisation qui décrit comment un service client peut accéder en toute sécurité aux ressources utilisateur sur un fournisseur de services. Dans le même temps, OAuth 2.0 évite à l'utilisateur d'avoir à saisir un mot de passe en dehors du prestataire de services: l'ensemble du processus se réduit à cliquer sur le bouton «J'accepte d'accorder l'accès à ...».
Un fournisseur en termes d'OAuth 2.0 est un service qui possède des données utilisateur et, avec la permission de l'utilisateur, fournit des services tiers (clients) avec un accès sécurisé à ces données. Un client est une application qui souhaite recevoir des données utilisateur d'un fournisseur.
Quelque temps après la sortie du protocole OAuth 2.0, les développeurs ordinaires l'ont adapté pour l'authentification, bien qu'il n'était pas initialement prévu pour cela. L'authentification déplace le vecteur d'attaque des données utilisateur stockées chez le fournisseur de services vers les comptes d'utilisateurs de service utilisateur.
Elle ne se limitait pas à l'authentification seule. À l'ère des applications mobiles et de l'exaltation de la conversion, entrer dans l'application avec un seul bouton est devenu très tentant. Les développeurs ont mis OAuth 2.0 sur des rails mobiles. Naturellement, peu de gens ont pensé à la sécurité et aux spécificités des applications mobiles: encore et encore, et en production. Cependant, OAuth 2.0 ne fonctionne généralement pas bien en dehors des applications Web: les mêmes problèmes sont observés dans les applications mobiles et de bureau.
Voyons comment créer un OAuth 2.0 mobile sécurisé.
Comment ça marche?
N'oubliez pas que sur les appareils mobiles, le client peut ne pas être un navigateur, mais une application mobile sans backend. Par conséquent, nous sommes confrontés à deux problèmes de sécurité majeurs pour OAuth 2.0 mobile:
- Le client n'est pas fiable.
- Le comportement d'une redirection d'un navigateur vers une application mobile dépend des paramètres et des applications que l'utilisateur a installés.
L'application mobile est un client public
Pour comprendre la racine du premier problème, voyons comment OAuth 2.0 fonctionne en cas d'interaction de serveur à serveur, puis comparons-le avec OAuth 2.0 en cas d'interaction client-serveur.
Dans les deux cas, tout commence par le fait que le client de service s'inscrit auprès du fournisseur de services et reçoit
client_id
et, dans certains cas,
client_secret
. La valeur
client_id
est publique et est nécessaire pour identifier le service client, contrairement à
client_secret
, dont la valeur est privée. Le processus d'enregistrement est décrit plus en détail dans la
RFC 7591 .
Le diagramme ci-dessous montre le fonctionnement d'OAuth 2.0 dans la communication de serveur à serveur.
Image prise à partir de https://tools.ietf.org/html/rfc6749#section-1.2Il existe 3 étapes principales du protocole OAuth 2.0:
- [Étapes AC] Obtenez le code d'autorisation (ci-après simplement
code
). - [Étapes DE] Échangez le
code
pour access_token
. - Accédez à la ressource en utilisant
access_token
.
Examinons plus en détail la réception du code:
- [Étape A] Le client de service redirige l'utilisateur vers le fournisseur de services.
- [Étape B] Le fournisseur de services demande l'autorisation à l'utilisateur de fournir des données au service client (flèche B vers le haut). L'utilisateur donne accès aux données (flèche B à droite).
- [Étape C] Le fournisseur de services renvoie le
code
au navigateur de l'utilisateur, qui redirige le code
le service client.
Voyons
access_token
obtenir
access_token
plus en détail:
- [Étape D] Le serveur client envoie une demande d'
access_token
. La demande comprend: code
, client_secret
et redirect_uri
. - [Étape E] Dans le cas d'un
code
valide, client_secret
et redirect_uri
, client_secret
fourni.
La demande d'
access_token
est effectuée selon le schéma de serveur à serveur, par conséquent, en général, pour voler
client_secret
attaquant doit pirater le serveur client-serveur ou le serveur du fournisseur de services.
Voyons maintenant à quoi ressemble le schéma OAuth 2.0 sur un appareil mobile sans backend (interaction client-serveur).
Image prise à partir de https://tools.ietf.org/html/rfc8252#section-4.1Le schéma général est divisé en 3 mêmes étapes principales:
- [étapes 1 à 4 de l'image] Obtenez le
code
. - [étapes 5-6 dans l'image]
code
échange pour access_token
. - Accédez à la ressource en utilisant
access_token
.
Cependant, dans ce cas, l'application mobile agit également comme un serveur, ce qui signifie que
client_secret
sera
client_secret
à l'intérieur de l'application. Cela conduit au fait que sur les appareils mobiles, il est impossible de garder
lient_secret
d'un attaquant.
client_secret
existe deux façons de
client_secret
dans l'application: pour filtrer le trafic de l'application vers le serveur ou effectuer une rétro-ingénierie de l'application. Les deux méthodes sont faciles à implémenter, donc
client_secret
inutile sur les appareils mobiles.
Concernant le schéma client-serveur, vous pourriez avoir une question: «pourquoi ne pas obtenir immédiatement
access_token
?». Il semblerait, pourquoi avons-nous besoin d'une étape supplémentaire? De plus, il existe un schéma de
subvention implicite dans lequel le client reçoit immédiatement un
access_token
. Bien qu'il puisse être utilisé dans certains cas, nous verrons ci-dessous que la
subvention implicite ne convient pas pour OAuth 2.0 mobile sécurisé.
Rediriger sur les appareils mobiles
En général, pour une redirection d'un navigateur vers une application sur des appareils mobiles, les mécanismes
Schéma URI personnalisé et
AppLink sont utilisés. Aucun de ces mécanismes dans sa forme pure n'est aussi fiable qu'une redirection de navigateur.
Le schéma d'URI personnalisé (ou lien profond) est utilisé comme suit: le développeur définit le schéma d'application avant l'assemblage. Le schéma peut être arbitraire, tandis que sur le même appareil, plusieurs applications avec le même schéma peuvent être installées. Tout est assez simple lorsque chaque application sur l'appareil correspond à une application. Mais que se passe-t-il si deux applications enregistrent le même circuit sur le même appareil? Comment le système d'exploitation peut-il déterminer laquelle des deux applications ouvrir lors de l'accès au schéma d'URI personnalisé? Android affichera une fenêtre avec le choix de l'application dans laquelle vous souhaitez ouvrir un lien. Dans iOS, le
comportement n'est pas défini , ce qui signifie que l'une des deux applications peut être ouverte. Dans les deux cas, un attaquant
peut intercepter du code ou access_token .
AppLink, contrairement au schéma d'URI personnalisé, garantit l'ouverture de la bonne application, mais ce mécanisme présente plusieurs inconvénients:
- Chaque client de service doit passer indépendamment la procédure de vérification .
- Les utilisateurs d'Android peuvent désactiver AppLink pour une application spécifique dans les paramètres.
- Android sous 6.0 et iOS sous 9.0 ne prennent pas en charge AppLink.
Les inconvénients ci-dessus d'AppLink, d'une part, augmentent le seuil d'entrée pour les services clients potentiels, et d'autre part, peuvent conduire au fait que dans certaines circonstances, l'utilisateur ne fonctionnera pas OAuth 2.0. Cela rend AppLink inadapté au remplacement des redirections de navigateur dans le protocole OAuth 2.0.
D'accord, quoi attaquer?
Les problèmes du mobile OAuth 2.0 ont également donné lieu à des attaques spécifiques. Voyons ce qu'ils sont et comment ils fonctionnent.
Attaque d'interception de code d'autorisation
Données initiales: une application légitime (client OAuth 2.0) et une application malveillante ayant enregistré le même schéma que celui légitime sont installées sur l'appareil de l'utilisateur. La figure ci-dessous montre le schéma d'attaque.
Image prise à partir de https://tools.ietf.org/html/rfc7636#section-1Voici le problème: à l'étape 4, le navigateur renvoie le
code
à l'application via le schéma d'URI personnalisé, afin que le
code
puisse être intercepté par le malware (car il a enregistré le même schéma que l'application légitime). Après cela, le malware modifie le
code
en
access_token
et accède aux données utilisateur.
Comment se protéger? Dans certains cas, des mécanismes de communication interprocessus peuvent être utilisés, nous en parlerons ci-dessous. Dans le cas général, vous devez appliquer un schéma appelé
clé de preuve pour l'échange de code . Son essence se reflète dans le schéma ci-dessous.
Image prise à partir de https://tools.ietf.org/html/rfc7636#section-1.1Dans les requêtes du client, il existe plusieurs paramètres supplémentaires:
code_verifier
,
code_challenge
(sur le
t(code_verifier)
) et
code_challenge_method
(sur le diagramme
t_m
).
Code_verifier
est un nombre aléatoire
d'au moins 256 bits qui n'est
utilisé qu'une seule fois . Autrement dit, pour
chaque demande de
code
client doit générer un nouveau
code_verifier
.
Code_challenge_method
est le nom d'une fonction de conversion, le plus souvent SHA-256.
Code_challenge
est un
code_verifier
auquel la conversion
code_challenge_method
a été
code_challenge_method
et encodée dans l'URL Safe Base64.
La conversion de
code_verifier
en
code_challenge
nécessaire pour se protéger contre les vecteurs d'attaque basés sur l'interception de
code_verifier
(par exemple, à partir des journaux système de l'appareil) lors de la demande de
code
.
Si l'appareil de l'utilisateur
ne prend pas en charge SHA-256, une
rétrogradation est autorisée jusqu'à ce que la conversion code_verifier soit manquante . Dans tous les autres cas, vous devez utiliser SHA-256.
Le schéma fonctionne comme suit:
- Le client génère un
code_verifier
et s'en souvient. - Le client sélectionne
code_challenge_method
et obtient code_challenge
de code_verifier
. - [Étape A] Le client demande du
code
, avec code_challenge
et code_challenge_method
ajoutés à la demande. - [Étape B] Le fournisseur se souvient du
code_challenge
et du code_challenge_method
sur le serveur et renvoie le code
client. - [Étape C] Le client demande
access_token
, avec access_token
étant ajouté à la code_verifier
. - Le fournisseur reçoit le
code_challenge
du code_challenge
entrant, puis le code_challenge
au code_challenge
dont il s'est souvenu. - [Étape D] Si les valeurs correspondent, le fournisseur émet un
access_token
client.
Voyons pourquoi
code_challenge
permet de
code_challenge
protéger contre une attaque d'interception de code. Pour ce faire, nous allons passer par les étapes d'obtention de
access_token
.
- Tout d'abord, une application légitime demande du
code
( code_challenge
et code_challenge_method
envoyés avec la demande ). - Le malware intercepte le
code
(mais pas code_challenge
, car il n'y a pas de code_challenge
dans la réponse ). - Le malware demande
access_token
(avec un code
valide, mais sans code_verifier
valide). - Le serveur remarque l'incompatibilité de
code_challenge
et renvoie une erreur.
Notez que l'attaquant n'a pas la capacité de deviner le
code_verifier
(256 bits aléatoires!) Ou de le trouver quelque part dans les journaux (
code_verifier
est transmis une fois).
Si tout cela est réduit à une phrase, alors
code_challenge
permet au fournisseur de services de répondre à la question: "
access_token
demandé par la même application cliente qui a demandé le
code
, ou par une autre?"
OAuth 2.0 CSRF
Sur les appareils mobiles, OAuth 2.0 est souvent utilisé comme mécanisme d'authentification. Comme nous nous en souvenons, l'authentification via OAuth 2.0 diffère de l'autorisation en ce que les vulnérabilités OAuth 2.0 affectent les données utilisateur du côté du client de service et non du fournisseur de services. Par conséquent, l'attaque CSRF sur OAuth 2.0 vous permet de voler le compte de quelqu'un d'autre.
Considérez une attaque CSRF contre OAuth 2.0 en utilisant l'exemple de l'application client taxi et du fournisseur provider.com. Tout d'abord, un attaquant se connecte à attacker@provider.com sur son appareil et reçoit un
code
de taxi. Après cela, l'attaquant interrompt le processus OAuth 2.0 et génère un lien:
com.taxi.app://oauth?
code=b57b236c9bcd2a61fcd627b69ae2d7a6eb5bc13f2dc25311348ee08df43bc0c4
Ensuite, l'agresseur envoie un lien à la victime, par exemple, sous le couvert d'une lettre ou d'un SMS de l'administration des taxis. La victime
access_token
lien, une application de taxi s'ouvre sur son téléphone, qui reçoit
access_token
, et en conséquence la victime se retrouve sur le compte de taxi de l'
attaquant . Ignorant la capture, la victime utilise ce compte: fait des voyages, saisit ses données, etc.
Désormais, un attaquant peut se connecter à tout moment au compte de taxi de la victime car il est lié à
attacker@provider.com
. Attaque CSRF sur la connexion autorisée à voler un compte.
Les attaques CSRF sont généralement protégées par un jeton CSRF (également appelé
state
), et OAuth 2.0 ne fait pas exception. Comment utiliser le jeton CSRF:
- L'application cliente génère et stocke le jeton CSRF sur l'appareil mobile de l'utilisateur.
- L'application cliente inclut le jeton CSRF dans la demande de
code
. - Le serveur renvoie le même jeton CSRF dans la réponse avec le code.
- L'application client compare le jeton CSRF entrant et stocké. Si les valeurs correspondent, le processus se poursuit.
Exigences de jeton CSRF:
nonce d' au moins 256 bits de long, obtenues à partir d'une bonne source de séquences pseudo-aléatoires.
En bref, le jeton CSRF permet à l'application cliente de répondre à la question: "est-ce que je commençais à obtenir
access_token
, ou est-ce que quelqu'un essaie de me
access_token
?"
Malware se faisant passer pour un client légitime
Certains logiciels malveillants peuvent imiter des applications légitimes et afficher un écran de consentement en leur nom (l'écran de consentement est un écran sur lequel l'utilisateur voit: «J'accepte d'accorder l'accès à ...»). Un utilisateur inattentif peut cliquer sur «autoriser» et, par conséquent, le logiciel malveillant accède aux données de l'utilisateur.
Android et iOS fournissent des mécanismes de vérification mutuelle des applications. L'application fournisseur peut vérifier la légitimité de l'application cliente et vice versa.
Malheureusement, si le mécanisme OAuth 2.0 utilise un flux via un navigateur, vous ne pouvez pas vous défendre contre cette attaque.
Autres attaques
Nous avons examiné les attaques spécifiques au mobile OAuth 2.0. Cependant, n'oubliez pas les attaques sur OAuth 2.0 normal: usurpation d'identité
redirect_uri
, interception du trafic sur une connexion non sécurisée, etc. Vous pouvez en savoir plus à leur sujet
ici .
Que faire?
Nous avons appris le fonctionnement du protocole OAuth 2.0 et déterminé quelles vulnérabilités existent dans les implémentations de ce protocole sur les appareils mobiles. Maintenant, assemblons un schéma OAuth 2.0 mobile sécurisé à partir de pièces individuelles.
Bon, mauvais OAuth 2.0
Commençons par savoir comment lever correctement l'écran de consentement. Sur les appareils mobiles, il existe deux façons d'ouvrir une page Web à partir d'une application native (exemples d'applications natives: Mail.Ru Mail, VK, Facebook).

La première méthode est appelée l'onglet personnalisé du navigateur (dans l'image de gauche).
Remarque : l'onglet personnalisé du navigateur sur Android est appelé onglet personnalisé Chrome et sur iOS SafariViewController. En fait, il s'agit d'un onglet de navigateur normal, qui s'affiche directement dans l'application, c'est-à-dire Il n'y a pas de commutation visuelle entre les applications.
La deuxième méthode est appelée «augmenter WebView» (dans l'image de droite), par rapport au mobile OAuth 2.0, je la considère comme mauvaise.
WebView est un navigateur autonome pour une application native.
Un «
navigateur autonome» signifie que WebView ne permet pas d'accéder aux cookies, au stockage, au cache, à l'historique et aux autres données des navigateurs Safari et Chrome. L'inverse est également vrai: Safari et Chrome ne peuvent pas accéder aux données WebView.
«
Navigateur pour une application native » signifie que l'application native qui a généré WebView a
un accès
complet aux cookies, stockage, cache, historique et autres données WebView.
Imaginez maintenant: l'utilisateur appuie sur le bouton "se connecter en utilisant ..." et le WebView de l'application malveillante demande son nom d'utilisateur et son mot de passe au fournisseur de services.
Échec à la fois sur tous les fronts:
- L'utilisateur entre le nom d'utilisateur et le mot de passe à partir du compte du fournisseur de services dans l'application, qui peut facilement voler ces données.
- OAuth 2.0 a été initialement développé pour ne pas saisir de nom d'utilisateur et de mot de passe d'un fournisseur de services.
- L'utilisateur s'habitue à entrer le login et le mot de passe n'importe où, la probabilité de phishing augmente.
Étant donné que tous les arguments sont contre WebView, la conclusion se suggère: soulevez l'onglet personnalisé du navigateur pour l'écran de consentement.
Si l'un d'entre vous a des arguments en faveur de WebView au lieu de l'onglet personnalisé du navigateur, écrivez-le dans les commentaires, je vous en serai très reconnaissant.
Schéma Secure Mobile OAuth 2.0
Nous utiliserons le schéma d'autorisation de code d'autorisation car il nous permet d'ajouter un
code_challenge
et de
code_challenge
protéger contre une attaque d'interception de code.
Image prise à partir de https://tools.ietf.org/html/rfc8252#section-4.1La demande de code (étapes 1-2) ressemblera à ceci:
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
À l'étape 3, le navigateur reçoit une réponse redirigée:
com.mail.cloud.app://outh?
code=b57b236c9bcd2a61fcd627b69ae2d7a6eb5bc13f2dc25311348ee08df43bc0c4&
anti_csrf=927489cb2fcdb32e302713f6a720397868b71dd2128c734181983f367d622c24
À l'étape 4, le navigateur ouvre le schéma d'URI personnalisé et transmet le
code
et le jeton CSRF à l'application cliente.
Demande d'
access_token
(étape 5):
https://o2.mail.ru/token?
code_verifier=e61748f28b7d5daf881d571df434ed815a4a65b62c0f90bc77b8a3056f174abc&
code=b57b236c9bcd2a61fcd627b69ae2d7a6eb5bc13f2dc25311348ee08df43bc0c4&
client_id=984a644ec3b56d32b0404777e1eb73390c
La dernière étape renvoie une réponse avec
access_token
.
En général, le schéma ci-dessus est sûr, mais il existe également des cas particuliers dans lesquels OAuth 2.0 peut être rendu plus simple et un peu plus sûr.
IPC Android
Android dispose d'un mécanisme d'échange de données bidirectionnel entre les processus: IPC (communication inter-processus). IPC est préféré au schéma d'URI personnalisé pour deux raisons:
- Une application qui ouvre un canal IPC peut vérifier l'authenticité d'une application ouverte par son certificat. L'inverse est également vrai: une application ouverte peut vérifier l'authenticité de l'application qui l'a ouverte.
- En envoyant une demande via un canal IPC, l'expéditeur peut recevoir une réponse via le même canal. Avec la vérification mutuelle (élément 1), cela signifie qu'aucun processus tiers ne peut intercepter
access_token
.

Ainsi, nous pouvons utiliser la
subvention implicite et simplifier considérablement le schéma OAuth 2.0 mobile. Aucun
code_challenge
et jetons CSRF. De plus, nous serons en mesure de nous protéger contre les logiciels malveillants qui imitent les clients légitimes afin de voler des comptes d'utilisateurs.
SDK client
En plus d'implémenter le schéma OAuth 2.0 mobile sécurisé décrit ci-dessus, le fournisseur doit développer un SDK pour ses clients. Cela facilitera la mise en œuvre d'OAuth 2.0 côté client et réduira en même temps le nombre d'erreurs et de vulnérabilités.
Tirer des conclusions
Pour les fournisseurs OAuth 2.0, j'ai compilé la «Liste de contrôle Secure Mobile OAuth 2.0»:
- Une base solide est vitale. Dans le cas d'OAuth 2.0 mobile, la base est le schéma ou le protocole que nous choisissons de mettre en œuvre. Lorsque vous implémentez votre propre schéma OAuth 2.0, il est facile de faire une erreur. D'autres ont déjà rempli les bosses et tiré des conclusions, il n'y a rien de mal à apprendre de leurs erreurs et à faire immédiatement une mise en œuvre sûre. En général, le schéma OAuth 2.0 mobile le plus sécurisé est celui de la section Que faire?
Access_token
et d'autres données sensibles: sous iOS - dans le trousseau, sous Android - dans le stockage interne. Ces référentiels sont spécifiquement conçus à ces fins. Si nécessaire, vous pouvez utiliser le fournisseur de contenu dans Android, mais il doit être configuré en toute sécurité.Code
doit être unique, avec une courte durée de vie.- Pour vous protéger contre l'interception de code, utilisez
code_challenge
. - Pour vous protéger contre une attaque CSRF sur la connexion, utilisez des jetons CSRF.
- N'utilisez pas WebView pour l'écran de consentement, utilisez l'onglet personnalisé du navigateur.
Client_secret
inutile s'il n'est pas stocké sur le backend. Ne le donnez pas aux clients publics.- Utilisez HTTPS partout , avec l'interdiction de rétrograder vers HTTP.
- Suivez les recommandations de cryptographie (sélection de chiffre, longueur de jeton, etc.) des normes . Vous pouvez copier les données et découvrir pourquoi cela a été fait de cette façon, mais vous ne pouvez pas faire votre cryptographie .
- Dans l'application client, vérifiez qui vous ouvrez pour OAuth 2.0 et dans l'application fournisseur, vérifiez qui vous ouvre pour OAuth 2.0.
- Soyez conscient des vulnérabilités habituelles d'OAuth 2.0 . Mobile OAuth 2.0 étend et complète la version standard, donc personne n'a annulé la vérification de
redirect_uri
pour les correspondances exactes et autres recommandations pour OAuth 2.0 standard. - Assurez-vous de fournir des SDK aux clients. Le client aura moins de bogues et de vulnérabilités dans le code, et il lui sera plus facile d'implémenter votre OAuth 2.0.
Que lire
- [RFC] OAuth 2.0 pour les applications natives https://tools.ietf.org/html/rfc8252
- Google OAuth 2.0 pour applications mobiles et de bureau https://developers.google.com/identity/protocols/OAuth2InstalledApp
- [RFC] Clé de preuve pour l'échange de code par les clients publics OAuth https://tools.ietf.org/html/rfc7636
- Condition de course OAuth 2.0 https://hackerone.com/reports/55140
- [RFC] Modèle de menace OAuth 2.0 et considérations de sécurité https://tools.ietf.org/html/rfc6819
- Attaques sur OAuth 2.0 régulier https://sakurity.com/oauth
- [RFC] Protocole d'enregistrement de client dynamique OAuth 2.0 https://tools.ietf.org/html/rfc7591
Remerciements
Merci à tous ceux qui ont contribué à la rédaction de cet article, en particulier Sergey Belov, Andrey Sumin, Andrey Labunts (
@isciurus ) et Daria Yakovleva.