TL; DR : Quelles sont les implications sur la sécurité de l'utilisation de oauth2 pour l'authentification?
Je crée une application (site A) qui permet aux utilisateurs d'effectuer des opérations sur un autre site Web (site B) via une interface plus simple.
Le site B fournit une API qui implémente OAuth2.0 pour l'autorisation de mon application.
Je pensais que je pouvais éviter de stocker des mots de passe et que les utilisateurs obtiennent un autre compte sur mon application en se superposant à l'authentification du site B.
Bien sûr, j'ai lire le littérature bashing sur le tilisation de plain OAuth pour l'authentification , mais je ne voient pas comment cela est mauvais du point de vue de la sécurité.
Ils semblent se concentrer principalement sur l'aspect pratique et (le manque de) généralité de la solution.
Voici le schéma que j'avais en tête:
Je noterai que j'utilise ici le soi-disant "flux côté serveur". Également authorization_code
renvoyé à la troisième étape est un code à usage unique de courte durée qui est lié à l'identifiant client de A et ne peut être utilisé qu'avec le client_secret
.
Lorsque Sally se connecte à partir d'un autre appareil, le processus se répète et les nouveaux jetons sont stockés.
Le seul problème que je vois est que l'utilisateur sera invité à autoriser mon application à chaque connexion au lieu de la première fois, mais ce n'est pas un problème pour le moment. Je demanderais également un nouveau jeton alors que j'en ai un valide en magasin. Bien qu'un peu impraticable, ce n'est pas un problème pour le moment (*) .
Ce que je ne vois pas, c'est à quel point c'est mauvais du point de vue de la sécurité.
Avec un tel schéma: quels sont les problèmes de sécurité pour l'utilisateur? Et pour l'application?
J'ai l'impression de manquer quelque chose.
(*) Je n'aurai pas une grande base d'utilisateurs, juste quelques utilisateurs sur liste blanche. Lorsque (si) l'application grandit, je prévois de l'intégrer dans un site plus grand qui utilise un véritable fournisseur OpenID Connect. Je veux juste rester simple, petit et concentré pendant ce test pilote
Remarque : Si vous recherchez quelque chose comme OAuth2, mais pour l'authentification, vous devez
utilisez plutôt OpenId Connect .
OAuth2 est destiné à un utilisateur à autoriser une application à charger les ressources de l'utilisateur à partir d'un fournisseur de ressources. En d'autres termes: OAuth2 est un mécanisme pour délégation d'autorisation. Le protocole ne prend pas en charge l'authentification (bien qu'il soit couramment utilisé à mauvais escient pour cela).
Le trou de sécurité est dans l'hypothèse que vous faites au 5ème point.
Vous dites:
Site [~ # ~] a [~ # ~] demande au site [~ # ~] b [~ # ~] pour Sally's
user_id
et la connecte avec cet ID
Alors qu'en réalité, il faut lire:
Site [~ # ~] a [~ # ~] demande au site [~ # ~] b [~ # ~] pour le
user_id
à partir des données utilisateur que leaccess_token
accorde l'accès à.
Figure 1: flux OAuth pour les clients (confidentiels).
Si tout se passe comme prévu, le access_token
est, en effet, de l'utilisateur que vous avez redirigé vers [~ # ~] b [~ # ~] pour l'authentification. Mais: il n'y a aucune garantie que ce soit le cas. En fait, tout site Web (malveillant) auquel l'utilisateur a précédemment accordé le droit d'accéder à ses données (en utilisant OAuth2 avec [~ # ~] b [~ # ~]), peut obtenir un valide authorization_code
de [~ # ~] b [~ # ~] et vous l'envoyer, au point 3.
En d'autres termes, si je gère un site Web qui demande aux utilisateurs leur permission d'accéder à leurs ressources à [~ # ~] b [~ # ~] en utilisant OAuth2, je peux emprunter l'identité de tous ces utilisateurs à tous les sites Web qui utilisent OAuth2 (avec [~ # ~] b [~ # ~] comme serveur d'autorisation OAuth2) pour l'authentification.
Le "problème" avec OAuth2 est que le authorization_code
est pas généré pour un client_id
. Donc, si vous recevez un authorization_code
, vous ne pouvez pas être sûr si [~ # ~] b [~ # ~] a émis le authorization_code
vous avez reçu ou un autre service. Ce qui est jugé acceptable pour autorisation mais est absolument inacceptable pour authentification.
Mise à jour :
Quant à votre commentaire:
(et je restreins A pour n'en accepter qu'un des utilisateurs qu'il a précédemment redirigé vers B, mais ce n'est toujours pas authentifié)
Je pense que vous ajoutez ici une précaution supplémentaire, qui n'est pas obligatoire dans le protocole OAuth. En tant que tel, il ne peut pas être invoqué.
Comme expliqué par Jacco, une implémentation naïve de l'authentification au-dessus de oauth2 comporte plusieurs vulnérabilités, dont la plus courante est CSRF .
Étant donné qu'il existe un protocole d'authentification parfaitement bon sans tous ces pièges, ce n'est pas une bonne idée de lancer le vôtre.
OTOH, il y a beaucoup à apprendre en le faisant et en comprenant et en résolvant ces problèmes.
TL; DR : n'utilisez pas oauth2 pour l'authentification à moins que vous ne le fassiez pour savoir pourquoi vous ne devriez pas le faire. Utilisez OpenID Connect.
Tout d'abord, il y a une analyse approfondie du modèle de menace pour oauth2 dans RFC6819
Il y a plusieurs "flux" possibles dans oauth2. Celui sur lequel je me suis concentré pour mon projet était le authorization_code
couler.
Voici ce que RFC6819 a à dire à ce sujet:
Un "code" d'autorisation représente le résultat intermédiaire d'un processus d'autorisation d'utilisateur final réussi et est utilisé par le client pour obtenir des jetons d'accès et d'actualisation. Les "codes" d'autorisation sont envoyés à l'URI de redirection du client au lieu des jetons à deux fins:
Les flux basés sur le navigateur exposent les paramètres du protocole aux attaquants potentiels via les paramètres de requête URI (référent HTTP), le cache du navigateur ou les entrées du fichier journal, et peuvent être relus. Afin de réduire cette menace, des "codes" d'autorisation de courte durée sont passés au lieu de jetons et échangés contre des jetons via une connexion directe plus sécurisée entre le client et le serveur d'autorisation.
Il est beaucoup plus simple d'authentifier des clients lors de la demande directe entre le client et le serveur d'autorisation que dans le cadre de la demande d'autorisation indirecte. Ce dernier nécessiterait des signatures numériques.
Les codes d'autorisation sont donc plus sécurisés, yay!
authorization_code
les vulnérabilités de flux sont analysées dans section 4.4.1 de RFC6819.
Cette section couvre beaucoup de terrain. Je vais me concentrer sur quelques-unes des menaces.
De la section 4.4.1.8:
Un attaquant pourrait autoriser un "code" d'autorisation sur leurs propres ressources protégées sur un serveur d'autorisation. Il abandonne ensuite le flux de redirection vers le client sur son appareil et incite la victime à exécuter la redirection vers le client. Le client reçoit la redirection, extrait le ou les jetons du serveur d'autorisation et associe la session client de la victime aux ressources accessibles à l'aide du jeton.
Impact: l'utilisateur accède aux ressources au nom de l'attaquant. [...] Par exemple, l'utilisateur peut télécharger des éléments privés vers les ressources d'un attaquant
Ceci est également couvert dans section 10.12 de la RFC6749 :
Le client DOIT implémenter la protection CSRF pour son URI de redirection. Cela est généralement accompli en exigeant que toute demande envoyée au point de terminaison URI de redirection inclue une valeur qui lie la demande à l'état authentifié de l'agent utilisateur (par exemple, un hachage du cookie de session utilisé pour authentifier l'agent utilisateur). Le client DEVRAIT utiliser le paramètre de demande "état" pour fournir cette valeur au serveur d'autorisation lors d'une demande d'autorisation.
Ainsi, dans votre redirection vers le fournisseur oauth2, vous ajoutez simplement un paramètre state
, qui est simplement un jeton CSRF (devrait être impossible à deviner, stocké dans un cookie sécurisé, etc.). Ce jeton sera renvoyé avec le authorization_code
lorsque le fournisseur oauth2 redirige l'utilisateur.
La contre-mesure de cette attaque doit être mise en œuvre à la fois par le client et le serveur d'autorisation, et peut également être appliquée par le serveur d'autorisation.
Le paramètre d'état est également couvert dans cette question sec.SE .
Celui-ci (couvert dans la section 4.4.1.13 de la RFC6819) vise spécifiquement le scénario d'authentification sur oauth2.
Fondamentalement, un attaquant obtient un authorization_code
pour l'utilisateur via un site malveillant (appelons-le site C) et l'envoie au site légitime (que nous appelons toujours le site A) qui l'échange contre un access_token qui est ensuite utilisé pour affirmer l'identité de l'utilisateur via le serveur de ressources. Cela permet effectivement à l'attaquant de se connecter en tant qu'utilisateur sur le site A.
C'est celui mentionné par Jacco dans sa réponse.
La contre-mesure de cette attaque doit être implémentée par le serveur d'autorisation:
Tous les clients doivent indiquer leurs identifiants client à chaque demande d'échange d'un "code" d'autorisation pour un jeton d'accès. Le serveur d'autorisation doit valider si le "code" d'autorisation particulier a été délivré au client particulier. Si possible, le client doit être authentifié au préalable.
Croyez-le ou non, les attaques précédentes et leurs contre-mesures couvrent la plupart des menaces à l'authentification lors de l'utilisation du flux de code.
Il existe de nombreuses autres menaces et contre-mesures, dont beaucoup doivent toujours être mises en œuvre:
De la section 4.4.1.3:
Les jetons basés sur le handle doivent utiliser une entropie élevée Authentifier le client; cela ajoute une autre valeur que l'attaquant doit deviner Lier le "code" d'autorisation à l'URI de redirection; cela ajoute une autre valeur que l'attaquant doit deviner Utiliser un court délai d'expiration pour les jetons
Ceux-ci doivent tous être mis en œuvre par le serveur d'autorisation.
De la section 4.1.1.4:
Le serveur d'autorisation doit authentifier le client Le serveur d'autorisation doit valider l'URI de redirection du client par rapport à l'URI de redirection préenregistré
Ceux-ci doivent également être mis en œuvre par le serveur d'autorisation.
De la section 4.4.1.5, 4.4.1.6 et autres:
l'URI de redirection du client doit pointer vers un point de terminaison protégé HTTPS
Celui-ci doit être implémenté par le client, et probablement appliqué par le serveur d'autorisation.
Non . Ne le fais pas. Utilisez OpenID Connect.
Rappelez-vous les contre-mesures de la section 4.4.1.13? Et bien il y en a un autre que je n'ai pas cité:
Les clients doivent utiliser un protocole approprié, comme OpenID (cf. [OPENID]) ou SAML (cf. [OASIS.sstc-saml-bindings-1.1]) pour implémenter la connexion utilisateur. Les deux prennent en charge les restrictions d'audience sur les clients.
Voilà. Utilisez-le à la place.
Si vous souhaitez/devez toujours vous authentifier auprès d'un fournisseur oauth2, assurez-vous d'abord que votre fournisseur implémente toutes les contre-mesures mentionnées ci-dessus.
Si c'est le cas, vous pourrez peut-être le retirer. Testez en profondeur et embauchez une équipe de sécurité pour effectuer une analyse complète de votre solution.
Assurez-vous également que toutes les fonctionnalités du fournisseur dont vous dépendez pour la sécurité sont documentées dans l'API de votre fournisseur, sinon elles pourraient être supprimées sans préavis et vous vous retrouvez avec un produit Very Broken ™.
Dans mon cas: - J'ai eu la chance que mon fournisseur implémente toutes ces contre-mesures de son côté. - Je ne compte pas sur cela pour l'authentification au-delà d'une période de test initiale de l'application (ce n'est pas une fonctionnalité requise de mon application, juste un pré-lancement d'espace réservé pratique)
En outre, j'ai suffisamment appris sur oauth2 tout au long de cette implémentation pour que cela en vaille la peine.
Si vous voulez en savoir plus, lisez les RFC6819 et RFC6749. J'ai aussi trouvé ce site très utile.
Le trou de sécurité dans votre flux:
Elle est redirigée vers la page d'autorisation du site B, où elle s'authentifie ou a une session active
Si l'utilisateur a une session active sur le site B et que d'autres sites Web (site C, D, etc.) peuvent utiliser le système OAuth du site B pour leur autorisation, un site malveillant (par exemple, site x) pourrait récupérer des jetons d'accès du site B et potentiellement utiliser la relecture de jetons pour usurper l'identité de l'utilisateur sur votre site et effectuer des actions qui n'ont pas été autorisées par l'utilisateur. Relisez l'exemple Facebook dans vos liens, cela explique le problème.
Le défi avec l'ensemble du schéma est que votre site ne sera aussi sécurisé que l'API OAuth du site B. Comme nous venons de le démontrer, leur API n'est pas sécurisée car des tiers peuvent en abuser En cas d'abus, votre seule ressource est de faire appel au site B pour interdire le site x. Personnellement je ne voudrais pas laisser le sort de ma sécurité entre les mains d'un site que je ne possède pas.
Un mécanisme plus sécurisé fournirait l'authentification de votre site par rapport à la liste des clients acceptés OAuth, de préférence à l'aide de certificats clients à clé publique) et empêcherait la relecture des jetons en limitant l'utilisation des jetons au site demandeur. ne peut pas forcer le site B à adopter ce type de schéma d'accès, vous êtes donc limité au niveau de sécurité qu'il offre.