OAuth 2.0 : Et si on arrêtait enfin d’utiliser l’Implicit Flow ?

CARBON
9 min readMar 1, 2023

Cet article est issu du blog de Kévin Llopis, développeur Fullstack chez CARBON IT. Vous pouvez retrouver tous ses articles sur son blog personnel.

Tout le monde aime partager des photos, des vidéos et bien d’autres contenus sur son blog préféré, mais imaginez que l’on souhaite partager des données dont on possède depuis une autre application web. Par exemple, il peut s’agir d’une application appelée My Best Recipes, pour ajouter et consulter ses recettes de cuisine. Et si l’on souhaite les partager sur son blog ou tout autre application ?

Avant l’arrivée du protocole OAuth, le blog souhaitant accéder aux données de My Best Recipes, aurait demandé dans un formulaire l’identifiant et le mot de passe de l’utilisateur en vue d’accéder aux recettes de My Best Recipes. Bien entendu, cela pose plusieurs problèmes de procéder de cette manière (on appelle ce procédé le password anti-pattern) :

  • L’accès à My Best Recipes par le blog ne peut pas être révoqué sans réinitialiser son propre mot de passe.
  • Le blog peut accéder à toutes les données du compte utilisateur (adresse, numéro de téléphone, etc), donc aucun contrôle de l’accès n’est possible.
  • Une application malicieuse peut demander à l’utilisateur l’identifiant/mot de passe de My Best Recipes, sous prétexte d’accéder à des données en vue de les partager et en réalité réaliser des actions malicieuses sur le compte de l’utilisateur.

Vous avez dit OAuth 2.0 ?

C’est pour répondre à ces problèmes que le protocole OAuth 2.0 a vu le jour et a pour objectif d’autoriser des applications tierces (le blog dans notre exemple) à accéder des données appartenant à un utilisateur en respectant les conditions suivantes :

  • L’accès aux données de l’utilisateur se fait avec son accord.
  • Sans donner son propre identifiant/mot de passe aux applications tierces.
  • Pour un accès restreint (on accorde l’accès à des données spécifiques et pas forcément à la totalité des données détenues).
  • Pour une durée limitée.

Pour cela, OAuth 2.0 s’appuie sur plusieurs acteurs qui vont participer aux échanges :

  • Resource Owner
    Il s’agit de l’utilisateur souhaitant autoriser une application à accéder à ses données.
  • Resource Server
    Dans notre exemple, cela correspond à My Best Recipes, c’est-à-dire l’application dont on souhaite partager les données avec le blog.
  • Client
    Le Client est l’application (le blog) accédant aux données du Resource Server.
  • Authorization Server
    L’Authorization Server est l’acteur central du protocole, il va jouer le rôle d’intermédiaire entre les différents acteurs, notamment en vue d’accorder l’autorisation au Client pour accéder au Resource Server. En effet, c’est l’Authorization Server qui va s’assurer que le Resource Owner(l’utilisateur) est bien d’accord pour accorder une telle autorisation.

L’implicit Flow

Nous avons vu en quoi consiste le protocole OAuth 2.0, mais il faut savoir qu’il existe plusieurs solutions (appelées flows) différentes pour le mettre en œuvre en fonction de notre cas d’utilisation. Par exemple, la solution sera différente selon que le Client soit une application back-end ou front-end.

Dans notre situation, nous allons nous intéresser au cas d’un Client front-end implémenté par exemple avec l’un de vos frameworks favoris tels que React/Angular. Pour une telle application, la solution préconisée aux prémices d’OAuth 2.0 était la mise en place de l’Implicit Flow, aujourd’hui obsolète.

Le schéma ci-dessous décrit les échanges dans l’Implicit Flow.

