Sécurité Mobile OAuth 2.0



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:

  1. Le client n'est pas fiable.
  2. 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.2

Il existe 3 étapes principales du protocole OAuth 2.0:

  1. [Étapes AC] Obtenez le code d'autorisation (ci-après simplement code ).
  2. [Étapes DE] Échangez le code pour access_token .
  3. Accédez à la ressource en utilisant access_token .

Examinons plus en détail la réception du code:

  1. [Étape A] Le client de service redirige l'utilisateur vers le fournisseur de services.
  2. [É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).
  3. [É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:

  1. [Étape D] Le serveur client envoie une demande d' access_token . La demande comprend: code , client_secret et redirect_uri .
  2. [É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.1

Le schéma général est divisé en 3 mêmes étapes principales:

  1. [étapes 1 à 4 de l'image] Obtenez le code .
  2. [étapes 5-6 dans l'image] code échange pour access_token .
  3. 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:

  1. Chaque client de service doit passer indépendamment la procédure de vérification .
  2. Les utilisateurs d'Android peuvent désactiver AppLink pour une application spécifique dans les paramètres.
  3. 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-1

Voici 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.1

Dans 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:

  1. Le client génère un code_verifier et s'en souvient.
  2. Le client sélectionne code_challenge_method et obtient code_challenge de code_verifier .
  3. [Étape A] Le client demande du code , avec code_challenge et code_challenge_method ajoutés à la demande.
  4. [Étape B] Le fournisseur se souvient du code_challenge et du code_challenge_method sur le serveur et renvoie le code client.
  5. [Étape C] Le client demande access_token , avec access_token étant ajouté à la code_verifier .
  6. Le fournisseur reçoit le code_challenge du code_challenge entrant, puis le code_challenge au code_challenge dont il s'est souvenu.
  7. [É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 .

  1. Tout d'abord, une application légitime demande du code ( code_challenge et code_challenge_method envoyés avec la demande ).
  2. Le malware intercepte le code (mais pas code_challenge , car il n'y a pas de code_challenge dans la réponse ).
  3. Le malware demande access_token (avec un code valide, mais sans code_verifier valide).
  4. 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:

  1. L'application cliente génère et stocke le jeton CSRF sur l'appareil mobile de l'utilisateur.
  2. L'application cliente inclut le jeton CSRF dans la demande de code .
  3. Le serveur renvoie le même jeton CSRF dans la réponse avec le code.
  4. 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:

  1. 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.
  2. 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.
  3. 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.1

La 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:

  1. 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.
  2. 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»:

  1. 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?
  2. 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é.
  3. Code doit être unique, avec une courte durée de vie.
  4. Pour vous protéger contre l'interception de code, utilisez code_challenge .
  5. Pour vous protéger contre une attaque CSRF sur la connexion, utilisez des jetons CSRF.
  6. N'utilisez pas WebView pour l'écran de consentement, utilisez l'onglet personnalisé du navigateur.
  7. Client_secret inutile s'il n'est pas stocké sur le backend. Ne le donnez pas aux clients publics.
  8. Utilisez HTTPS partout , avec l'interdiction de rétrograder vers HTTP.
  9. 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 .
  10. 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.
  11. 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.
  12. 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


  1. [RFC] OAuth 2.0 pour les applications natives https://tools.ietf.org/html/rfc8252
  2. Google OAuth 2.0 pour applications mobiles et de bureau https://developers.google.com/identity/protocols/OAuth2InstalledApp
  3. [RFC] Clé de preuve pour l'échange de code par les clients publics OAuth https://tools.ietf.org/html/rfc7636
  4. Condition de course OAuth 2.0 https://hackerone.com/reports/55140
  5. [RFC] Modèle de menace OAuth 2.0 et considérations de sécurité https://tools.ietf.org/html/rfc6819
  6. Attaques sur OAuth 2.0 régulier https://sakurity.com/oauth
  7. [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.

Source: https://habr.com/ru/post/fr417031/


All Articles