OpenID

Introduction (fr)

Main Page (fr) > Introduction (English)


Contents

Introduction

Je ne suis pas un génie. En essayant de mettre en œuvre un serveur OpenID fondé sur Lua, je me suis trouvé perplexe face aux deux références principales. Le flux décrit dans la spécification officielle était d'un niveau trop fort pour que j'y perçoive clairement le flux du protocole et les implications pour la sécurité. Après avoir enfin terminé la première passe de mon serveur OpenID Lua, j'ai pensé qu'il serait utile de coucher par écrit ce que j'avais appris sur le protocole en chemin.

Cette page est destinée à ceux qui veulent tout comprendre du flux de protocole sans avoir à faire des références croisées entre l'interface de programmation (API) de la spécification officielle et une mise en œuvre pour réaliser ce qui se passe.

Acte I Scène 1

Pour commencer, attribuons nos noms obscurs favoris aux acteurs de cette pièce de théâtre numérique qu'est OpenID. « Alice » sera notre utilisateur final. C'est notre utilisateur moyen qui navigue sur le Web avec son navigateur préféré Operfox Explorer. « Operfox Explorer » est l'agent utilisateur, le programme qui s'occupe de toutes les pulsions de cliquage, de saisie et autres besoins d'Alice. Alice vient tout juste de finir de lire la dernière entrée sur le blogue de son oncle et elle souhaite laisser un commentaire. Son oncle « Bob » a récemment mis à jour son site (Le blogue de Bob) pour utiliser OpenID. Il a payé un étudiant 20€ pour qu'il intègre un consommateur OpenID au système de commentaires de son blogue, et maintenant le blogue de Bob peut utiliser OpenID. Par chance, Alice est déjà un utilisateur enregistré auprès de « Carol », son site de blogue favori. Carol est quelqu'un dans le monde de OpenID et elle fournit à Alice une adresse URL « http://carol.example.com/Alice », qu'elle peut utiliser comme identité OpenID. Carol est une magicienne de la technique et elle a configuré les choses de telle sorte que cette adresse URL d'identité puisse être vérifiée au travers de son serveur OpenID.

Une dernière chose, Alice a une sœur jumelle : « Eve ». C'est une vrai farceuse qui aime jouer des tours à Alice et Bob. Son tour favori est de laisser des commentaires sur le blog de Bob, en se faisant passer pour Alice, pour les faire tourner en bourriques. Ni Alice ni Bob apprécient vraiment ce comportement, et ils voudraient y mettre un terme une fois pour toutes. La seule raison ayant poussé Bob à dépenser 20€, il a lu sur le Web qu'OpenID pourrait empêcher Eve de jouer ses tours. Il nous faut donc la surveiller aussi.

Récapitulons l'histoire :

Alice : utilisateur final
Operfox Explorer : agent utilisateur
Bob   : consommateur
Carol : serveur
http://carol.example.com/Alice : identité
Eve   : invitée surprise, ou attaquant malvaillant

Tandis que rideau tombe, Alice termine tout juste de taper son commentaire à Bob, et regarde un écran où elle est invitée à donner une identité OpenID pour s'authentifier sur le site de Bob.

Que se passe-t-il lorsqu'Alice entre son identité OpenID ? Comment Bob peut-il vraiment savoir que c'est bien Alice l'auteur du commentaire (et non Eve) ? Qu'est-ce que Carol a-t-elle bien pu faire pour que tout soit comme ça ? Où puis-je télécharger une copie de Operfox Explorer ? Toutes ces questions et d'autres vont recevoir une réponse !

Qui est passif !?

Le type payé par Bob pour monter un consommateur OpenID était plutôt fainéant. Il a dit à Bob que cela lui prendrait une semaine, a touché 10€ d'accompte et joué à Counter-Doom II pendant six jours. Le dernier jour, il a concocté en vitesse une page Web sans état avec un formulaire sans fioritures, attrapé les 10€ restants, et il est parti s'acheter des frites et six canettes chez l'épicier au coin de la rue.

