Défi­nir son API : authen­ti­fi­ca­tion


Je lis le PDF gratuit de Apigee à propos du design des API web. Si les autres PDF gratuits du site sont assez creux, celui là pose de bonnes ques­tions qui font écho avec mes propres reflexions.

Je le prends dans le désordre et pour reprendre mes erreurs passées ou celles que j’ai vu chez les autres :

  • Pas de système de session avec point d’en­trée spéci­fique pour le login. Ça demande au client de se préoc­cu­per de l’ex­pi­ra­tion de la session et de son main­tient. Pour des appels isolés ça veut dire faire deux requêtes (login + action) au lieu d’une, avec un délai de réponse finale allongé et une charge plus impor­tante pour le serveur. Sauf besoin spéci­fique, il faut rester en state­less : Chaque connexion contient ses propres infor­ma­tions d’au­then­ti­fi­ca­tion.
  • Pas d’au­then­ti­fi­ca­tion par IP, comme je le vois trop souvent. Outre que c’est un poten­tiel problème de sécu­rité, c’est juste quelque chose de diffi­ci­le­ment main­te­nable et c’est toujours au dernier moment quand on veut faire un correc­tif, une migra­tion ou une bascule vers le serveur de secours en urgence qu’on se rend compte du problème.
  • L’au­then­ti­fi­ca­tion HTTP Digest me semble être une mauvaise réponse à tous les problèmes. Pour amélio­rer légè­re­ment la résis­tance aux inter­cep­tions, il faut stocker le mot de passe en clair côté serveur. Une authen­ti­fi­ca­tion HTTP Basic avec du TLS pour sécu­ri­ser la commu­ni­ca­tion me semble bien plus perti­nent, et aussi plus simple à réali­ser.
  • Le système fait maison est toujours la pire solu­tion, même si vous pensez savoir ce que vous faites. C’est un NO GO commun à toute problé­ma­tique qui touche la sécu­rité. Vous avez plus de chances de vous tirer une balle dans le pied qu’autre chose, et pour le même prix ce sera toujours plus complexe quand vous commu­nique­rez avec des tiers.
  • OAuth 2 a la mauvaise idée d’être plus une boite à outils qu’une solu­tion finie. Même les gros groupes se prennent les pieds dans le tapis avec ça. On rejoint un peu le système fait maison. OAuth a ses défauts, mais globa­le­ment est une sphère contrô­lée que vous devriez préfé­rer.

Au final il reste le choix entre l’au­then­ti­fi­ca­tion HTTP Basic, l’au­then­ti­fi­ca­tion par certi­fi­cat client avec SSL/TLS, ou OAuth 1.0. Ma grille de choix est la suivante :

  • OAuth s’il s’agit d’avoir une authen­ti­fi­ca­tion à trois pattes. Hors de ques­tion d’im­po­ser à l’uti­li­sa­teur final de saisir ses mots de passes dans un logi­ciel tiers. Pour une API qui veut créer un écosys­tème de logi­ciels clients (type twit­ter) c’est le choix quasi­ment imposé. Oui il y a des diffi­cul­tés pour le mobile ou pour ce qui n’est pas « navi­ga­teur », mais ces ques­tions sont désor­mais large­ment docu­men­tées. Pensez bien que choi­sir ce type d’au­then­ti­fi­ca­tion demande un réel travail (par exemple trou­ver l’er­go­no­mie pour permettre à l’uti­li­sa­teur d’au­to­ri­ser et reti­rer l’au­to­ri­sa­tion d’ap­pli­ca­tions tierces sur votre propre système)
  • HTTP Basic par défaut pour quasi­ment toutes les autres situa­tions. C’est simple côté client, simple et maitrisé côté serveur, supporté partout et pour tout, suffi­sam­ment sécu­risé si on passe par du SSL/TLS.
  • Et les certi­fi­cats clients avec SSL/TLS ? C’est une solu­tion qui semble plus inté­res­sante que l’au­then­ti­fi­ca­tion HTTP mais qui se révèle complexe pour pas mal d’in­ter­lo­cu­teurs. La valeur ajou­tée ne semble pas valoir la complexité supplé­men­taire si vous n’in­te­ra­gis­sez pas avec des entre­prises de taille signi­fi­ca­tive. J’y croyais beau­coup, mais fina­le­ment j’ai peu d’ar­gu­ment face à la simpli­cité du HTTP Basic.

Et vous ? vous utili­sez quoi pour l’au­then­ti­fi­ca­tion de vos services ?