Analysons en détail chaque échange :

  1. L’application web (Web App) côté client initie le flow et envoie l’Authorization Request à l’Authorization Server.
  2. L’Authorization Server soumet un formulaire d’authentification au Resource Owner à travers le navigateur (Browser).
  3. Le Resource Owner transmet son identifiant/mot de passe.
  4. L’Authorization Server demande au Resource Owner d’accorder/refuser l’accès du Client aux données protégées (détenues par le Resource Server).
  5. Le Resource Owner accepte d’accorder l’accès.
  6. L’Authorization Server délivre sa réponse (Authorization Response) au navigateur.
  7. Le navigateur redirige vers l’URL contenue dans le header de réponse Location. Cette URL correspond à celle de l’application web avec l’access token transmis dans la query string. D’ailleurs, il s’agit d’une spécificité de l’Implicit Flow de transmettre directement l’access token au Client (sans passer par une étape intermédiaire, contrairement à l’Authorization Code Flow que nous verrons dans la suite de l’article).
  8. L’application web envoie sa requête d’accès aux ressources.
  9. Le Resource Server répond en transmettant les données protégées. Cela dit, une étape en amont consiste à vérifier la provenance de l’access token et s’assurer qu’il a bien été émis par l’Authorization Server, c’est à cette condition que l’on délivre les données protégées.

Pourquoi faut-il éviter de l’utiliser ?

La réponse de l’Authorization Server contient l’access token en tant que paramètre de la query string dans l’URL et c’est là tout le problème. Du fait du format de la réponse, il existe plusieurs moyens pour exploiter cette faille :

  • Interception de l’access token
    Ce cas s’applique uniquement si les échanges ne sont pas chiffrés entre le Client et le serveur. De ce fait, en cas d’interception des échanges entre le Client et l’Authorization Server, l’access token étant valorisé dans la query string, il peut être subtilisé.
  • Enregistrement de l’URL dans l’historique de navigation
    Après réception de la réponse de l’Authorization Server par le Client, le navigateur procède à une redirection de l’URL, du fait du header Location contenue dans la réponse de l’Authorization Server. En effet, cette URL est celle qui contient l’access token et elle sera enregistrée dans l’historique de navigation, ce qui rend exploitable l’access token par du code JavaScript en naviguant simplement sur un site web malicieux.
  • Injection de script malicieux
    Dans une application web, les bonnes pratiques de code ne sont pas toujours respectées par les développeurs, ce qui rend possible l’exploitation de failles de sécurité telles que XSS. C’est également une attaque qui peut permettre d’intercepter la réponse de l’Authorization Server et donc l’access token.
  • Librairie malicieuse/vulnérable
    Des librairies sont couramment utilisées dans les applications web mais les développeurs n’ont pas toujours conscience de ce qui s’y cache. En effet, ces librairies peuvent être malicieuses et avoir le même impact que dans le précédent point. De plus, elles peuvent également se révéler vulnérables et être un point d’entrée pour les attaquants. Par exemple, une librairie d’analyse des flux d’échanges HTTP stockant tout ou partie des échanges, est vulnérable dans la mesure où l’access token peut être récupéré.

Malgré ces vulnérabilités, il existe des solutions pour se prémunir de ces attaques :

  • Les échanges chiffrés en TLS pour éviter d’intercepter l’access token.
  • L’utilisation du paramètre response_mode=form_post dans la requête d’autorisation, en vue d’éviter de recevoir l’access token en paramètre de la query string comme présenté dans cet exemple.
  • Le respect des bonnes pratiques de code en termes de sécurité (voir le guide OWASP) pour éviter les risques d’injection de code malicieux.

Ces solutions réduisent en effet la surface d’attaque mais restent insuffisantes pour éliminer totalement le risque de tirer partie des vulnérabilités de l’Implicit Flow. D’autant plus que les attaques citées précédemment ne sont pas exhaustives, ce qui laisse une grande marge de manœuvre aux attaquants. C’est pour cette raison que l’Implicit Flow est considéré comme vulnérable et a été déprécié dans les nouveaux drafts du protocole OAuth 2.0 (voir le draft sur les bonnes pratiques de sécurité OAuth 2.0), au profit d’un flow plus optimal et sécurisé.

Quelle alternative est possible ?

Pour éviter d’être exposé aux failles de l’Implicit Flow, l’alternative est donc de le remplacer par l’Authorization Code Flow. Contrairement à l’Implicit Flow, il requiert que le Client s’authentifie auprès de l’Authorization Server en vue d’obtenir l’access token. Ceci est rendu possible par un code unique (appelé Authorization Code) transmis par l’Authorization Server lorsque l’autorisation est accordée au Client.

Cela dit, bien qu’on évite la faille de la query string concernant l’access token, on ne peut pas en dire autant pour l’Authorization Code. En effet, c’est dans une query string que sera transmis l’Authorization Code au Client. Ce qui veut dire que l’Authorization Code peut être subtilisé par les mêmes failles évoquées précédemment et par ce biais, l’access token pourra être récupéré dans un second temps.