Bob a hérité d'un consommateur passif. Non, sérieusement. D'après le protocole OpenID, son consommateur fonctionne dans le mode passif (N.d.T. dumb mode). C'est-à-dire que le consommateur est incapable de suivre un état. Ça va quand même, OpenID s'en charge. En fait, le type était si paresseux qu'il ne s'était pas embarrassé de JavaScript ni de fantaisie. Quelques scripts CGI collés sur le serveur et ça roulait. Non seulement son consommateur était passif mais il était aussi de la vieille école.

La paresse du gars est notre chance puisque nous pouvons voir ce qu'il se passe dans ce cas le plus simple.

Dans le terrier

Toute excitée, Alice tape son identité OpenID et clique sur le bouton « Authentifier ». Son écran commence à onduler sous l'action magique du septième domaine planaire de Cartésie, où Lord Bitrot faisait vaciller les démons mineurs de...

Attendez, ce n'est pas ça. Que se passe-t-il donc ? Et bien, Operfox Explorer traite le formulaire de commentaire de Bob et le soumet aux scripts CGI écrits par le fainéant pour le consommateur. Un script nettoie l'adresse URL d'identité fournie par Alice, la rend canonique, puis s'achève en récupérant le document à cette adresse. Absolument rien de spécial ici. Juste une bonne vieille requête HTTP de fichier HTML ordinaire. Relevez vos manches, la magie commence ici.

Une fois que le consommateur a obtenu le fichier HTML à l'adresse URL d'identité fournie, il le parcourt à la recherche d'un balisage spécifique dans la section head :

<link rel="openid.server" href="http://carol.example.com/openid-server.cgi">

Dans le cas d'Alice, le fichier HTML, à son adresse URL d'identité OpenID, contient un lien vers le serveur OpenID du site de Carol. Tel quel, le consommateur de Bob n'est vraiment pas si brillant que ça, et il a tendance à éviter le travail comme la peste. Maintenant qu'il connaît l'adresse URL du serveur OpenID de Carol, il va se décharger d'autant de travail que possible. En utilisant une redirection, il envoie le navigateur Operfox Explorer d'Alice tout droit sur le serveur OpenID de Carol, fournissant quelques paramètres supplémentaires en passant. En particulier, le navigateur d'Alice est redirigé sur http://carol.example.com/openid-server.cgi, avec les paramètres GET suivants :

openid.mode = checkid_setup
   C'est l'un des modes OpenID possibles.
   Ce mode indique que l'on veut vérifier
   une identité, que nous passons le
   contrôle de l'agent utilisateur au
   serveur pour cela. Nous attendons le
   retour du serveur dès qu'il a fini.

openid.identity = http://carol.example.com/Alice
   C'est l'adresse URL fournie par Alice.
   C'est ce que Bob veut vérifier, qu'elle la
   possède réellement, via Carol.

openid.return_to = http://bob.example.com/comment.cgi?
                   session_id=Alice&nonce=123456
   C'est là où Bob veut que le navigateur
   d'Alice aille après que Carol a fini
   de l'authentifier. Bob attend de Carol
   qu'elle colle des infos supplémentaires
   à cette adresse URL pour valider
   l'identité revendiquée par Alice.

Eve entre en scène

Bon, voyons de plus près ce paramètre openid.return_to. C'est là où Bob veut qu'Alice aille après que Carol l'aura authentifiée. Quelque part dans son programme de blogue, Bob attache le session_id d'Alice à son commentaire en attente, ou à tout autre chose qu'elle essaye de faire. Nous ignorerons juste le fait qu'il s'agit d'un horrible identificateur de session, et continuons : après tout, nous ne sommes pas ici pour critiquer le logiciel de blogage de Bob. Outre l'adresse URL de retour qu'il a déjà fournie, Bob attend d'autres informations que Carol aura accrochées, lorsque tout est dit et fait, pour être sûr d'être sur la bonne voie.