18 réponses à “Défi­nir son API : authen­ti­fi­ca­tion”

    • À voir, quand l’éditeur des spec OAuth considère que la v2 est un excellent moyen de se mettre une balle dans le pied et que même les gros se sont planté une ou deux fois côté sécu dans leur implémentation, j’ai tendance à me méfier.

      Il y a un doc de 70 pages lié à la spec, uniquement pour la sécurité. Si tu trouves ça simple, tu as peut être oublié quelque chose. http://tools.ietf.org/html/draft-ietf-oauth-v2-threatmodel-08

  1. Slt,

    Juste une précision, HTTP Digest ne force pas le stockage en clair du mot de passe. Il requiert juste de stocker le mot de passe + le sel sous une forme particulière qui correspond au hash HA1 ( http://en.wikipedia.org/wiki/Digest_access_authentication ).

    Personnellement, j’ai choisi de stocker sous cette forme afin d’être compatible avec Digest tout en gardant la sécurité de mots de passes cryptés : https://github.com/nfroidure/Rest4/blob/master/php/class.RestAuthDigestDriver.php#L59

    Le seul inconvénient, c’est que tu ne peux pas faire varier le sel ou l’algo en fonction des clients. En fait, c’est possible pour l’algo, mais dans ce cas, ça impose de tester les différents algos successivement.

    • Oui mais non. Cf justement le lien dans Wikipedia : https://tools.ietf.org/html/rfc2617#section-4.13
      «  » »The security implications of this are that if this password file is
      compromised, then an attacker gains immediate access to documents on
      the server using this realm. Unlike, say a standard UNIX password
      file, this information need not be decrypted in order to access
      documents in the server realm associated with this file. » » »

      et

      «  » »There are two important security consequences of this. First the
      password file must be protected as if it contained unencrypted
      passwords, because for the purpose of accessing documents in its
      realm, it effectively does. » » »

      En gros, celui qui lit ton mot de passe stocké peut accéder à ta ressource.

      C’est chiffré dans le sens « si ton mot de passe est utilisé pour plusieurs ressources et que le realm utilisé pour les autres ressources est différent, alors l’attaquant ne pourra pas utiliser ce qu’il a trouvé pour accéder aux autres ressources ». Reste que toi tu l’as bien dans l’os. Il est très probable que ce n’est pas la sécurité que tu attendais, et probablement pas celle dont tu as besoin.

    • C’est à ça que sert le paramètre nonce que tu envois avec la demande d’authentification et que tu peux changer à chaque requête si tu veux. Mais ce dernier n’entre pas en compte pour le calcul de HA1.

      Dans les faits, je n’ai pas encore trouvé la solution idéale car c’est pas vraiment « stateless », tu dois bien stocker ce nonce quelque part si tu veux que la requête d’authentification soit comprise, mais c’est bien possible.

    • Je ne sais comment mieux préciser mais si tu veux vérifier l’égalité des mots de passe tu peux :

      * faire un hachage chez toi, récupérer une entrée en clair (ou déchiffrable), et refaire le procédé de hachage pour comparer

      * récupérer une version hachée, stocker en clair (ou déchiffrable), et refaire le procédé de hachage pour comparer

      * récupérer la même chose en distant et en local (ou déchiffrable de façon à ce que ça donne la même chose) et comparer

      * On fait un système de signature avec chiffrage asymétrique (type RSA) et un challenge

      * On utilise des mots de passe à usage unique (spip fait un truc de ce type, assez intelligent s’il n’y a pas de concurrence d’accès au login)

      Le HTTP Basic est la première option. Le défaut de chiffrage du transport est compensé par TLS.

      Le HTTP Digest est la seconde option. Il nécessite que le local soit déchiffrable. Pour compenser on considère que la clef n’est pas le mot de passe source mais un hachage avec le mot de passe source et le realm, ça ne change rien à la sécurité de la ressource elle-même (vu que ce qui permet l’accès est la clef, pas le mot de passe source) mais ça évite de griller d’autres ressources qui auraient le même mot de passe.

    • Re,

      La démo vite fait :). Une authentification de type digest est basée sur la vérification d’une valeur nommée « response » dont la formule est :
      HA1 = MD5(username:realm:password)
      HA2 = MD5(method:digestURI)
      response = MD5(HA1 : nonce : HA2)

      Tu stockes en db HA1 ce qui implique que username, password, realm sont et restent toujours les mêmes.

      Quand le client fait une demande :
      GET /dir/index.html HTTP/1.0
      Host: localhost

      Tu lui envoie :
      HTTP/1.0 401 Unauthorized
      Server: HTTPd/0.9
      Date: Sun, 10 Apr 2005 20:26:47 GMT
      WWW-Authenticate: Digest realm= »MonRealm »,
      qop= »md5″,
      nonce= »dcd98b7102dd2f0e8b11d0f600bfb0c093″,
      opaque= »5ccc069c403ebaf9f0171e9517f40e41″

      Le client te renvoie :
      GET /dir/index.html HTTP/1.0
      Host: localhost
      Authorization: Digest username= »Mufasa »,
      realm= »MonRealm »,
      nonce= »dcd98b7102dd2f0e8b11d0f600bfb0c093″,
      uri= »/dir/index.html »,
      qop=auth,
      nc=00000001,
      cnonce= »0a4f113b »,
      response= »[valeur de response] »,
      opaque= »5ccc069c403ebaf9f0171e9517f40e41″

      Valeur de response vaut :
      response = MD5(HA1 : nonce : HA2)

      Tu récupère HA1 dans la base de donnée, tu reconstitues response avec la valeur nonce du client et la valeur de HA2, tu hashes et tu compares les hash.

      Puisque le nonce est envoyé par tes soins à la demande d’authentification, response n’aura jamais la même valeur pour peu que tu prennes cette précaution. Tu peux, par exemple, baser nonce sur un timestamp unix valide
      5 minutes après envoi.

      Au final, tu as bien : un mot de passe hashé dans ta base de donnée ET une valeur de Authorization non réutilisable en cas d’interception par un tiers malveillant.

    • On revient à la question essentielle « Quel est le mot de passe ? »

      Du point de vue utilisateur le mot de passe est une chaîne de caractère qui sert à calculer HA1. Du point de vue sécurité le vrai mot de passe c’est directement HA1 : Si tu connais HA1 tu peux t’authentifier sur le service (tu lances une requête pour récupérer un nonce et tu fais ton MD5(HA1:nonce:HA2)). L’attaquant n’a pas besoin de connaitre le mot de passe utilisateur.

      Au final tu as bien sécurisé ton transport (celui qui intercepte ne peut pas se réauthentifier plus tard si tu empêche bien les attaques par replay) mais c’est au prix de retirer toute sécurité sur ton stockage local. Vu que tu stockes directement HA1 dans ta base et que ce HA1 est le seul mot de passe réel du point de vue sécurité, tu viens en fait de stocker ton mot de passe en clair dans ta base.

      Le problème de cette authentification digest c’est qu’elle résout le mauvais problème. Pour sécuriser le transport TLS fonctionne relativement bien et répond au problème. L’essentiel des problèmes est plus lié au stockage local, et là tu es à poil.

      La solution auth basic avec hachage du mot de passe sur le stockage serveur + TLS pour le transport n’est pas parfaite, mais elle répond bien mieux aux problématiques standard qu’on retrouve sur le web.

    • Je remets 10 francs ;).

      Si je compromets ton serveur avec authentification basic, rien ne m’empêche de sniffer les connexions HTTP pour récupérer les champs Basic ce qui limite encore l’intérêt de Basic même avec TLS. En effet, il est possible d’intercepter le mot de passe en compromettant :
      – le client
      – la couche réseau
      – et le serveur.

      De plus, les conséquences sont plus grandes car elle ne se limitent pas au site hacké.

      Pour Digest, la compromission ne peut être réalisée qu’au niveau du serveur et se limite au serveur lui même ce qui est pas trop dérangeant car vu que le serveur est compromis, un reset des mots de passe s’impose de toute façon.

      Reste le problème des sauvegardes de la base qui peuvent être plus facilement divulguées par inadvertance. Bref, no silver bullet dans notre cas, à chacun de choisir la moins pire en âme et conscience.

    • * le client : si on compromet le client, tu es toujours dans la merde, quelle que soit ton authentification (sauf à déporter le calcul de la clef en dehors du client, avec un token externe par exemple)

      * la couche réseau : côté http basic, la gestion est faite avec TLS – si ton client n’accepte pas n’importe quel certificat, tu es normalement correctement couvert (il y a toujours des failles dans les chaînes de certificat, mais si tu n’es pas un top100 international ou une organisation qui craint l’espionnage des gouvernements, normalement c’est plutôt bon)

      * le serveur c’est toute la question. Si la personne arrive à intercepter tout le réseau en entrée ou contrôle toute ta machine tu es de toutes façons foutu. La question c’est en général de pouvoir limiter la casse sur des failles plus limitées. Le cas très classique c’est l’accès en lecture à du stockage de mot de passe. C’est plus que théorique car on voit passer régulièrement des news de ce type. La solution du http basic permet de palier ça.

      Mais oui, il s’agit bien de faire une pondération des risques, suivant si tu souhaites plus protéger le réseau ou le serveur.

    • Pour l’attaque man in the middle, elle est très fréquente en entreprise. L’idée est qu’il mettent leur propres certificats racines dans le navigateur de leur employés et pour le coup, les salariés ne sont même pas au courant car peu au fait des problèmes de certificats. Ce genre de truc peuvent faire que si une personne accède à ton API depuis une boîte, son directeur peu avoir accès à son profil Facebook pour lequel elle a le même mot de passe.

      Pour ça, de mémoire, il y a une extension Firefox qui permet justement d’être mis au courant quand c’est pas le même certificat racine qui a été employé que d’habitude.

  2. Bonjour,

    j’aime bien la réflexion ici.

    J’ai bien compris Nicolas Froidure, personnellement, j’ai aussi un problème avec le stockage du « mot de passe » coté client et c’est là que j’aimerai bien comprendre edaspet.
    «La solution auth basic avec hachage du mot de passe sur le stockage serveur + TLS pour le transport n’est pas parfaite, mais elle répond bien mieux aux problématiques standard qu’on retrouve sur le web.»
    Ici, j’ai du mal, Auth Basic doit bien recevoir un mot de passe qui doit être stocké sur le client (c’est lui qui l’envoi)?
    En quoi c’est différent HTTP Digest hormis l’aspect « plus complexe » des échanges qui sécurise au final que le transport.

    • Le client connait forcément le secret (mot de passe). C’est la base de l’authentification (c’est la connaissance du secret qui permet de différencier la personne légitime de l’usurpateur). Rien ne change ici quelle que soit la méthode.

      La différence c’est que sur l’auth basique le serveur lui n’a pas besoin de stocker le secret sous une forme accessible. Il reçoit le secret en clair (modulo TLS) et peut donc lui appliquer un hachage (sens unique) pour le confronter avec sa version hachée stockée en local. Celui qui a accès à la version hachée du serveur ne peut pas s’en servir pour s’authentifier vu que ça ne lui permet pas de connaitre le secret à envoyer sur le réseau (sauf à tester toutes les combinaisons jusqu’à en trouver une qui donne le même résultat).

      Sur l’auth digest c’est l’opération inverse qui est faite : le secret est stocké en clair sur le serveur. Ce dernier reçoit une version hachée qu’il ne peut décoder alors il hache sa propre version pour la confronter avec celle reçue. Celui qui a accès à ce qui est stocké sur le serveur peut s’en servir pour générer une authentification par le réseau.

      Est-ce plus clair ?

    • Impect, merci beaucoup.

      Ça répond parfaitement à ma question.
      Même si le digest me semble indigeste pour le coup…

      Pour résumé
      – le serveur est hacké => qui est compromis?
      Basic = personne
      Digest = tout le monde
      – le client est hacké => qui est compromis?
      Basic = le client
      Digest = personne

      Evidemment, le serveur est sensé être la forteresse imprenable et là, le Digest est parfait car le client (qui est logiquement plus facile à compromettre) n’est pas une « faille »

      En tout cas merci à vous deux, j’ai sous estimé les aspects sécurité et question qui pouvait ce poser (pour moi SSL/TLS c’était suffisant, alors que ça ne couvre qu’une couche réseaux)

      Personnellement, je préfère le Basic (plus simple, mais pas plus simplicite)

  3. Hello,

    Et pour l’auth des utilisateurs sur des applications externe ?

    L’auth basic a une limit, l’application connais le mot de passe…

  4. Bien vu, il ya bien une sécurité plus faible en cas de compromission du serveur cependant la compromission se limite au site lui même. L’attaquant ne pourra pas accèder à d’autres sites pour lesquels les utilisateurs auront stocké le même mdp.

    En revanche, pour basic, en cas d’attaque man in the middle, la sécurité du mot de passe lui même est compromise. Du coup, la question originale devient : faut-il faire plus confiance au serveur ou a la couche réseau.

    • le man in the middle de l’authentification basic est normalement gérée par la couche TLS justement

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.