Date de publication du RFC : Septembre 2013
Auteur(s) du RFC : Paul E. Jones, Gonzalo Salgueiro (Cisco Systems), Michael B. Jones (Microsoft), Joseph Smarr (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF appsawg
Première rédaction de cet article le 28 septembre 2013
Dernière mise à jour le 2 mai 2017
Ce RFC décrit (même si elle n'est pas
présentée ainsi) la deuxième version du protocole
WebFinger. La première était informelle et
permettait de récupérer de l'information sur une personne ou une
organisation à partir de son adresse de
courrier. La deuxième version, la première
officiellement normalisée, généralise WebFinger : la clé d'entrée dans
l'information est un URI, qui peut être une
adresse de courrier (mailto:
, RFC 6068) mais pas
forcément.
WebFinger doit son nom à l'antique protocole
Finger, normalisé dans le RFC 1288. Finger permettait de récupérer de l'information sur
une personne, identifiée par login@nom-de-machine
en se connectant sur le port 79 de la dite
machine. On obtenait une réponse non structurée (du texte libre). Au
contraire, WebFinger tourne sur le port 80, celui de
HTTP, utilise REST, et
envoie une réponse structurée, en JSON, qui est
donc analysable par un programme. On nomme cette réponse le JRD,
JSON Resource Descriptor.
Que contient ce JRD ? Ce qu'on veut. Pas grand'chose si on est soucieux de vie privée et, sinon, une adresse, un numéro de téléphone, une photo, etc. WebFinger peut être utilisé pour des entités non humaines (une imprimante, une machine à café, etc), pour obtenir de l'information sur leurs capacités. Par contre, WebFinger est prévu pour de l'information relativement statique, et pas pour se renseigner sur l'état actuel. Pour une imprimante, il peut servir à apprendre qu'elle sait faire de la couleur, ou seulement du noir & blanc, mais pas à connaître la quantité de feuilles de papier restant dans le bac.
(Pour des raisons historiques, certaines mises en œuvre de WebFinger distribuent un XRD, la même chose mais en XML. D'une manière générale, attention à ce que vous trouvez avec un moteur de recherche, lorsque vous demandez « webfinger » : ce seront souvent des informations complètement dépassées.)
Et quel genre d'URI
peut-on utiliser en argument ? Ce qu'on veut mais, en pratique,
l'usage le plus courant aujourd'hui est avec les URI
acct:
(normalisés dans le RFC 7565).
WebFinger reste délibérement ouvert : le RFC spécifie un protocole et un format mais pas une sémantique. Ainsi, le JRD peut avoir un contenu très varié selon les applications. L'idée est que chaque application de WebFinger spécialisera ce protocole et ce format, en indiquant plus précisément le type d'URI attendu et les informations contenues dans le JRD de réponse. À noter que ce point avait fait l'objet de vives controverses à l'IETF, à la fin du processus de normalisation. Tel que spécifié, WebFinger semblait à certains terriblement vague, un framework plutôt qu'un protocole. Selon les mots de Pete Resnick lors de la discussion entre l'IESG et les auteurs, « I suspect that the semantics are so underspecified that there could not possibly be interoperable implementations without lots of out-of-band information ». C'est pour cela que le RFC intègre aujourd'hui ces précisions : la sémantique sera dans d'autres spécifications (la section 8 du RFC détaille ce choix et ses conséquences).
La section 3 donne deux exemples d'utilisation, le premier
dans le cas d'OpenID
Connect et le second pour récupérer des métadonnées sur une
page Web. Dans le premier cas, Carol veut s'authentifier auprès d'un
site Web et donne son identificateur OpenID
Connect, carol@example.com
. Le site
qui authentifie
va utiliser WebFinger (avec l'URI
acct:carol@example.com
, avec le plan acct:
) pour trouver le fournisseur OpenID de
Carol. Dans le second cas, le client WebFinger va interroger le site
qui héberge la page Web et demander en utilisant comme URI celui de la
page Web.
À noter que les versions préliminaires de ce RFC avaient également plein d'exemples très hypothétiques, jamais utilisés en vrai, et qui ont été ensuite supprimés. On y trouvait par exemple un mécanisme possible pour l'autoconfiguration du client de courrier électronique, qui n'apportait rien par rapport au RFC 6186.
Avant de se plonger dans le protocole lui-même, la section 2
rappelle quelques points de vocabulaire. WebFinger étant, comme son
nom l'indique, fondé sur le Web, il fait un
grand usage des liens, tels que décrits dans le
RFC 8288, pour indiquer une
relation. La relation a un type et une
information liée. En HTTP, le RFC 8288 utilise ainsi les attributs rel
(type de la relation) et href
(information liée)
dans un en-tête Link:
. WebFinger représente le
même concept en JSON avec des objets JSON
links
(un tableau JSON), chacun comportant un membre
rel
et un membre href
. Un
exemple, pour une entité qui est un article publié sur le Web (le
deuxième cas d'exemple cité plus haut, la recherche de métadonnées), les liens
seraient :
"links" : [ { "rel" : "copyright", "href" : "http://www.example.com/copyright" }, { "rel" : "author", "href" : "http://blog.example.com/author/steve", } ]
Cet exemple se lit : l'entité interrogée via WebFinger a un
copyright (qu'on peut
trouver en http://www.example.com/copyright
) et un
auteur, décrit en http://blog.example.com/author/steve
.
La section 4 décrit le protocole complet. C'est du
REST, donc au-dessus de
HTTP. Le client WebFinger doit spécifier l'URI
de l'entité sur laquelle il veut des informations et il peut aussi
spécifier un ou plusieurs types de relations qui l'intéressent (par
défaut, il va tout recevoir). À noter que WebFinger
impose l'usage de HTTPS,
ce protocole étant souvent utilisé pour transporter des données
sensibles (section 9.1, et c'était un des points de discussion les
plus chauds à l'IETF). La requête WebFinger va utiliser un chemin qui utilise le
préfixe .well-known
du RFC 8615 et le suffixe webfinger
(désormais
enregistré dans les noms bien connus). Si l'URI de l'entité qui nous intéresse contient un
nom de machine, c'est cette machine que contacte le client WebFinger
(sinon, il doit se débrouiller, d'une manière non précisée). La
méthode HTTP utilisée est toujours GET
(section
9.3 du RFC 2616). Prenons
un exemple, celui de l'article
http://blog.example.com/article/id/314
sur lequel on voudrait
plus d'informations. Le client WebFinger va se connecter à
blog.example.com
en HTTPS et envoyer la requête HTTP :
GET /.well-known/webfinger?resource=http%3A%2F%2Fblog.example.com%2Farticle%2Fid%2F314 HTTP/1.1 Host: blog.example.com
Le composant de requête (section 3.4 du RFC 3986) resource
est l'URI (ici
pour-cent encodé) de l'entité qui nous
intéresse.
Si le serveur WebFinger connait l'entité en question, et accepte de
répondre, il répond par
le JRD (les données en JSON, étiquetées
application/jrd+json
, et décrites plus loin, en
section 4.4 du RFC). Dans tous les autres cas, il répond par
les codes HTTP traditionnels (400 « tu as oublié un truc, peut-être la
resource
», 403 « pas question que je te réponde
à toi »,
404 « je ne connais pas cette entité », 500 « j'ai un problème », 429
« trop de travail, je craque », etc).
Et si le client a inclus un ou plusieurs rel
dans sa requête, indiquant qu'il n'est pas intéressé par tous les
types de données mais seulement par certains ? Cela n'influe que sur
le membre links
du JRD, qui n'incluera alors que
ce qui est demandé. Reprenons l'exemple de notre page Web et ne
cherchons que l'auteur :
GET /.well-known/webfinger?resource=http%3A%2F%2Fblog.example.com%2Farticle%2Fid%2F314&rel=author HTTP/1.1 Host: blog.example.com ... "links" : [ { "rel" : "author", "href" : "http://blog.example.com/author/steve", } ]
Quel est l'intérêt (après tout, le client pourrait ainsi bien filtrer
les types de liens après les avoir tous récupérés) ? Économiser des
ressources sur le serveur (certaines informations peuvent nécessiter
des requêtes compliquées dans une base de données) et diminuer le
débit réseau. Notez toutefois, si vous écrivez un client, que tous les
serveurs ne gèrent pas ce paramètre rel
dans la
requête et que le client risque donc toujours de tout récupérer, et de
devoir trier ensuite.
Le format complet du JRD (JSON Resource
Descriptor, annexe A du RFC 6415 et
dérivé du
XRD) figure en section 4.4. C'est un objet
JSON (RFC 8259)
comprenant les membres subject
,
aliases
, properties
et
links
que nous avons déjà
vu. subject
, le seul obligatoire, est un identificateur de l'entité sur
laquelle on se renseigne (en général le même que le paramètre
resource
), properties
sont
les informations sur l'entité (un registre
IANA les stocke, en échange d'une spécification écrite,
cf. section 10.4.2) et links
les
liens. links
est le plus complexe. Chaque lien
est un objet JSON comportant plusieurs
membres. rel
est le seul obligatoire et sa valeur
est, soit un type enregistré
à l'IANA selon le RFC 8288, soit un URI
(on peut ainsi « créer » ses propres types). Les autres membres
possibles sont type
(un type
MIME), href
(pointe vers la
valeur du lien), titles
(un texte humainement
lisible, par exemple pour le présenter à l'utilisateur, marqué par une
étiquette de langue) et
properties
(informations diverses).
Voici un exemple complet, tiré du RFC, toujours au sujet de
notre page Web intéressante :
{ "subject" : "http://blog.example.com/article/id/314", "aliases" : [ "http://blog.example.com/cool_new_thing", "http://blog.example.com/steve/article/7" ], "properties" : { "http://blgx.example.net/ns/version" : "1.3", "http://blgx.example.net/ns/ext" : null }, "links" : [ { "rel" : "copyright", "href" : "http://www.example.com/copyright" }, { "rel" : "author", "href" : "http://blog.example.com/author/steve", "titles" : { "en" : "The Magical World of Steve", "fr" : "Le Monde Magique de Steve" }, "properties" : { "http://example.com/role" : "editor" } } ] }
La section 7 du RFC couvre un cas délicat, celui de services WebFinger hébergés. Si on souhaite sous-traiter WebFinger à un tiers, comment l'indique-t-on ? La seule solution est de faire une redirection HTTP depuis son site. Par exemple, avec Apache, on mettra dans la configuration :
Redirect /.well-known/webfinger http://wf.example.net/.well-known/webfinger
Et les requêtes WebFinger qu'on recevra seront gérées par le
prestataire wf.example.net
par le biais d'une
redirection HTTP.
La section 8 décrit ce que veut dire « spécifier l'usage de
WebFinger pour une application ». On a vu que WebFinger fournissait un
protocole et un format très général. Chaque application qui compte se
servir de WebFinger doit préciser un certain nombre de choses,
notamment le contenu du JRD attendu. Si vous voulez vous servir de
WebFinger pour un nouveau service très cool, vous allez devoir lire
cette section et rédiger les détails. Première chose, le type d'URI
attendu (acct:
? un autre ?) Deuxième chose,
comment trouver le serveur à interroger. Si l'URI utilise le plan
http:
, c'est trivial. Mais pour les
acct:
ou les mailto:
?
L'application doit donc préciser comment on trouve le serveur
WebFinger (cela peut être aussi simple que d'avoir un serveur
WebFinger central, codé en dur dans les programmes, pour tous les URI
de l'application...)
Enfin, l'application doit spécifier le contenu attendu : quelles
properties
sont obligatoires dans la réponse, par
exemple ? Même chose pour les liens : quels types
rel
peuvent/doivent être utilisés dans les
liens ?
Ce n'est pas un peu indiscret, toutes ces informations distribuées à tout vent ? Si, et ce point a fait l'objet de vives discussions à l'IETF, ce qui a fini par donner naissance aux sections 6 et 9.2 de ce RFC. Le principal avantage de WebFinger (un seul endroit où aller pour récupérer toutes les informations sur une entité, et sous une forme structurée, ce qui est très pratique pour les programmes qui vont l'analyser) est aussi son principal risque (comme dit le RFC « The easy access to user information via WebFinger was a design goal of the protocol, not a limitation »). Le RFC cite l'exemple de données qui permettraient le harcèlement d'une personne. L'article « Abusing social networks for automated user profiling » illustrait bien comment le recoupement d'informations provenant de différents réseaux sociaux permettait de découvrir plein de choses sur les utilisateurs.
Ces sections « vie privée » du RFC rappellent qu'un serveur WebFinger ne distribue que ce qu'il veut. En cas de demande d'information sur une personne, par exemple, la norme technique qu'est ce RFC ne spécifie pas qu'on doive distribuer l'adresse et le numéro de téléphone. C'est un choix des administrateurs du serveur. (Au passage, c'est exactement la même chose pour le protocole whois, RFC 3912, un protocole dont les usages sont proches de ceux de WebFinger. Le RFC spécifie un protocole, pas une politique de distribution des données.)
Ensuite, le serveur n'est pas obligé d'être ouvert à tout le monde. Il peut parfaitement utiliser l'authentification HTTP (ou d'autres mécanismes de contrôle d'accès comme l'adresse IP du client) pour restreindre la distribution d'informations à certains. Un serveur WebFinger est également, cela va de soi, autorisé à fournir des réponses différentes selon le client. Par exemple, on peut imaginer une réponse minimale pour les clients inconnus, et davantage de détails pour ceux qui s'authentifient. Le RFC ne cite pas les questions légales (hors sujet pour une norme technique) mais, par exemple, un serveur WebFinger d'une entreprise qui distribuerait des détails personnels sur ses employés, comme des photos, sans leur autorisation, serait certainement en violation de la directive européenne sur la protection des données personnelles.
La section 9.2 demande donc que, pour tout service WebFinger, il existe une interface permettant aux utilisateurs d'indiquer de manière simple s'ils veulent que des informations à leur sujet soient publiées ou pas, et lesquelles. Par exemple, pour un réseau social typique, on peut imaginer que les utilisateurs choisissent quels éléments d'information sur eux soient publics et, dans ce cas, que seuls les éléments ainsi marqués soient distribués par WebFinger. Le RFC demande aussi que, par défaut, rien ne soit publié (ce qui n'est certainement pas la pratique des gros silos de données comme Facebook).
Les liens fournis en réponse à une requête WebFinger peuvent d'ailleurs eux aussi pointer vers des ressources dont l'accès est contrôlé ou limité. Bref, ce n'est pas de la faute de WebFinger si des informations sensibles circulent, il n'est qu'un outil, à utiliser intelligemment.
Autre problème de sécurité avec WebFinger, le fait que la réponse est différente selon que la ressource existe ou pas (code HTTP 200 dans le premier cas et 404 dans le second). Ainsi, même si la réponse est vide, un client WebFinger peut, par essais répétés, se constituer une liste des ressources existantes. Cela peut permettre d'énumérer les utilisateurs d'un réseau social, ou bien les adresses de courrier valides (information certainement utile pour un spammeur). Le RFC recommande donc que des mesures techniques, comme une limitation du trafic par adresse IP du client, soient déployées.
Autre cas où l'utilisation maladroite de WebFinger peut avoir des
conséquences néfastes, les requêtes automatiques. Supposons un
MUA qui ferait automatiquement une requête
WebFinger sur le champ From:
du message lorsque
celui-ci est lu. Un spammeur pourrait générer un champ
From:
différent par destinataire et les requêtes
WebFinger entrantes lui diraient quels destinataires ont lu le
message... Le RFC recommande donc de ne pas effectuer de requêtes
WebFinger automatiquement.
Enfin, le RFC note que rien ne garantit que les valeurs renvoyées par WebFinger soient correctes (là encore, c'est un problème que les utilisateurs de whois connaissent déjà bien). Il y a en effet des choses fausses sur le Web.
Question mises en œuvre, ce n'est pas cela qui manque, y compris en logiciel libre. Il en existe une liste incomplète. Par exemple, GNU Social gère ce protocole.
Voici quelques exemples de requêtes WebFinger réelles, faites avec le client REST curl :
% curl -v 'https://packetizer.com/.well-known/webfinger?resource=acct:paulej@packetizer.com' HTTP/1.1 200 OK Server: Apache/2.2.22 (Fedora) Access-Control-Allow-Origin: * Content-Type: application/jrd+json; charset=UTF-8 ... { "subject" : "acct:paulej@packetizer.com", "aliases" : [ "h323:paulej@packetizer.com" ], "properties" : { "http://packetizer.com/ns/name" : "Paul E. Jones", "http://packetizer.com/ns/name#zh-CN" : "保罗‧琼斯", "http://packetizer.com/ns/activated" : "2000-02-17T03:00:00Z" }, "links" : [ { "rel" : "test1", "href" : "http://example.com/author?q=acct%3Apaulej%40packetizer.com", "titles" : { "en-us" : "Test Link" } }, { "rel" : "test2", "href" : "http://example.org/%E7%A7%81%E3%81%AE%E6%96%87%E6%9B%B8.txt" }, { "rel" : "http://webfinger.net/rel/avatar", "type" : "image/jpeg", "href" : "http://www.packetizer.com/people/paulej/images/paulej.jpg" }, { "rel" : "http://specs.openid.net/auth/2.0/provider", "href" : "https://openid.packetizer.com/paulej" }, { "rel" : "http://packetizer.com/rel/share", "type" : "text/html", "href" : "http://hive.packetizer.com/users/paulej/" }, { "rel" : "http://webfinger.net/rel/profile-page", "type" : "text/html", "href" : "http://www.packetizer.com/people/paulej/" }, { "rel" : "http://packetizer.com/rel/blog", "type" : "text/html", "href" : "http://www.packetizer.com/people/paulej/blog/", "titles" : { "en-us" : "Paul E. Jones' Blog" } }, { "rel" : "http://packetizer.com/rel/businesscard", "type" : "text/vcard", "href" : "http://www.packetizer.com/people/paulej/paulej.vcf" }, { "rel" : "http://schemas.google.com/g/2010#updates-from", "type" : "application/atom+xml", "href" : "http://www.packetizer.com/people/paulej/blog/blog.xml" }, { "rel" : "http://microformats.org/profile/hcard", "type" : "text/html", "href" : "http://www.packetizer.com/people/paulej/" }, { "rel" : "http://bitcoin.org/rel/address", "href" : "bitcoin:17XoqvUCrf12H7Vc7c7uDxib8FDMXFx2p6" } ] }
Autre exemple, pour l'URI
acct:javier@seed.gluu.org
, avec beaucoup moins
d'information distribuée :
% curl -v 'https://seed.gluu.org/.well-known/webfinger?resource=acct:javier@seed.gluu.org' ... { "subject": "acct:javier@seed.gluu.org", "links": [{ "rel": "http://openid.net/specs/connect/1.0/issuer", "href": "https://seed.gluu.org" }] }
En théorie, si on était sérieux, on ajouterait à curl l'option
--header "Accept: application/jrd+json"
pour
indiquer au serveur qu'on ne comprend que ce format, le seul
actuellement standard pour WebFinger (la syntaxe
jrd+json
, c'est-à-dire langage + format, est
décrite dans le RFC 6839). Mais beaucoup de serveurs n'en
tiennent pas compte (certains vous servent du
XRD si vous mettez --header "Accept:
application/xrd+xml"
).
WebFinger est également utilisé par Mastodon
donc tout serveur Mastodon est également un serveur
WebFinger. Essayons avec mon compte Mastodon,
bortzmeyer@mastodon.gougere.fr
:
% curl 'https://mastodon.gougere.fr/.well-known/webfinger?resource=bortzmeyer@mastodon.gougere.fr' {"subject":"acct:bortzmeyer@mastodon.gougere.fr","aliases":["https://mastodon.gougere.fr/@bortzmeyer"],"links":[{"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://mastodon.gougere.fr/@bortzmeyer"},{"rel":"http://schemas.google.com/g/2010#updates-from","type":"application/atom+xml","href":"https://mastodon.gougere.fr/users/bortzmeyer.atom"},{"rel":"salmon","href":"https://mastodon.gougere.fr/api/salmon/369"},{"rel":"magic-public-key","href":"data:application/magic-public-key,RSA._AmUWXDlwOkzKtqUsxUC94_B9yRZct-C8QqrxLWhGzA3zKNZwic0KWKMBuVRuQ7GXOq5lsyhA2pvXBTnh-Sk_8G5uLY6I7C0sjgAQKyiHVCmOBAGwcw67qfxIoN5-l2NrIZ0IygxnMOY_GU1q6fg8v6_1_bepnjCduWRVAdDBoo_HzSn91LYVleAg3E3oK8eXWYb28_DaCq9tJy5hHYLDK92XKTtk7t0Ii9U7znFvSrqgqD-qEc3KQHS5kOFRD1EfK9CI6872og0M_b6FVhNfcITaVjjk3S0uM0mpHiQuqPtfytdkRlEBd4uZUce3dPk0sODQaNcVrAMHf0KFm3w1w==.AQAB"},{"rel":"http://ostatus.org/schema/1.0/subscribe","template":"https://mastodon.gougere.fr/authorize_follow?acct={uri}"}]}
Enfin, pour terminer cet article, une question que se posent
certainement tous mes lecteurs qui connaissent le
DNS. Pourquoi diable avoir décidé que le
serveur WebFinger était le nom de domaine dans l'URI, ce qui manque de
souplesse (si l'URI est acct:twitter.com
, cela
oblige Twitter à avoir le serveur WebFinger au même endroit que le
serveur Web) plutôt que d'avoir une indirection, fournie par les très
utiles enregistrements SRV ? Je suis d'accord,
cela aurait été la bonne solution et cela aurait
résolu bien des problèmes. Hélas, le groupe de travail WebFinger a
fait un autre choix, pour les raisons suivantes :
Vous pouvez lire une discussion qui avait eu lieu à l'époque.
Autres lectures sur WebFinger, la synthèse de l'auteur du RFC, et un argumentaire pro-WebFinger de sa part.
Version PDF de cette page (mais vous pouvez aussi imprimer depuis votre navigateur, il y a une feuille de style prévue pour cela)
Source XML de cette page (cette page est distribuée sous les termes de la licence GFDL)