Mais il nous faut plus qu'un simple identificateur de session. Si Eve est prête à jouer (et elle l'est), tout ce qu'elle aurait à faire pour usurper l'identité d'Alice serait d'écouter entièrement cette conversation, de l'enregistrer, puis de rejouer le même échange de messages plus tard. Même s'ils sont signés et scellés par chiffrement et qu'elle ne puisse pas voir ce qu'il y a dedans, elle sera toujours capable de prendre l'identité d'Alice. Il nous faut donc autre chose pour être sûrs qu'Eve ne puisse pas le faire, et c'est le paramètre nonce supplémentaire. Le consommateur de Bob place un identificateur aléatoire unique dans le paramètre URL return_to de chaque requête d'authentification effectuée. Grâce au nonce, cette adresse URL return_to change à chaque requête d'authentification faite par Bob, et donc Eve ne peut plus simplement rejouer la séquence de messages. Tant que la signature de chiffrement des réponses ultérieures sert aussi pour signer cette adresse URL, la relecture d'une session précédente par Eve échouera parce que le nonce de la nouvelle requête d'authentification sera différent de celui utilisé dans la requête qu'elle essaye de rejouer.

Revenons à Carol

Maintenant Carol. Elle a un travail à faire... Bob lui a demandé de valider le fait qu'Alice possède bien l'adresse URL qu'elle prétend posséder, et il a renvoyé l'agent utilisateur d'Alice vers Carol afin que tout cela puisse être démêlé. Carol a donc le contrôle de l'agent utilisateur d'Alice et elle doit authentifier qu'il s'agit bien d'Alice. Comment Carol s'y prend-elle ?

ON NE LE SAIT PAS. Et on ne veut pas le savoir non plus. Alice et Carol devront le déterminer elles-mêmes. Carol a l'agent utilisateur d'Alice, elle peut donc faire ce qu'elle veut. Carol dispose peut-être d'un système de chiffrement à clé publique avec authentification forte (N.d.T. two factor authentication) et des lecteurs de cartes à puce (N.d.T. smart card) et un scanner d'ADN (N.d.T. DNA scanner). Ou peut-être a-t-elle un singe attaché à son serveur OpenID qui dit au hasard "valid" ou "invalid". Pour nous, c'est une boîte noire. Ce n'est pas notre problème.

Étrange n'est-ce-pas ? Mais c'est une partie de la mission d'OpenID : il s'agit d'identité (N.d.T. identity) et non de confiance (N.d.T. trust). Tout ce que nous savons, d'après Carol, c'est qu'Alice est celle qu'elle dit être. Si nous n'avons pas confiance en Carol, alors nous ne pouvons vraiment pas croire ce qu'elle a à dire au sujet d'Alice, non ? Mais c'est bon. Ce dont nous sommes SÛRS c'est qu'Alice est bien la même Alice que Carol dit toujours être, à supposer que Carol soit cohérente à ce sujet. Plus encore, Alice ne peut pas prétendre être quelqu'un qu'elle n'est pas, tout au moins sans l'aide de Carol. Et quand bien même, elle peut seulement prétendre être quelqu'un déclarant aussi Carol comme son serveur d'identité officiel. Si les choses devaient aller mal, nous cesserions probablement de prêter attention à Carol, comme quiconque aurait tenté de l'utiliser comme serveur d'identités.

Donc au travers de comptes d'utilisateurs soigneusement mis en œuvre, ou bien au hasard sur un jet de dés, Carol décide qu'il s'agit vraiment d'Alice à l'autre bout de l'agent utilisateur. Maintenant, Carol doit juste convaincre Bob que c'est bien Alice à l'autre bout et le convaincre aussi que c'est bien elle, Carol, qui essaye de le convaincre. Facile, non ? La première étape de ce processus de persuasion consiste pour Carol à renvoyer l'agent utilisateur d'Alice à l'adresse URL return_to de Bob, chargé d'autres paramètres. Alice est donc réexpédiée à http://bob.example.com/comment.cgi?session_id=Alice&nonce=123456, avec les paramètres GET supplémentaires suivants joints par Carol :