Pour pallier à ça, il convient d’ajouter l’extension PKCE qui rend la subtilisation de l’Authorization Code insuffisante en vue d’obtenir l’access token auprès de l’Authorization Server. Pour comprendre comment fonctionne PKCE, il faut savoir que l’extension va avoir pour effet d’ajouter trois éléments supplémentaires dans les échanges de l’Authorization Code Flow :

  • Code verifier
    Il s’agit d’un code unique généré aléatoirement et stocké au sein du Client. Il sera transmis lors de l’Access Token Request (voir le schéma ci-dessous) afin que l’Authorization Server s’assure que l’émetteur de la requête est bien le Client ayant initié la demande d’autorisation d’accès. La particularité de ce code est qu’il ne peut pas être anticipé par un attaquant, c’est ce qui permet de renforcer la sécurisation de ce flow. Mais pour cela, le code challenge est indispensable.
  • Code challenge
    Le code challenge est le résultat de la transformation du code verifier. En effet, le code verifier est transformé avec un algorithme défini par le code challenge method. Sa valeur qui devient le code challenge est transmise durant l’Authorization Request en vue de la persister côté Authorization Server pour ce Client spécifique.
  • Code challenge method
    Cela correspond à la méthode de transformation du code verifier. Il peut s’agir par exemple du SHA256.
  1. L’application web (Web App) côté client initie le flow et envoie l’Authorization Request à l’Authorization Server. Avec l’extension PKCE, la requête contient les paramètres code_challenge et code_challenge_method.
  2. L’Authorization Server soumet un formulaire d’authentification au Resource Owner à travers le navigateur (Browser).
  3. Le Resource Owner transmet son identifiant/mot de passe.
  4. L’Authorization Server demande au Resource Owner d’accorder/refuser l’accès du Client aux données protégées (détenues par le Resource Server)
  5. Le Resource Owner accepte d’accorder l’accès.
  6. L’Authorization Server délivre sa réponse (Authorization Response) au navigateur.
  7. Le navigateur redirige vers l’URL contenue dans le header de réponse Location. Cette URL correspond à celle de l’application web avec l’Authorization Code transmis dans la query string.
  8. L’application web envoie une Access Token Request à l’Authorization Server avec le paramètre code_verifier (l’access token sera délivré à la condition que code_challenge_method(code_verifier) = code_challenge). En effet, même en cas d’interception du code_challenge durant l’Authorization Request, le code_verifier ne peut pas être anticipé à l’avance. De cette manière, comme précisé précédemment, cette solution n’est pas vulnérable en cas d’interception de l’Authorization Code ou des codes transmis par PKCE.
  9. L’Access Token Response transmet l’access token.
  10. L’application web envoie sa requête d’accès aux ressources.
  11. Le Resource Server répond en transmettant les données protégées.

Conclusion

L’Implicit Flow est donc obsolète du fait des failles présentes et la solution la plus adaptée consiste à implémenter l’Authorization Code Flow avec l’extension PKCE. D’ailleurs, la prochaine version du protocole, OAuth 2.1, renforcera la sécurisation en supprimant définitivement les flows dépréciés tels que l’Implicit Flow et le Resource Owner Password Credentials Flow. De plus, OAuth 2.1 préconise dorénavant d’appliquer systématiquement la solution présentée dans cet article à la fois pour les Client back-end et front-end.

Cela dit, malgré les mises à jour de OAuth 2.0 et l’arrivée prochaine de OAuth 2.1, l’Implicit Flow continue d’être utilisé à ce jour dans bon nombre d’organisations sans se soucier des failles de sécurité existantes. Donc, en tant que développeur, architecte ou product owner, si vos équipes ont la charge de maintenir des applications soumises à OAuth 2.0 ou OIDC (OpenID Connect), il est recommandé d’éviter l’Implicit Flow si vous souhaitez réduire le risque de rencontrer des failles de sécurité.

Et comment pourrions-nous implémenter l’alternative à l’Implicit Flow ? Pour en savoir plus, un article sera publié prochainement avec un exemple d’implémentation basé sur l’Authorization Code Flow et PKCE.

Ressources

--

--

CARBON

Une communauté de passionnés, spécialisés dans l’expertise technique et les bonnes pratiques de développement web !