Date de publication du RFC : Avril 2013
Auteur(s) du RFC : S. Farrell (Trinity College Dublin), D. Kutscher (NEC), C. Dannewitz (University of Paderborn), B. Ohlman, A. Keranen (Ericsson), P. Hallam-Baker (Comodo Group)
Chemin des normes
Première rédaction de cet article le 22 avril 2013
Dernière mise à jour le 23 avril 2013
La question des identificateurs sur le
Web agite des électrons depuis le début. Il
n'existe pas d'identificateurs idéaux, ayant toutes
les bonnes propriétés. Dans la grande famille des
URI (RFC 3986), il faut donc choisir selon l'importance
qu'on donne à tel ou tel critère. Par exemple, si on attache du prix à
la stabilité de l'identificateur et qu'on veut qu'il ne désigne pas
seulement un endroit où se trouve un contenu, mais qu'on veut qu'il
désigne ce contenu et pas un autre ? Alors, on peut choisir les
nouveaux URI ni:
(Named
Information) qui désignent un contenu par un
condensat (hash). Un URI
ni:
ne change pas si le contenu change de
serveur, mais il est modifié si le contenu lui-même change. C'est donc
une forme d'adressage par le contenu.
À quoi cela sert ? À éviter un problème de l'indirection : si je
dis « regardez l'image en
http://www.example.org/holidays/beach.png
» et
que l'image de vacances à la plage est remplacée par une photo de
LOLcat, l'URI sera
toujours valable, alors que le contenu a changé. Inversement, si un
webmestre incompétent et qui n'a pas lu
« Cool URIs don't change »
réorganise le site et met le contenu en
http://www.example.org/bigcomplicatedcmswithsecurityholes.php?kind=image&tag=beach&foo=bar&id=612981
,
l'URI ne marchera plus alors que le contenu est inchangé. Un autre
exemple, plus positif, est le cas où un contenu est répliqué en
plusieurs endroits, ayant des URL
différents. Il faut pouvoir désigner le contenu, indépendamment du
service qui l'héberge. C'est pour
répondre à ces deux problèmes qu'a été créé par ce
RFC le plan d'URI ni:
(désormais dans le registre IANA des plans d'URI). Le
contenu en question pourra donc être désigné par
ni:///sha256;rJAeWFhQWIoTExwyEQ8w_L5uB0UkfmnCGfNIPy7CdDc
. Une
telle technique a déjà été employée mais de manière non standard (par
exemple, on voit parfois des URI du genre
http://www.example.org/hash?function=sha256&value=rJAeWFhQWIoTExwyEQ8w_L5uB0UkfmnCGfNIPy7CdDc
où un programme situé sur www.example.org
récupère la ressource selon le contenu). Désormais, le but est de
permettre du vrai adressage par le contenu sur le Web. Cela se nomme
information-centric sur les
PowerPoint des gourous. Sur ce sujet, le RFC recommande la lecture de « Design
Considerations for a Network of Information » de Ahlgren, D'Ambrosio, Dannewitz, Marchisio, Marsh, Ohlman,
Pentikousis, Rembarz, Strandberg, et Vercellone, ainsi que des articles de Van Jacobson. On peut
aussi lire des articles sur les autres mécanismes d'adressage par le
contenu comme les magnet links de plusieurs systèmes
pair-à-pair, comme Freenet.
Une fois le contenu récupéré (en HTTP ou par d'autres moyens), le lecteur peut alors recalculer le condensat et vérifier s'il a bien reçu le bon fichier (des protocoles comme BitTorrent utilisent un mécanisme analogue pour s'assurer que le fichier transmis par les pairs est bien celui qu'on voulait).
D'autres informations peuvent se retrouver dans l'URI
ni:
(voir des exemples plus loin) mais la
comparaison de deux URI ni:
se fait uniquement
sur le couple {fonction de hachage utilisée,
valeur du condensat}.
Le condensat est calculé par une fonction de hachage
cryptographique et, par défaut, c'est
SHA-256 (vous avez noté le
sha256
dans l'URI ni:
donné
en exemple plus haut ?) Les valeurs possibles pour l'algorithme de
hachage figurent dans un nouveau
registre. Les nouvelles valeurs sont enregistrées selon une
procédure légère d'examen par un expert (RFC 5226 et section 9.4 de notre RFC).
Les condensats de SHA-256 sont de grande taille, parfois trop pour
certaines utilisations. On a donc le droit de les tronquer à leurs N
premiers bits et le nom d'algorithme indiqué doit préciser cette
troncation. Ainsi, si on garde les 32 premiers bits, on doit indiquer
sha256-32
et pas juste
sha256
. Attention, c'est évidemment au détriment
de la sécurité (si la sortie de SHA-256 est si longue, c'est pour une
bonne raison, cf. RFC 3766) et ces condensats raccourcis, quoique simples à
manipuler, ne protègent plus tellement. (Notez que le
VCS git, qui identifie
les commits par un condensat
cryptographique, permet également de les raccourcir, pour faciliter
son utilisation, par exemple depuis le
shell.)
Le format exact des URI ni:
figure en section
3. On note un composant dont je n'ai pas encore parlé, l'autorité. On
peut écrire
ni:///sha256;rJAeWFhQWIoTExwyEQ8w_L5uB0UkfmnCGfNIPy7CdDc
mais aussi
ni://www.example.net/sha256;rJAeWFhQWIoTExwyEQ8w_L5uB0UkfmnCGfNIPy7CdDc
. L'autorité
(ici, www.example.net
) désigne un endroit où on
pourra peut-être récupérer la ressource. Le gros problème des
identificateurs fondés sur le contenu du fichier, en effet, est qu'ils
sont inutiles pour accéder effectivement au fichier : pas moyen de
télécharger un fichier dont on ne connait que le condensat ! Il existe
plusieurs solutions et l'une d'elles est de passer par
l'autorité. L'idée est que l'URI ni:
est
automatiquement transformé en un URL HTTP sous
.well-known
(cf. RFC 8615 et section 4 de notre RFC). Ainsi,
ni://www.example.net/sha256;rJAeWFhQWIoTExwyEQ8w_L5uB0UkfmnCGfNIPy7CdDc
devient
http://www.example.net/.well-known/ni/sha256/rJAeWFhQWIoTExwyEQ8w_L5uB0UkfmnCGfNIPy7CdDc
qu'on peut ensuite récupérer par des moyens classiques. On combine
alors les avantages de l'adressage par le contenu (le condensat est là
pour vérifier le contenu) et du fait que les URL marchent bien pour
récupérer un contenu. On notera que le système d'identificateurs
ARK a un mécanisme analogue (un identificateur
stable et non résolvable plus un préfixe qui
permet de faire un URL résolvable et actionnable). On a ainsi le beurre
et l'argent du beurre. ni
fait désormais partie
des termes enregistrés dans le registre "well-known".
Petite question : pourquoi http:
et pas
https:
, qui serait plus sûr ? Parce que tous les
serveurs ne gèrent pas HTTPS et aussi parce que
ce n'est pas nécessaire pour s'assurer de l'intégrité du fichier
récupéré, le condensat cryptographique suffit. Bien sûr, une mise en
œuvre de ce RFC est autorisée à essayer avec HTTPS, par exemple
pour la confidentialité.
Comme indiqué plus haut, la comparaison entre deux URI
ni:
se fait uniquement sur le couple {algorithme,
condensat} donc ni://datastore.example/sha256;rJAeWFhQWIoTExwyEQ8w_L5uB0UkfmnCGfNIPy7CdDc
et
ni://www.example.net/sha256;rJAeWFhQWIoTExwyEQ8w_L5uB0UkfmnCGfNIPy7CdDc
sont identiques.
Au fait, SHA-256 peut être affiché en binaire ou bien sous une
forme hexadécimale. Laquelle est utilisée ? Le format binaire
suivi d'un encodage en
Base64 (RFC 4648), sous
sa forme URL encoding, celle qui utilise
_ et - au lieu de
/ et + (ce n'est pas la forme par
défaut : songez que l'outil base64
sur Unix ne permet pas de produire cette
variante).
Si on n'a pas besoin d'être lisible par des humains et
transmissible dans des textes, il existe aussi une forme binaire, plus
compacte, des URI ni:
, spécifiée en section
6.
Signe de leur souplesse, les URI ni:
ont
également une forme « prononçable ». S'il faut dicter un URI au
téléphone, l'encodage par défaut est très ambigu (par exemple,
minuscules et majuscules n'ont pas la même
valeur). D'où la syntaxe nih:
(NI for
Humans et non pas Not Invented Here, cf. RFC 5513) de la section
7. Les URI sous leur forme nih:
sont en
Base16 (RFC 4648), peuvent inclure
des tirets supplémentaires, pour la lisibilité,
et incluent une somme de contrôle (le dernier
chiffre, calculé selon l'algorithme de Luhn de
la norme ISO 7812) pour détecter les erreurs de
compréhension. Un exemple est nih:sha-256-120;5326-9057-e12f-e2b7-4ba0-7c89-2560-a2;f
.
Enfin, des paramètres peuvent apparaître dans l'URI, par exemple
pour indiquer le type de la ressource
(ni:///sha256;rJAeWFhQWIoTExwyEQ8w_L5uB0UkfmnCGfNIPy7CdDc?ct=image/png
). Une
liste de paramètres possibles est enregistrée.
À noter que l'abréviation ni
veut
officiellement dire Named Information mais que tout
geek va évidemment penser aux chevaliers qui disent Ni...
Avant d'utiliser les URI ni:
, il est prudent
de lire la section 10, consacrée aux questions de sécurité. Ainsi, il
ne faut pas oublier que le condensat cryptographique n'est
pas une signature. Il sert
à vérifier l'intégrité mais, comme n'importe qui peut générer un
condensat pour n'importe quel contenu, il ne prouve rien quant à
l'authenticité.
La fonction SHA-256 et ses camarades ne sont
pas inversibles. D'un condensat, on ne peut pas remonter au
contenu. Toutefois, celui-ci n'est pas vraiment secret. Un attaquant
peut toujours deviner plus ou moins le contenu (c'est particulièrement
facile si le contenu est très structuré, avec peu de variations
possibles) et tester les différentes possibilités. Il peut aussi
utiliser un moteur de recherche, si des pages
existent déjà avec la correspondance entre une ressource et son
condensat (pour empêcher cela, il faudrait que les condensats
ni:
soient salés, ce qui
n'est pas le cas).
Et, naturellement, si vous utilisez des condensats tronqués, comme le permet de RFC, vous perdez beaucoup en sécurité.
La notion d'autorité dans les URI
ni:
peut être trompeuse. Le nom de domaine qui peut apparaître dans un URI
ni:
n'est pas forcément la source du contenu,
puisque n'importe qui peut copier la ressource, et la servir depuis un
programme. Il ne faut donc pas attribuer de sémantique à la soi-disant « autorité ».
Si vous voulez regarder un tel système « en vrai », les articles de
ce blog sont tous accessibles via un URI
ni:
. L'identificateur est calculé toutes les
nuits et stocké dans une base de données. Un simple petit programme
WSGI permet ensuite de récupérer un fichier
en fonction de son identificateur. À noter que ce n'est pas la forme
HTML qui est utilisée mais le source en
XML (la forme en HTML change trop souvent, par
exemple si les outils qui la produisent automatiquement à partir du
source sont modifiés). Ainsi, l'URI
ni:///sha256;6OuucQ1RgugCDVinT2RGmzYYpra0fenH-zw7tilsx9k
correspond au source XML de cet
article. En indiquant explicitement l'autorité (le serveur qui
permet de faire la récupération), c'est l'URI
ni://www.bortzmeyer.org/sha256;6OuucQ1RgugCDVinT2RGmzYYpra0fenH-zw7tilsx9k
. Et
la version sous forme d'URL est
. Si
vous préferez un autre article,
https://www.bortzmeyer.org/.well-known/ni/sha256/6OuucQ1RgugCDVinT2RGmzYYpra0fenH-zw7tilsx9k
ni:///sha256;1etMCVZtd7_cq38MrtnQcoZW_e7J2cslulrFp92lueI
correspond au source de cet article.
Notez qu'il n'est pas possible de mettre l'URI
ni:
de l'article que vous êtes en train de lire
dans cet article (inclure le condensat change l'article et il faut
donc changer le condensat, ce qui change l'article...)
Vous voulez vérifier ? Allons-y.
% wget -O /tmp/x.xml https://www.bortzmeyer.org/.well-known/ni/sha256/1etMCVZtd7_cq38MrtnQcoZW_e7J2cslulrFp92lueI ... % openssl dgst -sha256 -binary /tmp/x.xml | base64 1etMCVZtd7/cq38MrtnQcoZW/e7J2cslulrFp92lueI=
Et on retrouve bien l'identificateur 1etMCVZtd...
(aux transformations URL encoding près).
Si vous voulez faire depuis le shell Unix les calculs nécessaires, voici quelques exemples avec OpenSSL. Pour calculer le NI de Hello World! :
% echo -n 'Hello World!' | openssl dgst -sha256 -binary | base64 f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=
Il faut ensuite rechercher/remplacer car base64
(ou bien la commande
openssl enc -base64
) ne sait pas faire de
l'URL encoding de Base64. Avec
sed :
% echo -n 'Hello World!' | openssl dgst -sha256 -binary | base64 | sed -e 's#/#_#g' -e 's#+#-#g' -e 's#=$##' f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk
(Pour la même manipulation, on peut aussi utiliser tr :
tr -cd a-zA-Z0-9+/ | tr +/ -_
. Comme expliqué par
Kim-Minh Kaplan : « Note le tr -cd
pour nettoyer le résultat de l’encodage en Base 64.
Si avec SHA-256 il n’est pas nécessaire, avec SHA-512, l’encodeur
d’OpenSSL introduira un retour à la ligne qu’il faudra aussi supprimer. »)
Si on reprend l'exemple plus haut, on peut combiner les deux opérations : on récupère le fichier grâce au condensat et on vérifie que le contenu est bien le contenu attendu. Utilisons ce simple script :
#!/bin/sh BASE_URL="https://www.bortzmeyer.org/.well-known/ni/sha256/" if [ -z "$1" ]; then echo "Usage: $0 ni" >&2 exit 1 fi ni=$1 hash=$(wget -q -O - ${BASE_URL}/${ni} | openssl dgst -sha256 -binary | openssl enc -base64 | \ sed -e 's#/#_#g' -e 's#+#-#g' -e 's#=$##') if [ "$hash" != "$ni" ]; then echo "Error: hash is $hash instead of $ni" >&2 exit 1 else exit 0 fi
Et voyons :
% get-and-check-ni 1etMCVZtd7_cq38MrtnQcoZW_e7J2cslulrFp92lueI %
Si on met une valeur fausse :
% get-and-check-ni 1etMCVZtd7_cq38MrtnQcoZW_e7J2cslulrFp922ueI Error: hash is 47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU instead of 1etMCVZtd7_cq38MrtnQcoZW_e7J2cslulrFp922ueI
Si vous voulez lire de source de mon programme, il est dans les fichiers de mon blog, scripts/blog2db/blog2db.py
pour le
stockage dans la base de données et wsgis/ni.py
pour la récupération.
Il existe une autre implémentation, par certains des auteurs du RFC, en http://sourceforge.net/projects/netinf/
.
Si vous voulez d'autres lectures, le RFC 1737. sur les URN, parlait déjà (en
1994) d'utiliser un condensat cryptographque
(avec MD5) pour désigner un contenu. En
2003, une proposition plus élaborée,
draft-thiemann-hash-urn
décrivait un système d'URN avec adressage par le contenu (des choses
comme
urn:hash::sha1:LBPI666ED2QSWVD3VSO5BG5R54TE22QL
).
Mais, avec draft-thiemann-hash-urn
, il y a un gros manque : pas moyen d'indiquer un (ou
plusieurs, comme dans le cas des magnets) serveur pouvant servir le
document en question. Avec draft-thiemann-hash-urn
, on n'a que des
URN (ils ne sont pas « actionnables »). Enfin, si vous voulez une
critique des ni:
par rapport aux
magnets, voyez la discussion sur LinuxFr.
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)