openid.mode = id_res
   Carol affirme par cette valeur qu'Alice
   possède réellement cette identité.
   Elle pourrait valoir "cancel" indiquant
   qu'Alice a décidé ne pas vouloir continuer,
   mais c'est un détail ennuyeux.

openid.return_to = http://bob.example.com/comment.cgi?
                   session_id=Alice&nonce=123456
   La même adresse URL de retour envoyée
   par Bob à Carol, reprise pour lui.

openid.identity = http://carol.example.com/Alice
   À nouveau l'identité URL qu'Alice prétend
   posséder et laquelle Carol confirme.

openid.signed = mode,identity,return_to
   C'est la liste de paramètres pour lesquels
   nous allons fournir une signature.
   Nous signerons bien sûr le mode et l'identité
   à authentifier, mais également l'adresse URL
   return_to en prévention des attaques par
   répétition, ce dont nous avons déjà discuté.

openid.assoc_handle = descripteur opaque
   Nous verrons cela plus en détails après.

openid.sig = signature HMAC codée en base 64
   Nous le verrons aussi.

Houla! Qu'est-ce que c'est que ces incantations ajoutées à cette adresse URL de retour ? La plupart des éléments sont assez parlants mais les deux que nous voulons examiner de plus près sont assoc_handle et sig(nature). Le premier (assoc_handle) est défini simplement dans la spécification OpenID comme étant un « descripteur opaque » (N.d.T. opaque handle). C'est quoi un descripteur opaque ? Et bien c'est essentiellement un descripteur vers un secret. Un secret cryptographique. Carol doit pouvoir prendre ce descripteur opaque et chercher, en interne, quel secret elle a utilisé pour signer cette assertion pour Bob. Carol se charge de tout, mais elle doit faire une chose : différencier les descripteurs assoc_handle normaux de ceux sans état. Nous verrons pourquoi plus tard, pour l'instant il est seulement question des descripteurs sans état (car Bob est un consommateur passif, c'est-à-dire sans état, vous vous souvenez ?)

Donc Carol arrange son secret et l'attache à assoc_handle, mais pour quoi faire le secret ? Et bien, il sert à créer le second élément d'importance : la signature. Carol claque les deux champs à signer (dans notre cas, les paramètres mode, identity et return_to), puis lance l'algorithme de signature HMAC-SHA1 par-dessus, en utilisant le secret attaché à assoc_handle comme clé de chiffrement pour HMAC. Cela produit une signature qu'elle code alors en base 64, à renvoyer comme paramètre à l'adresse URL return_to de Bob.

En avant la musique Bob ! Ou pas encore ?

Super ! Bob a maintenant une affirmation signée selon laquelle Alice est bien qui elle prétend être, et tout est pour le mieux, non ? Bon, il y a encore des détails à régler. Premièrement, Bob ne peut pas vérifier lui-même la signature en réalité. Il ne connaît pas le secret après tout. Il n'a que ce descripteur opaque dont il ne sait pas réellement quoi faire, et une signature qui n'est peut-être pas valide. Deuxièmement, tout ça vient droit de l'agent utilisateur d'Alice de toute façon, et les octets dans leur ensemble sont peut-être complètement inventés. Bob n'a vraiment aucun moyen de savoir à ce stade si Carol a quelque chose à voir avec tout ça. Alors que fait-il ?

Pourquoi cette question, Bob refourgue le tout à Carol bien sûr ! Je vous l'avais bien dit que le consommateur de Bob était paresseux. Ainsi, le script CGI sur lequel Alice avait rebondi sur le site de Bob (http://bob.example.com/comment.cgi) va ouvrir une session HTTP une dernière fois avant de croire quoi que ce soit de ce que dit Alice. Cette session HTTP a lieu côté serveur dans au sein du script de consommateur, l'agent utilisateur d'Alice n'a rien à y voir. Bob envoie une requête POST à http://carol.example.com/openid-server.cgi avec les paramètres suivants :

