Authentification HTTP [veille]

le

HTTP est un protocole déconnecté (même s'il sait réutiliser une connexion déjà établie depuis sa version 1.1). Le mode déconnecté est problématique pour l'authentification : il faut soit s'authentifier à chaque fois, ou tricher et proposer une notion de session, en regroupant un ensemble de connexion sur un laps de temps, en la débutant par une authentification.

HTTP propose un système officiel d'authentification, "auth plain" et "auth digest". En plain, le mot de passe transite en clair, en digest, c'est un hachage qui transite. La sécurité de l'auth digest est illusoire, il faut passer par une connexion SSL pour ne pas avoir de soucis. Auth digest est d'ailleurs tranquillement en train de sombrer dans l'oubli. Ces deux types d'authentifications sont parfaitement gérés par les navigateurs qui font apparaitre deux champs de formulaire par dessus, plutôt que dans la page. Les bibliothèques des langages de programmation le gèrent avec plus ou moins de bonheur, il est souvent plus simple de le faire à la main, en ajoutant un header encodé en base64 de la paire login password, rien d'insurmontable.

La convention de nommage http://login:password@monsite.com , qui permet de caser l'authentification dans l'url n'est qu'une convention, qui sera interprété par le client, puis envoyé dans un header sur le serveur. L'authentification HTTP est un standard facilement géré par les clients, mais il pose des problèmes cotés serveur, en fait. L'authentification se fait à chaque requête. Traditionnellement, le référentiel d'authentification est un simple fichier plat avec de bêtes hachages pour les mots de passe. Le contenu se retrouve rapidement en RAM, plus de soucis de temps d'accès, le temps de calcul du hachage est négligeable, la vie est belle.

Puis viennent la multiplication des serveurs, et le souhait de garder un référentiel commun et cohérent, voire même de pouvoir changer les mots de passe de temps et de bannir les gens qui partent. La réponse officielle est LDAP, mais le LDAP n'est toujours pas arrivé à faire rêver qui que ce soit, malgré ses belles promesses. Il faut donc se rabattre sur quelque chose de plus classique. Tiens, un mysql, c'est connu, facile à bidouiller avec phpmyadmin, et voilà, une requête sql par requête http, images et css comprises, sur une machine qui va devenir un joli goulet d'étranglement. Autres petits soucis historiques, les hachages utilisés sont faibles. Pour des raisons de compatibilité, il y a le grand ancien "crypt" (qui tronque les mots de passe à 8 caractères), mais aussi du SHA1, sans sel, pour faciliter l'usage des rainbows tables et Apache propose gentiment un bricolage autour de MD5, avec une astuce de récursion et du sel. Rien qui ne peut résister bien longtemps à une itération sur un joli GPU.

De toute façon, cela fait longtemps que les applications webs snobent l'authentification HTTP, par ce que leur vrai besoin, c'est l'identification, et que le référentiel utilisateur, elles l'ont, bien rangés dans leur base de données. L'authentification n'est alors qu'une formalité, un simple formulaire web, qui va utiliser la session (et son cookie). Certaines applications, comme Trac, proposent une approche originale, l'authentification est confiée au serveur web, puis l'application crée l'utilisateur en mode lazy, sans utiliser de cookie de session. Dans le même esprit, certains serveurs webs authentifient via un token et ajoute un header pour simuler une authentification HTTP, pour rendre service au serveur d'application qui est derrière.

La solution la plus efficace est donc de faire comme tout le monde, d'utiliser un mode connecté, en commençant par une authentification, puis de conserver cet état un certain temps. C'est la notion de session qui est apparue juste après le bridage des cookies. Une fois authentifié, on confie à l'utilisateur un cookie, qui sert de clef dans une base responsable temporairement du contenu de la session. Pour éviter tous bricolages de la part du client, le cookie utilise une signature et une durée de vie, pour restreindre le nombre de personnes capable de créer un tel cookie. Pour ne pas embêter les développeurs qui font des appels REST, un équivalent dans un header est possible, et pour les flemmasses, un argument en GET peut faire l'affaire.

Ce token peut être partagé par plusieurs sous domaine, on parle alors de SSO (Shibbolet, CAS, LemonLDAP...). Les ambitieux peuvent authentifier différents protocoles avec le même token, sans se restreindre à de l'HTTP (Kerberos ou NTLM). La plupart des SSOs rajoutent des couches en plus, comme les autorisations et l'identification, pour arriver à quelque chose d'assez indigeste, avec des belles couches de LDAP pour lier le tout.

Il est possible de confier l'authentification à un site tiers, comme le fait historiquement OpenId. Bien que répandue, cette technologie a du mal à prendre, et ne corresponds pas forcément à des besoins d'entreprises.

Mozilla propose une solution plus légère, articulée sur le mail comme moyen d'authentification : BrowserID actuellement connue sous le nom de Persona. Cette solution est élégante, facile à mettre en place (des bibliothèques sont proposées pour la plupart des frameworks webs), et le serveur de référence est proposé par un acteur moralement irréprochable. De toute façon, le serveur ne fait pas grand-chose, il signe, valide et envoie des mails. Persona utilise le localstorage du navigateur pour stocker une clef privé (crée en local), le serveur signe le certificat, et sert d'entremetteur avec le site tiers. Persona fournit une explication avec des petits dessins. L'idée est qu'un utilisateur a un mail et un navigateur web récent, Persona fait une soupe avec tout ça, en ajoutant des certificats avec des clefs publics/privés, tout comme SSL. Persona ne propose pas de solution hors navigateur pour l'instant, en proposant un simple service REST pour obtenir un token avant d'aller sur un site nécessitant une authentification. Persona n'est pas prêt pour le REST. Persona est conçu pour proposer une authentification simple et sans mot de passe pour un site public, mais il est tout à fait imaginable d'imposer une restriction sur un domaine de mail, et d'héberger le serveur Persona.

Persona utilise les Json Web Token que l'on retrouve dans différents projets Firefox, dont FirefoxOS. Il semble exister de la concurrence en interne, avec le TokenServer, et si vous n'avez pas encore trop mal au crane, il y a le projet Big Tent à surveiller.

AWS utilise un système plus complexe, avec un système de signature d'url, car avec son ensemble de serveurs mouvant, il ne peut pas utiliser de SSL. Les domaines sont créés dynamiquement, à partir d'un nom choisi par l'utilisateur (le bucket), et on ne sait pas trop sur quel serveur on va taper, avec en plus la possibilité d'être redirigé sur un autre. Bref, pas moyen de faire transiter sereinement un token.

Redmine a une approche beaucoup plus tranquille. En plus de classique "authentication basic", il est possible d'utiliser un token unique, comme header ou comme paramètre d'url. Ce token est disponible sur la page de compte de l'utilisateur.

Il est possible de confier l'authentification à la couche réseau, en utilisant un certificat client dans le protocole SSL. C'est un poil plus complexe à gérer pour l'utilisateur (les navigateurs webs savent le gérer), et il faut que le serveur maintienne une liste des gens bannis. Normalement, on réserve ce genre d'arme lourde pour déclarer ses impôts. Notons ce bel effort de la part du ministère des Finances pour démocratiser les PKI depuis 12 ans.

Donc, pour résumer.

  • Il faut faire circuler les mots de passe au minimum. Plusieurs sites peuvent confier à un seul la charge de vérifier les mots de passe et se contenter de tokens signés.
  • Il ne faut pas faire trainer les mots de passe en clair, dans la transmission (SSL, donc) et le stockage. Il faut les stocker sous forme de hachage avec du sel. HMac est la solution officielle pour ce genre de chose, une astuce pour utiliser la taille complète du hachage.
  • Un serveur doit pouvoir vérifier simplement la validité d'un token. Il peut utiliser un hachage avec un secret partagé, ou une signature avec une clef publique/privée.
  • Il ne faut pas confondre authentification, identification et autorisations, bien que ce soit souvent lié.
  • Un utilisateur doit pouvoir changer son mot de passe ou en réclamer un nouveau. C'est la bonne pratique que l'on oublie volontairement.
  • Il ne faut pas que l'authentification soit un point faible de l'architecture, en terme de performance ou de fiabilité.

Partager cet article :