openid.mode = check_authentication
   Cela indique à Carol que nous voulons confirmer
   ce qu'Alice a dit de ce que Carol disait.

openid.signed = mode,identity,return_to
   La liste d'éléments qu'Alice déclare
   avoir été signée par la signature.

openid.assoc_handle = descripteur opaque
   Le même descripteur fourni par l'agent utilisateur
   d'Alice à l'adresse URL return_to de Bob.

openid.sig = signature HMAC codée en base 64
   La signature qu'Alice déclare soutenir
   son identité.

openid.* = tout le reste
   Tout ce qui se trouve dans notre liste
   d'éléments signés (moins le mode).
   Dans ce cas, il nous faut renvoyer les
   paramètres identity et return_to que
   nous faisons rebondir depuis
   un moment déjà.

Dès que Carol reçoit cette requête, elle va refaire tout le travail qu'elle avait déjà accompli, depuis le début encore, juste pour faire plaisir à Bob. Elle construira à nouveau la liste des paramètres signés, consultera le secret qu'elle avait attaché au descripteur assoc_handle fourni, et créera une signature HMAC-SHA1 des paramètres en utilisant ce secret comme clé de chiffrement, tout comme elle l'avait fait auparavant. Puis elle comparera cette signature à celle fournie par Bob dans son message. Si elles correspondent, alors Carol saura qu'elle a envoyé la déclaration originale (ou alors, quelqu'un connaissant son secret) et elle retournera un fichier en clair à Bob avec sa réponse définitive : "is_valid:true".

Évidemment, si les signatures ne correspondent pas (ou si ce n'est pas un descripteur assoc_handle sans état), Carol reviendra à Bob avec "is_valid:false", et Bob saura que quelqu'un essaye de le tromper. Peut-être Eve, ou Alice, ou le fainéant qui a écrit son consommateur. 20€ ? On peut faire moins cher ?

Bob nettoie la maison à l'AJAX

AJAX ! Le même vieux truc avec un nouveau nom. Bref, accroche tendance ou pas, Bob est assis dans son café Internet favori songeant combien son blogue est génial, ravi qu'Eve ne puisse plus lui jouer de tours, lorsqu'il entend deux développeuses Web à la table d'à-côté parler d'AJAX. Curieux, il tend l'oreille et commence à se demander si son authentification OpenID ne pourrait pas être un peu plus extraordinaire. Après tout, son dispositif actuel redirige l'agent utilisateur en boucles et c'est un peu ennuyeux quand tout ce que vous voulez faire est signer un simple commentaire.

Toujours opportun, Bob offre spontanément 50€ aux deux développeuses pour adapter son authentification OpenID en utilisant AJAX. À la pointe de la technique, elles connaissent bien sûr tout sur OpenID et concluent rapidement l'affaire. À peine 43 minutes plus tard (dont 10 à attendre que Bob se souvienne du mot de passe de son site de test), son authentification de commentaires de blogue était aussi moderne et fluide que tout ce qu'il aurait pu espérer. Quels étaient donc les secrets de ces femmes qui leur valaient de gagner rapidement de l'argent ?

Aucune magie noire chez ces dames. Non, elles font juste appel à une autre commande du protocole OpenID : "checkid_immediate". Tout comme sa cousine "checkid_setup", cette valeur du paramètre openid.mode est envoyée au serveur OpenID de Carol avec toute la garniture pour tenter d'authentifier une identité OpenID. En fait, elle est exactement comme l'autre commande et elle est envoyée dans le flux pareil, avec une seule différence de comportement. Comme son nom l'indique, "checkid_immediate" revient toujours immédiatement sans prendre le contrôle de l'agent utilisateur. Si le serveur peut effectuer l'authentification à la volée, alors tout peut continuer normalement, et le consommateur doit poursuivre avec une commande "check_authentication".

Par contre, si le serveur OpenID de Carol ne peut pas confirmer l'identité, alors "checkid_immediate" revient instantanément avec une assertion fausse et un seul paramètre openid.user_setup_url, au lieu de prendre le contrôle de l'agent utilisateur. À ce stade, le consommateur peut décider de la suite. Il peut rediriger l'agent utilisateur à l'adresse openid.user_setup_url fournie, ou le faire dans une fenêtre surgissante (N.d.T. popup), ou commencer à jouer une chanson des Beatles, ou quoi que ce soit. Mais le point capital est que le consommateur contrôle le comportement de l'agent utilisateur, qui rend cette commande appropriée pour une utilisation asynchrone dans une architecture découplée de type HTTPRequest.

Et c'est exactement ce que fort à propos les deux développeuses ont fait au blogue de Bob. Lorsqu'un utilisateur essaye d'authentifier son commentaire, au lieu de soumettre directement le formulaire au serveur OpenID via l'agent utilisateur, on utilise du JavaScript pour ouvrir une requête HTTPRequest vers le serveur OpenID à la place. Et au lieu d'envoyer un mode "checkid_setup", qui implique une prise de contrôle de l'agent utilisateur, on envoie un mode "checkid_immediate". Cela permet d'utiliser une fenêtre surgissante avec l'adresse openid.user_setup_url obtenue (peut-être) en réponse, où l'utilisateur peut conclure les étapes nécessaires auprès de son serveur OpenID pour que l'authentification ait lieu. Faites suivre d'une autre requête HTTPRequest pour envoyer la commande "check_authentication", et le processus entier se sera déroulé sans déranger l'agent utilisateur le moins du monde.

C'était pas 50€ faciles ça ? Et pour le coup nous n'avons même pas fait venir Alice !

Bob a besoin d'un prêt

Le blogue de Bob est si bien fait et facile à utiliser avec son authentification OpenID qu'il est devenu très populaire. En fait, il a beaucoup de mal à tenir le cap avec toutes les requêtes générées par Alice et ses nombreux amis. Tandis qu'il s'arrache les cheveux à se demander comment payer les factures pour l'hébergement et les coûts en bande passante, un bel après-midi d'été, Carol lui laisse un courrier se plaignant d'Alice et ses copains en train de saturer les unités centrales de son serveur OpenID à générer toutes ces signatures et ces secrets, et quoi d'autre encore. Bob se dit qu'il doit bien exister un moyen de réduire le flot des allers-retours de tous ces messages d'authentification, et peut-être épargner un peu de boulot au pauvre serveur de Carol aussi.

Il décide d'appeler les deux développeuses Web rencontrées auparavant pour voir si elles pourraient y faire quelque chose. Il leur offre 200€ (la moitié de ce que lui coûte le blogue tous les mois) pour réduire le trafic d'authentification. Elles promettent de faire de leur mieux en essayant de contenir leurs ricanements. Pourquoi sont-elles si joyeuses ? La perspective d'un salaire rapide et facile, en utilisant des descripteurs avec états et le mode "associate" d'OpenID.

Ne repoussez pas au lendemain... Associez !

Il est grand temps que les scripts du consommateur OpenID de Bob assument leur part du travail, et c'est exactement ce que nos héroïques développeuses Web ont l'intention de faire. Voyez-vous, si le consommateur de Bob pouvait se souvenir de ce qu'il était en train de dire juste un petit instant, cela épargnerait beaucoup de travail à Carol, et aux deux beaucoup de bande passante. L'idée de base est simple : en mode passif, le consommateur de Bob ne fait que répéter comme un perroquet le descripteur sans état qu'on lui a donné, et il ne sait en fait jamais quel secret ce descripteur est censé représenter. Et si Bob pouvaient réellement se souvenir du secret partagé que Carol et lui utilisent, et le réutiliser pendant une quantité de temps raisonnable à chaque fois qu'il a besoin de parler à Carol ? Qu'est-ce que cela nous apporterait ?

En fait, il se trouve que c'est possible et ça aide beaucoup. C'est le principe du « mode intelligent » (N.d.T. smart mode). En mode intelligent, le consommateur de Bob établit au préalable un secret partagé avec le serveur de Carol, et il s'en souvient pour une période de temps raisonnable. Il établit ce secret partagé en envoyant une requête POST au serveur de Carol (http://carol.example.com/openid-server.cgi) avec ces paramètres :

openid.mode = associate
   Il indique à Carol vouloir établir
   un secret partagé avec elle.

openid.assoc_type = HMAC-SHA1
   Le type de secret à partager souhaité ;
   actuellement, seul HMAC-SHA1 est accepté.

openid.session_type = *blank*
   Le type de session indique comment
   le secret devrait être établir.
   Une valeur vide signifie une transmission
   en clair, qui n'est pas sûre à 100 %.
   Un type "DH-SHA1" signifie qu'il faudrait
   plutôt utiliser une négotiation de clé
   Diffie-Hellman.

openid.dh_* = *meh*
   Si un échange de clé Diffie-Hellman
   est demandé, alors quelques paramètres
   supplémentaires sont nécessaires.
   Tout ça est très mathématique et puis
   Bob est mauvais en maths.

Quand Bob fait-il ça ? Il le fera sans doute la toute première fois où quelqu'un lui demandera d'authentifier une identité auprès de Carol, ou si le secret partagé obtenu la dernière fois où il l'aura fait est expiré. Mais la chose importante à noter ici, alors que cet événement est déclenché par Alice en demandant une authentification au serveur OpenID de Carol, est que le secret ne fait pas strictement partie de leur transaction. Il s'agit d'une transaction complètement séparée entre Bob et Carol qui peut avoir lieu n'importe quand.

Bon, maintenant que Bob a envoyé cette requête "associate", qu'est-ce que Carol va en faire ? Elle doit générer un secret partagé tout comme elle l'aurait fait autrement et l'attacher à un paramètre assoc_handle. Mais au lieu d'un descripteur sans état, elle l'attache à un assoc_handle avec état. Puis elle répond à la requête POST de Bob par un document de format « clé=valeur » contenant les paramètres repris habituels. Les deux paramètres magiques sont toutefois assoc_handle et mac_key (ou enc_mac_key si utilisation d'une clé Diffie-Hellman). Avec ces deux éléments, Bob peut alors garder la trace, interne au consommateur, de ce descripteur et cette clé de secret partagé.

Qu'est-ce que cela nous apporte ? Beaucoup, en fait. Désormais, dès lors que Bob doit authentifier une identité auprès de Carol, il envoie le descripteur avec ses requêtes "checkid_setup" ou "checkid_immediate" au lieu d'attendre que Carol en génère un sans état à chaque fois. Cela économise à Carol la charge de générer un nouveau secret partagé pour chaque transaction sans état. Une fois que Bob reçoit en retour une réponse signée de Carol (si l'assertion est positive), il n'a plus à la faire suivre d'une requête "check_authentication" puisqu'il connaît déjà le secret partagé utilisé pour signer l'assertion et qu'il peut le faire lui-même. Rappelez-vous, avant il devait revérifier auprès de Carol car il n'avait aucun moyen de savoir si la signature était authentique, ou falsifiée par Alice, sans retourner directement chez Carol pour le déterminer. Désormais, comme seuls Carol et lui connaissent le secret partagé (et pas Alice en particulier), il peut vérifier immédiatement la signature sans parler à nouveau à Carol. Tout cela divise presque par deux son trafic d'authentification (et celui de Carol) dans le meilleur des cas.

Eve renifle des paquets

Maintenant, supposons que Bob et Carol sont tous deux relativement paresseux, malgré tout ce qu'ils ont accompli jusque là, et ils conviennent de ne pas échanger de clé Diffie-Hellman pour leur requêtes "associate". Quelle est l'exposition possible envers Eve, notre farceur pragmatique, industrieux et persévérant ?

Du point de vue d'Eve, ce qu'elle désire le plus c'est ce secret partagé établi au cours de la commande "associate" et envoyé en clair par Carol à Bob. Elle doit donc intercepter cette commande quand elle se présente et l'aspirer du canal de transmission. Si Eve dispose d'un accès complet et sans entrave au réseau pour le faire, alors Bob et Carol sont cuits. Eve pourra connaître tous les secrets partagés et usurper n'importe quelle identité.

Comme nous ne sommes pas allés très loin dans ce scénario, examinons le cas plus limité où Eve ne peut renifler le trafic du réseau que pendant un temps fini. À cause du volume du trafic d'authentification entrant et sortant de chez Bob et parce que les secrets partagés (et leurs descripteurs assoc_handle) de Bob sont programmés pour expirer au bout d'une période raisonnable, Eve n'obtiendra peut-être qu'une petite partie des secrets partagés à exploiter pour usurper des identités. C'est toujours bien assez pour faire des bêtises, mais Eve veut absolument usurper l'identité d'Alice. Il lui faut donc le secret spécifique partagé par Bob et Carol afin d'usurper l'identité d'Alice. Le champ d'action est plutôt étroit, et en pratique le secret partagé de Carol et Bob est probablement à l'abri.

Le meilleur pari d'Eve serait d'essayer de forcer l'apparition de la commande "associate" pendant qu'elle peut écouter. Par exemple, si Eve prétend authentifier une identité gérée par Carol avec Bob, elle pourrait déclarer que le descripteur partagé assoc_handle utilisé est invalide, forçant Bob et Carol en mode passif. Ce n'est pas un avantage pour Eve, et maintenant que Bob a invalidé sa clé partagée, à la prochaine requête d'authentification pour une identité gérée par Carol (qui sera la toute prochaine action d'Eve), Bob sera peut-être tenté d'envoyer d'abord une nouvelle commande "associate" à Carol pour obtenir un nouveau secret partagé. Et c'est exactement ce qu'Eve veut entendre, mais le protocole OpenID s'y oppose en obligeant Bob à vérifier directement auprès de Carol avant d'invalider un descripteur assoc_handle qu'ils partagent. À cause de ça, Eve ne peut pas forcer les descripteurs de Bob à devenir invalides, ce qui l'empêche de forcer l'apparition d'une commande "associate" dans sa fenêtre d'attaque restreinte.

Bien sûr, si Bob et Carol utilisent tout de suite un échange de clé Diffie-Hellman pour établir leur secret partagé, alors tous les reniflages de paquets seront inutiles pour Eve, et leur secret partagé sera en sécurité.

Un autre chose qu'Eve pourrait écouter serait une tentative d'authentification réussie faite par Alice en utilisant un secret partagé. Si elle capture l'assertion signée envoyée en retour à Bob par Carol, elle peut essayer de forcer le secret partagé à loisir. Une fois la clé obtenue, elle peut feindre une tentative d'authentification sous l'identité d'Alice auprès de Bob. Carol et Bob retomberont en mode passif, sans état, s'il y a un problème avec leur descripteur partagé actuel, qu'Eve peut facilement créer simplement en mentant à Bob sur la réponse de Carol. Ensuite, Eve crée et signe sa propre assertion avec la clé brisée, qu'elle renvoie à Bob dans le cadre de la transaction sans état. Bob, en mode passif maintenant, la renverra alors directement à Carol pour validation dans une requête "check_authentication". Si Carol ignorait la différence entre un descripteur assoc_handle sans état et un descripteur avec état, elle vérifierait le message retransmis par Bob, trouverait que le secret partagé utilisé pour signer l'assertion et le descripteur assoc_handle correspondent (puisque c'était l'un de ceux que Carol avait véritablement utilisés dans le passé), et répondrait à Bob que l'assertion est valide. Il est donc important que Carol puisse distinguer les descripteurs sans état de ceux avec état, et elle ne devrait jamais répondre à une requête "check_authentication" pour un descripteur avec état. La spécification OpenID impose aussi cela.