Ce blog n'a d'autre prétention que de me permettre de mettre à la disposition de tous des petits textes que j'écris. On y parle surtout d'informatique mais d'autres sujets apparaissent parfois.
Première rédaction de cet article le 24 décembre 2017
Pour Noël, un truc complètement inutile mais amusant : trouver la longitude et la latitude d'une adresse IP via le DNS.
Ce service est fourni par Bert Hubert,
l'auteur de PowerDNS, via le domaine
geo.lua.powerdns.org
. (Il ne marche
apparemment que pour IPv4.) On inverse
l'adresse IP (comme pour
in-addr.arpa
) et on
fait une requête pour le type
TXT. Exemple avec l'adresse du serveur Web de
l'AFNIC, 192.134.5.24
:
% dig TXT 24.5.134.192.geo.lua.powerdns.org ...;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40951 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 2, ADDITIONAL: 1 ... ;; ANSWER SECTION: 24.5.134.192.geo.lua.powerdns.org. 3600 IN TXT "48.858200 2.338700"
On peut automatiser l'opération avec awk (merci à Oli Schacher) :
% reverse() { echo $1 | awk -F. '{print $4"."$3"." $2"."$1}' } % reverse 192.134.5.24 24.5.134.192
Une fois que c'est fait, on peut lancer un navigateur Web directement vers la carte, ici avec OpenStreetMap :
% show-ip() { x-www-browser https://www.openstreetmap.org/\?$(dig $(echo $1 | awk -F. '{print $4"."$3"." $2"."$1}').geo.lua.powerdns.org TXT +short | head -1 | awk '{gsub(/"/, ""); print "mlat="$1"&mlon="$2"&zoom=12"}')} % show-ip 192.134.5.24
Et si vous préférez Evil Corp. :
% show-ip() { x-www-browser https://maps.google.com/maps\?q=$(dig $(echo $1 | awk -F. '{print $4"."$3"." $2"."$1}').geo.lua.powerdns.org TXT +short | awk '{gsub(/"/, ""); print $1","$2}')}
La base de géolocalisation utilisée est celle de MaxMind qui, comme toutes les bases de géolocalisation vaut ce qu'elle vaut (le serveur Web de l'AFNIC n'est pas au centre de Paris…)
Date de publication du RFC : Décembre 2017
Auteur(s) du RFC : D. Schinazi, T. Pauly (Apple)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF v6ops
Première rédaction de cet article le 21 décembre 2017
Une machine connectée à l'Internet et répondant aux requêtes venues du réseau a souvent plusieurs adresses IP pour son nom. C'est parfois une adresse IPv4 et une IPv6 mais cela peut aussi être plusieurs adresses IPv6, ou bien un mélange en proportions quelconques. Les développeurs d'application et les administrateurs système qui déploieront ces applications ensuite, ont un choix difficile si certaines de ces adresses marchent et d'autres pas (ou mal). Si les différentes adresses IP de cette machine passent par des chemins différents, certains marchant et d'autres pas, l'application arrivera-t-elle à se rabattre sur une autre adresse très vite ou bien imposera-t-elle à l'utilisateur un long délai avant de détecter enfin le problème ? Cette question est connue comme « le bonheur des globes oculaires » (les dits globes étant les yeux de l'utilisateur qui attend avec impatience la page d'accueil de PornHub) et ce RFC spécifie les exigences pour l'algorithme de connexion du client. En les suivant, les globes oculaires seront heureux. Il s'agit de la version 2 de l'algorithme, bien plus élaborée que la version 1 qui figurait dans le RFC 6555.
La section 1 rappelle les données du problème : on veut évidemment que cela marche aussi bien en IPv6 (RFC 8200) qu'en IPv4 (pas question d'accepter des performances inférieures) or, dans l'état actuel du déploiement d'IPv6, bien des sites ont une connexion IPv6 totalement ou partiellement cassée. Si un serveur a IPv4 et IPv6 et que son client n'a qu'IPv4, pas de problème. Mais si le client a IPv6, tente de l'utiliser, mais que sa connexion est plus ou moins en panne, ou simplement sous-optimale, ses globes oculaires vont souffrir d'impatience. On peut aussi noter que le problème n'est pas spécifique à IPv6 : du moment que la machine visée a plusieurs adresses, qu'elles soient IPv4 ou IPv6, le risque que certaines des adresses ne marchent pas (ou moins bien) existe, et l'algorithme des globes oculaires heureux doit être utilisé. (C'est un des gros changements avec le précédent RFC, le RFC 6555, qui n'envisageait que le cas IPv6.)
La bonne solution est donc que l'application elle-même gère le problème (ou, sinon l'application elle-même, la bibliothèque logicielle qu'elle utilise et où se trouve la fonction de connexion). Il existe plusieurs algorithmes pour cela, déjà largement déployés depuis des années. On peut donc se baser sur l'expérience pour spécifier ces algorithmes. Ce RFC normalise les caractéristiques que doivent avoir ces algorithmes. Si on suit ce RFC, le trafic (IP et DNS) va légèrement augmenter (surtout si la connectivité IPv6 marche mal ou pas du tout) mais la qualité du vécu de l'utilisateur va être maintenue, même en présence de problèmes, ce qui compense largement. Autrement, il existerait un risque élevé que certains utilisateurs coupent complètement IPv6, plutôt que de supporter ces problèmes de délai de connexion.
La cible principale de notre RFC est composée des protocoles de transport avec connexion (TCP, SCTP), les protocoles sans connexion comme UDP soulevant d'autres questions (s'ils ont une sémantique requête/réponse, comme dans ICE, les algorithmes de ce RFC peuvent être utilisés).
Donc, on a un nom de machine qu'on veut contacter, mettons
www.example.com
, avec plusieurs adresses
associées, peut-être de familles (v4 et v6) différentes. Prenons
une machine ayant une seule adresse IPv4 et une seule adresse
IPv6, avec une connexion IPv6 qui marche mal. Avec l'algorithme
naïf qu'utilisent encore certains logiciels voici la séquence
d'évenements traditionnelle :
192.0.2.1
et
2001:db8::1
./etc/gai.conf
). L'initiateur envoie un paquet
TCP SYN
à
2001:db8::1
.SYN
à 192.0.2.1
.SYN+ACK
en échange,
l'initiateur réplique par un ACK
et la connexion
TCP est établie.Le problème de cet algorithme naïf est donc la longue attente lors des essais IPv6. On veut au contraire un algorithme qui bascule rapidement en IPv4 lorsqu'IPv6 ne marche pas, sans pour autant gaspiller les ressources réseau en essayant par exemple toutes les adresses en même temps.
L'algorithme recommandé (sections 3 à 5, cœur de ce RFC) aura donc l'allure suivante :
192.0.2.1
et
2001:db8::1
. Il sait donc qu'il a plusieurs
adresses, de famille différente.SYN
à
2001:db8::1
, avec un très court délai de garde.SYN
à 192.0.2.1
.SYN+ACK
en échange,
l'initiateur réplique par un ACK
et la connexion
TCP est établie.Si le répondeur réagit à une vitesse normale en IPv6, la connexion sera établie en IPv6. Sinon, on passera vite en IPv4, et l'utilisateur humain ne s'apercevra de rien. Naturellement, si le DNS n'avait rapporté qu'une seule adresse (v4 ou v6), on reste à l'algorithme traditionnel (« essayer, patienter, ré-essayer »).
Maintenant, les détails. D'abord, le DNS (section 3 de notre RFC). Pour récupérer les adresses appartenant aux deux familles (IPv4 et IPv6), il faut envoyer deux requêtes, de type A et AAAA. Pas de délai entre les deux, et le AAAA en premier, recommande le RFC. Notez qu'il n'existe pas de type de requête DNS pour avoir les deux enregistrements d'un coup, il faut donc deux requêtes.
Il ne faut pas attendre d'avoir la réponse aux deux avant de commencer à tenter d'établir une connexion. En effet, certains pare-feux configurés avec les pieds bloquent les requêtes AAAA, qui vont finir par timeouter. Du point de vue du programmeur, cela signifie qu'il faut faire les deux requêtes DNS dans des fils différents (ou des goroutines différentes en Go), ou bien, utiliser une API asynchrone, comme getdns. Ensuite, si on reçoit la réponse AAAA mais pas encore de A, on essaye tout de suite de se connecter, si on a la réponse A, on attend quelques millisecondes la réponse AAAA puis, si elle ne vient pas, tant pis, on essaie en IPv4. (La durée exacte de cette attente est un des paramètres réglables de l'algorithme. Il se nomme Resolution Delay et sa valeur par défaut recommandée est de 50 ms.)
À propos de DNS, notez que le RFC recommande également de privilégier IPv6 pour le transport des requêtes DNS vers les résolveurs (on parle bien du transport des paquets DNS, pas du type des données demandées). Ceci dit, ce n'est pas forcément sous le contrôle de l'application.
Une fois récupérées les adresses, on va devoir les trier selon l'ordre de préférence. La section 4 décrit comment cela se passe. Rappelons qu'il peut y avoir plusieurs adresses de chaque famille, pas uniquement une v4 et une v6, et qu'il est donc important de gérer une liste de toutes les adresses reçues (imaginons qu'on ne récupère que deux adresses v4 et aucune v6 : l'algorithme des globes oculaires heureux est quand même crucial car il est parfaitement possible qu'une des adresses v4 ne marche pas).
Pour trier, le RFC recommande de suivre les règles du RFC 6724, section 6. Si le client a un état (une mémoire des connexions précédentes, ce qui est souvent le cas chez les clients qui restent longtemps à tourner, un navigateur Web, par exemple), il peut ajouter dans les critères de tri le souvenir des succès (ou échecs) précédents, ainsi que celui des RTT passés. Bien sûr, un changement de connectivité (détecté par le DNA des RFC 4436 ou RFC 6059) doit entraîner un vidage complet de l'état (on doit oublier ce qu'on a appris, qui n'est plus pertinent).
Dernier détail sur le tri : il faut mêler les adresses des deux familles. Imaginons un client qui récupère trois adresses v6 et trois v4, client qui donne la priorité à IPv4, mais dont la connexion IPv4 est défaillante. Si sa liste d'adresses à tester comprend les trois adresses v4 en premier, il devra attendre trois essais avant que cela ne marche. Il faut donc plutôt créer une liste {une adressse v4, une adresse v6, une adresse v4…}. Le nombre d'adresses d'une famille à inclure avant de commencer l'autre famille est le paramètre First Address Family Count, et il vaut un par défaut.
Enfin, on essaie de se connecter en envoyant des paquets
TCP SYN (section 5). Il est important de ne pas tester IPv4 tout de suite. Les
premiers algorithmes « bonheur des globes oculaires » envoyaient
les deux paquets SYN
en même temps, gaspillant des ressources réseau et
serveur. Ce double essai faisait que les équipements IPv4 du réseau
avaient autant de travail qu'avant, alors qu'on aurait souhaité les
retirer du service petit à petit. En outre, ce test simultané fait
que, dans la moitié des cas, la connexion sera établie en IPv4,
empêchant de tirer profit des avantages d'IPv6 (cf. RFC 6269). Donc, on doit tester en IPv6
d'abord, sauf si on se souvient des tentatives précédentes (voir
plus loin la variante « avec état ») ou bien si l'administrateur
système a délibérement configuré la machine pour préférer IPv4.
Après chaque essai, on attend pendant une durée paramétrable, Connection Attempt Delay, 250 ms par défaut (bornée par les paramètres Minimum Connection Attempt Delay, 100 ms par défaut, qu'on ne devrait jamais descendre en dessous de 10 ms, et Maximum Connection Attempt Delay, 2 s par défaut).
L'avantage de cet algorithme « IPv6 d'abord puis rapidement basculer en IPv4 » est qu'il est sans état : l'initiateur n'a pas à garder en mémoire les caractéristiques de tous ses correspondants. Mais son inconvénient est qu'on recommence le test à chaque connexion. Il existe donc un algorithme avec état (cf. plus haut), où l'initiateur peut garder en mémoire le fait qu'une machine (ou bien un préfixe entier) a une adresse IPv6 mais ne répond pas aux demandes de connexion de cette famille. Le RFC recommande toutefois de re-essayer IPv6 au moins toutes les dix minutes, pour voir si la situation a changé.
Une conséquence de l'algorithme recommandé est que, dans
certains cas, les deux connexions TCP (v4 et
v6) seront établies (si le SYN
IPv6 voyage
lentement et que la réponse arrive après que l'initiateur de la
connexion se soit impatienté et soit passé à IPv4). Cela peut être
intéressant dans certains cas rares, mais le RFC recommande plutôt
d'abandonner la connexion perdante (la deuxième). Autrement, cela
pourrait entraîner des problèmes avec, par exemple, les sites Web
qui lient un cookie à l'adresse IP du
client, et seraient surpris de voir deux connexions avec des
adresses différentes.
La section 9 du RFC rassemble quelques derniers problèmes
pratiques. Par exemple, notre algorithme des globes oculaires
heureux ne prend en compte que l'établissement de la connexion. Si
une adresse ne marche pas du tout, il choisira rapidement la
bonne. Mais si une adresse a des problèmes de
MTU et pas l'autre, l'établissement de la
connexion, qui ne fait appel qu'aux petits paquets TCP
SYN
, se passera bien alors que le reste de
l'échange sera bloqué. Une solution possible est d'utiliser
l'algorithme du RFC 4821.
D'autre part, l'algorithme ne tient compte que de la possibilité d'établir une connexion TCP, ce qui se fait typiquement uniquement dans le noyau du système d'exploitation du serveur. L'algorithme ne garantit pas qu'une application écoute, et fonctionne.
Parmi les problèmes résiduels, notez que l'algorithme des globes oculaires heureux est astucieux, mais tend à masquer les problèmes (section 9.3). Si un site Web publie les deux adresses mais que sa connectivité IPv6 est défaillante, aucun utilisateur ne lui signalera puisque, pour eux, tout va bien. Il est donc recommandé que l'opérateur fasse des tests de son côté pour repérer les problèmes (le RFC 6555 recommandait que le logiciel permette de débrayer cet algorithme, afin de tester la connectivité avec seulement v4 ou seulement v6, ou bien que le logiciel indique quelque part ce qu'il a choisi, pour mieux identifier d'éventuels problèmes v6.)
Pour le délai entre le premier SYN
IPv6 et le
premier SYN
IPv4, la section 5 donne des
idées quantitatives en suggérant 250 ms entre deux essais. C'est
conçu pour être quasiment imperceptible à un
utilisateur humain devant son navigateur Web, tout en évitant de
surcharger le réseau inutilement. Les algorithmes avec état ont le
droit d'être plus impatients, puisqu'ils peuvent se souvenir des
durées d'établissement de connexion précédents.
Notez que les différents paramètres réglables indiqués ont des valeurs par défaut, décrites en section 8, et qui ont été déterminées empiriquement.
Si vous voulez une meilleure explication de la version 2 des globes oculaires heureux, il y a cet exposé au RIPE.
Enfin, les implémentations. Notez que les vieilles mises en œuvre du RFC 6555 (et présentées à la fin de mon précédent article) sont toujours conformes à ce nouvel algorithme, elles n'en utilisent simplement pas les raffinements. Les versions récentes de macOS (Sierra) et iOS (10) mettent en œuvre notre RFC, ce qui est logique, puisqu'il a été écrit par des gens d'Apple (l'annonce est ici, portant même sur des versions antérieures). Apple en a d'ailleurs profité pour breveter cette technologie. À l'inverse, un exemple récent de logiciel incapable de gérer proprement le cas d'un pair ayant plusieurs adresses IP est Mastodon (cf. bogue #3762.)
Dans l'annexe A, vous trouverez la liste complète des importants changements depuis le RFC 6555. Le précédent RFC n'envisageait qu'un seul cas, deux adresses IP, une en v4, l'autre en v6. Notre nouveau RFC 8305 est plus riche, augmente le parallélisme, et ajoute :
Date de publication du RFC : Décembre 2017
Auteur(s) du RFC : K. Oku (Fastly)
Expérimental
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 20 décembre 2017
Lorsqu'un serveur HTTP répond, la réponse contient souvent des liens vers d'autres ressources. Un exemple typique est celui de la page Web dont le chargement va déclencher le chargement de feuilles de style, de JavaScript, etc. Pour minimiser la latence, il serait intéressant de prévenir le client le plus tôt possible. C'est le but de ce RFC, qui décrit le code de retour intérimaire 103, qui prévient le client qu'il peut tout de suite commencer à charger ces ressources supplémentaires.
Il existe un type de lien pour cela,
preload
, décrit par ses auteurs et
enregistré dans le registre des types de
liens (cf. RFC 8288). Il peut être
utilisé dans la réponse « normale » :
HTTP/1.1 200 OK Date: Fri, 26 May 2017 10:02:11 GMT Content-Length: 1234 Content-Type: text/html; charset=utf-8 Link: </main.css>; rel="preload"; as="style" Link: </script.js>; rel="preload"; as="script"
Mais cela ne fait pas gagner grand'chose : une toute petite fraction de seconde après, le client HTTP verra arriver le source HTML et pourra y découvrir les liens. On voudrait renvoyer tout de suite la liste des ressources à charger, sans attendre que le serveur ait fini de calculer la réponse (ce qui peut prendre du temps, s'il faut dérouler mille lignes de Java et plein de requêtes SQL…)
Le nouveau code de retour, 103, lui, peut être envoyé immédiatement, avec la liste des ressources. Le client peut alors les charger, tout en attendant le code de retour 200 qui indiquera que la ressource principale est prête. (Les codes de retour commençant par 1, comme 103, sont des réponses temporaires, « pour information », en attendant le succès, annoncé par un code commençant par 2. Cf. RFC 7231, sections 6.2 et 6.3.) La réponse HTTP utilisant le nouveau code ressemblera à :
HTTP/1.1 103 Early Hints Link: </main.css>; rel="preload"; as="style" Link: </script.js>; rel="preload"; as="script" HTTP/1.1 200 OK Date: Fri, 26 May 2017 10:02:11 GMT Content-Length: 1234 Content-Type: text/html; charset=utf-8 Link: </main.css>; rel="preload"; as="style" Link: </script.js>; rel="preload"; as="script"
Les détails, maintenant (section 2 du RFC). 103 indique au client qu'il y aura une série de liens vers des ressources supplémentaires qu'il peut être intéressant, par exemple, de charger tout de suite. Les liens finaux seront peut-être différents (dans l'exemple ci-dessus, ils sont identiques). 103 est juste une optimisation, pas une obligation. (Hint = suggestion.) Les liens qu'il indique ne font pas autorité. Le serveur peut indiquer des liens supplémentaires, ne pas indiquer des liens qui étaient dans la réponse 103, indiquer des liens différents, etc.
Il peut même y avoir plusieurs 103 à la suite, notamment si un relais sur le trajet ajoute le sien, par exemple en se basant sur une réponse qu'il avait gardée en mémoire. 103 n'est en effet pas toujours envoyé par le serveur d'origine de la ressource, il peut l'être par un intermédiaire. Voici un exemple qui donne une idée des variantes possibles :
HTTP/1.1 103 Early Hints Link: </main.css>; rel="preload"; as="style" HTTP/1.1 103 Early Hints Link: </style.css>; rel="preload"; as="style" Link: </script.js>; rel="preload"; as="script" HTTP/1.1 200 OK Date: Fri, 26 May 2017 10:02:11 GMT Content-Length: 1234 Content-Type: text/html; charset=utf-8 Link: </main.css>; rel="preload"; as="style" Link: </newstyle.css>; rel="preload"; as="style" Link: </script.js>; rel="preload"; as="script"
On voit que la réponse finale n'est ni la première suggestion, ni la deuxième (ni une union des deux).
Note pour les programmeurs Python/WSGI. Je ne suis pas arrivé à utiliser ce code « intérimaire » avec WSGI, cela ne semble pas possible en WSGI. Mais on trouvera sans doute d'autres implémentations…
Le code 103 est désormais dans le registre IANA des codes de retour.
Date de publication du RFC : Décembre 2017
Auteur(s) du RFC : T. Bray (Textuality)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF jsonbis
Première rédaction de cet article le 14 décembre 2017
Il existe une pléthore de langages pour décrire des données structurées. JSON, normalisé dans ce RFC (qui succède au RFC 7159, avec peu de changements), est actuellement le plus à la mode. Comme son concurrent XML, c'est un format textuel, et il permet de représenter des structures de données hiérarchiques.
À noter que JSON doit son origine, et son nom complet (JavaScript Object Notation) au langage de programmation JavaScript, dont il est un sous-ensemble (enfin, approximativement). La norme officielle de JavaScript est à l'ECMA, dans ECMA-262. JSON est dans la section 24.5 de ce document mais est aussi dans ECMA-404, qui lui est réservé. Les deux normes, ce RFC et la norme ECMA, sont écrites de manière différente mais, en théorie, doivent aboutir au même résultat. ECMA et l'IETF sont censés travailler ensemble pour résoudre les incohérences (aucune des deux organisations n'a, officiellement, le dernier mot).
Contrairement à JavaScript, JSON n'est
pas un langage de programmation, seulement un langage de
description de données, et il ne peut donc pas servir de véhicule
pour du code
méchant (sauf si on fait des bêtises comme de
soumettre du texte JSON à eval()
, cf. section
12 et erratum
#3607 qui donne des détails sur cette vulnérabilité).
Voici un exemple, tiré du RFC, d'un objet exprimé en JSON :
{ "Image": { "Width": 800, "Height": 600, "Title": "View from 15th Floor", "Thumbnail": { "Url": "http://www.example.com/image/481989943", "Height": 125, "Width": "100" }, "IDs": [116, 943, 234, 38793] } }
Les détails de syntaxe sont dans la section 2 du RFC. Cet objet
d'exemple a un seul champ, Image
, qui est un
autre objet (entre { et }) et qui a plusieurs champs. (Les objets
sont appelés dictionnaires ou maps
dans d'autres langages.) L'ordre des éléments de l'objet n'est pas
significatif (certains analyseurs JSON le conservent, d'autres
pas). Un de ces champs, IDs
, a pour valeur un
tableau (entre [ et ]). Les éléments d'un
tableau ne sont pas forcément du même type (section 5).
Un texte JSON n'est pas forcément un objet ou un tableau, par exemple :
"Hello world!"
est un texte JSON légal (composé d'une chaîne de caractères en tout
et pour tout). Une des conséquences est qu'un lecteur de JSON qui lit au fil de l'eau peut
ne pas savoir si le texte est fini ou pas (il ne suffit pas de
compter les crochets et accolades). À part les objets, les tableaux
et les chaînes de caractères, un texte JSON peut être un nombre, ou
bien un littéral, false
,
true
ou null
.
Et quel encodage utiliser pour les textes JSON (section 8) ? Le RFC 4627 était presque muet à ce sujet. Cette question est désormais plus développée. Le jeu de caractères est toujours Unicode et l'encodage est obligatoirement UTF-8 dès qu'on envoie du JSON par le réseau (bien des mises en œuvre de JSON ne peuvent en lire aucun autre). Les textes JSON transmis par le réseau ne doivent pas utiliser de BOM.
Lorsqu'on envoie du JSON par le réseau, le type MIME à utiliser est application/json
.
Autre problème classique d'Unicode, la comparaison de chaînes de
caractères. Ces comparaisons doivent se faire selon les caractères
Unicode et pas selon les octets (il y a plusieurs façons de
représenter la même chaîne de caractères, par exemple
foo*bar
et foo\u002Abar
sont la même chaîne).
JSON est donc un format simple, il n'a même pas la possibilité de commentaires dans le fichier... Voir sur ce sujet une intéressante compilation.
Le premier RFC décrivant JSON était le RFC 4627, remplacé ensuite par le RFC 7159. Quels changements apporte cette troisième révision (annexe A) ? Elle corrige quelques erreurs, résout quelques incohérences avec le texte ECMA, et donne des avis pratiques aux programmeurs. Les principaux changements :
Voici un exemple d'un programme Python pour écrire un objet Python en JSON (on notera que la syntaxe de Python et celle de JavaScript sont très proches) :
import json objekt = {u'Image': {u'Width': 800, u'Title': u'View from Smith\'s, 15th Floor, "Nice"', u'Thumbnail': {u'Url': u'http://www.example.com/image/481989943', u'Width': u'100', u'Height': 125}, u'IDs': [116, 943, 234, 38793], u'Height': 600}} # Example from RFC 4627, lightly modified print(json.dumps(objekt))
Et un programme pour lire du JSON et le charger dans un objet Python :
import json # One backslash for Python, one for JSON objekt = json.loads(""" { "Image": { "Width": 800, "Height": 600, "Title": "View from Smith's, 15th Floor, \\\"Nice\\\"", "Thumbnail": { "Url": "http://www.example.com/image/481989943", "Height": 125, "Width": "100" }, "IDs": [116, 943, 234, 38793] } } """) # Example from RFC 4267, lightly modified print(objekt) print("") print(objekt["Image"]["Title"])
Le code ci-dessus est très simple car Python (comme Perl ou
Ruby ou, bien sûr,
JavaScript) a un typage complètement
dynamique. Dans les langages où le typage est plus statique, c'est
moins facile et on devra souvent utiliser des méthodes dont
certains programmeurs se méfient, comme des conversions de types à
l'exécution. Si vous voulez le faire en Go, il existe un bon
article d'introduction au paquetage standard
json
. Un exemple en Go figure plus loin, pour
analyser la liste des stations de la RATP.
Pour Java, qui a le même « problème » que Go, il existe une quantité impressionnante de bibliothèques différentes pour faire du JSON (on trouve en ligne plusieurs tentatives de comparaison). J'ai utilisé JSON Simple. Lire un texte JSON ressemble à :
import org.json.simple.*; ... Object obj=JSONValue.parse(args[0]); if (obj == null) { // May be use JSONParser instead, it raises an exception when there is a problem System.err.println("Invalid JSON text"); System.exit(1); } else { System.out.println(obj); } JSONObject obj2=(JSONObject)obj; // java.lang.ClassCastException if not a JSON object System.out.println(obj2.get("foo")); // Displays member named "foo"
Et le produire :
JSONObject obj3=new JSONObject(); obj3.put("name","foo"); obj3.put("num",new Integer(100)); obj3.put("balance",new Double(1000.21)); obj3.put("is_vip",new Boolean(true));
Voyons maintenant des exemples réels avec divers outils de traitement de JSON. D'abord, les données issues du service de vélos en libre-service Vélo'v. C'est un gros JSON qui contient toutes les données du système. Nous allons programmer en Haskell un programme qui affiche le nombre de vélos libres et le nombre de places disponibles. Il existe plusieurs bibliothèques pour faire du JSON en Haskell mais Aeson semble la plus utilisée. Haskell est un langage statiquement typé, ce qui est loin d'être idéal pour JSON. Il faut déclarer des types correspondant aux structures JSON :
data Velov = Velov {values :: [Station]} deriving Show instance FromJSON Velov where parseJSON (Object v) = Velov <$> (v .: "values") data Station = Station {stands :: Integer, bikes :: Integer, available :: Integer} deriving Show data Values = Values [Station]
Mais ça ne marche pas : les nombres dans le fichier JSON ont été
représentés comme des chaînes de caractères ! (Cela illustre un
problème fréquent dans le monde de JSON et de
l'open data : les données
sont de qualité technique très variable.) On doit donc les
déclarer en String
:
data Station = Station {stands :: String, bikes :: String, available :: String} deriving Show
Autre problème, les données contiennent parfois la chaîne de
caractères None
. Il faudra donc filtrer avec
la fonction Haskell filter
. La fonction
importante filtre les données, les convertit en entier, et en fait
la somme grâce à foldl
:
sumArray a = show (foldl (+) 0 (map Main.toInteger (filter (\i -> i /= "None") a)))
Le programme
complet est velov.hs
. Une fois compilé, testons-le :
% curl -s https://download.data.grandlyon.com/ws/rdata/jcd_jcdecaux.jcdvelov/all.json | ./velov "Stands: 6773" "Bikes: 2838" "Available: 3653"
Je n'ai pas utilisé les dates contenues dans ce fichier mais on peut noter que, si elles sont exprimées en ISO 8601 (ce n'est hélas pas souvent le cas), c'est sans indication du fuseau horaire (celui en vigueur à Lyon, peut-on supposer).
Un autre exemple de mauvais fichier JSON est donné par
Le Monde avec la base
des députés français. Le fichier
est du JavaScript, pas du JSON (il commence par une déclaration
JavaScript var datadep = {
…) et il contient
plusieurs erreurs de syntaxe (des apostrophes qui n'auraient pas dû
être échappées).
Voyons maintenant un traitement avec le programme spécialisé
dans JSON, jq. On va servir du service de
tests TLS
, dont les résultats sont
consultables avec un navigateur Web, mais également téléchargeables
en JSON. Par exemple,
https://tls.imirhil.fr/
https://tls.imirhil.fr/https/www.bortzmeyer.org.json
donne accès aux résultats des tests pour la version HTTPS de ce blog :
% curl -s https://tls.imirhil.fr/https/www.bortzmeyer.org.json| jq '.date' "2017-07-23T14:10:25.760Z"
Notons qu'au moins une clé d'un objet JSON n'est pas nommée
uniquement avec des lettres et chiffres, la clé
$oid
. jq n'aime pas cela :
% curl -s https://tls.imirhil.fr/https/www.bortzmeyer.org.json| jq '._id.$oid' jq: error: syntax error, unexpected '$', expecting FORMAT or QQSTRING_START (Unix shell quoting issues?) at <top-level>, line 1: ._id.$oid jq: 1 compile error
Il faut mettre cette clé entre guillemets :
% curl -s https://tls.imirhil.fr/https/bortzmeyer.org.json| jq '."_id"."$oid"' "596cb76c2525939a3b34120f"
Toujours avec jq, les données de la Deutsche
Bahn, en
. C'est
du GeoJSON (RFC 7946),
un profil de JSON. Ici, on cherche la gare de Ratisbonne :
http://data.deutschebahn.com/dataset/data-streckennetz
% jq '.features | map(select(.properties.geographicalName == "Regensburg Hbf"))' railwayStationNodes.geojson [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [ 12.09966625451, 49.011754555481 ] }, "properties": { "id": "SNode-1492185", "formOfNode": "railwayStop", "railwayStationCode": "NRH", "geographicalName": "Regensburg Hbf", ...
Toujours avec jq, on peut s'intéresser aux données officielles
états-uniennes en
https://catalog.data.gov/dataset?res_format=JSON
. Prenons
les données
sur la délinquance à Los
Angeles (j'ai bien dit délinquance et pas criminalité,
celui qui traduit crime par crime ne connait pas
l'anglais, ni le
droit). https://data.lacity.org/api/views/y8tr-7khq/rows.json?accessType=DOWNLOAD
est un très gros fichier (805 Mo) et jq
n'y arrive pas :
% jq .data la-crime.json error: cannot allocate memory
Beaucoup de programmes qui traitent le JSON ont ce problème (un
script Python produit un MemoryError
) : ils
chargent tout en mémoire et ne peuvent donc pas traiter des données
de grande taille. Il faut donc éviter de produire de trop gros
fichiers JSON.
Si vous voulez voir un vrai exemple en Python, il y a mon article sur le traitement de la base des codes postaux. Cette base peut évidemment aussi être examinée avec jq. Et c'est l'occasion de voir du GeoJSON :
% jq '.features | map(select(.properties.nom_de_la_commune == "LE TRAIT"))' laposte_hexasmal.geojson [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [ 0.820017087099, 49.4836816397 ] }, "properties": { "nom_de_la_commune": "LE TRAIT", "libell_d_acheminement": "LE TRAIT", "code_postal": "76580", "coordonnees_gps": [ 49.4836816397, 0.820017087099 ], "code_commune_insee": "76709" } } ]
J'avais promis plus haut un exemple écrit en
Go. On va utiliser la liste des positions
géographiques des stations RATP, en
https://data.ratp.fr/explore/dataset/positions-geographiques-des-stations-du-reseau-ratp/table/?disjunctive.stop_name&disjunctive.code_postal&disjunctive.departement
.
Le programme Go read-ratp.go
va afficher le nombre
de stations et la liste :
% ./read-ratp < positions-geographiques-des-stations-du-reseau-ratp.json 26560 stations Achères-Ville Alésia Concorde ...
Comme déjà indiqué, c'est plus délicat en Go que dans un langage très dynamique comme Python. Il faut construire à l'avance des structures de données :
type StationFields struct { Fields Station } type Station struct { Stop_Id int Stop_Name string }
Et toute violation du « schéma » des données par le fichier JSON (quelque chose qui arrive souvent dans la nature) plantera le programme.
Si on veut beaucoup de fichiers JSON, le service de données
ouvertes officielles data.gouv.fr permet de
sélectionner des données par format. Ainsi,
donnera tous les fichiers en JSON. Prenons au hasard les frayères du centre de la France,
https://www.data.gouv.fr/fr/datasets/?format=JSON
https://www.data.gouv.fr/fr/datasets/points-de-frayere-des-especes-de-linventaire-frayeres-des-regions-centre-et-poitou-charentes/
.
Il est encodé en ISO-8859-1, ce qui est
explicitement interdit par le RFC. Bref, il faut encore rappeler
qu'on trouve de tout dans le monde JSON et que l'analyse de
fichiers réalisés par d'autres amène parfois des surprises.
On peut aussi traiter du JSON dans
PostgreSQL. Bien sûr, il est toujours
possible (et sans doute parfois plus avantageux) d'analyser le JSON
avec une des bibliothèques présentées plus haut, et de mettre les
données dans
une base PostgreSQL. Mais on peut aussi mettre le JSON directement
dans PostgreSQL et ce SGBD fournit un
type de données JSON et quelques
fonctions permettant de l'analyser. Pour les données, on va
utiliser les centres de santé en Bolivie,
en
http://geo.gob.bo/geoserver/web/?wicket:bookmarkablePage=:org.geoserver.web.demo.MapPreviewPage
. On
crée la table :
CREATE TABLE centers ( ID serial NOT NULL PRIMARY KEY, info json NOT NULL );
Si
on importe le fichier JSON bêtement dans PostgreSQL (psql -c "copy centers(info) from stdin" mydb < centro-salud.json
), on récupère
un seul enregistrement. Il faut donc éclater le fichier JSON en
plusieurs lignes. On peut utiliser les extensions à SQL de
PostgreSQL pour cela, mais j'ai préféré me servir de jq :
% jq --compact-output '.features | .[]' centro-salud.json | psql -c "copy centers(info) from stdin" mydb COPY 50
On peut alors faire des requêtes dans le JSON, avec l'opérateur
->
. Ici, le nom des centres (en jq, on
aurait écrit .properties.nombre
) :
mydb=> SELECT info->'properties'->'nombre' AS Nom FROM centers; nom ------------------------------------------ "P.S. ARABATE" "INSTITUTO PSICOPEDAGOGICO" "HOSPITAL GINECO OBSTETRICO" "HOSPITAL GASTROENTEROLOGICO" "C.S. VILLA ROSARIO EL TEJAR" "C.S. BARRIO JAPON" "C.S. SAN ANTONIO ALTO (CHQ)" "C.S. SAN JOSE (CHQ)" "C.S. SAN ROQUE" ...
Bon, sinon, JSON dispose d'une page Web officielle, où vous trouverez plein d'informations. Pour tester dynamiquement vos textes JSON, il y a ce service.
Auteur(s) du livre : Jean-Paul Demoule
Éditeur : Fayard
978-2-213-67757-6
Publié en 2017
Première rédaction de cet article le 10 décembre 2017
Quand on parle de la préhistoire, tout le monde voit ce que c'est : des types vêtus de peaux de bêtes partant chercher du feu ou chassant le mammouth, ou encore inventant plein de choses. Idem pour l'histoire, on a plein de livres et de films sur tous les aspects de l'histoire, depuis qu'on a inventé l'écriture, jusqu'à Stéphane Bern et Franck Ferrand. Mais entre les deux ? Entre les deux, dit l'auteur, il y a « dix millénaires zappés ». La période qui va en gros de 13 000 à 3 000 AEC : qu'en savons-nous ?
Le livre fait le tour des innovations qui ont marqué cette période un peu oubliée : l'agriculture, bien sûr, les villages, les outils en métal, mais aussi la religion organisée. Il y avait bien sûr des croyances depuis longtemps mais, pendant ces dix millénaires, on commence à en voir des manifestations partout, y compris au prix d'efforts colossaux, inimaginables quand on était chasseur-cueilleur. Et cela ne s'arrête pas là. C'est aussi pendant cette période, dix mille ans avant Macron, qu'on invente les inégalités. Avant, il y avait sans doute des chefs. Mais l'examen des tombes ne permettait pas de voir des vraies inégalités. Pendant les dix millénaires, on voit au contraire apparaitre la distinction entre ceux qu'on enterre simplement, et ceux dont le tombeau recèle des richesses qui feront la joie des archéologues. L'auteur examine comment des gens qui vivaient à peu près libres ont pu accepter cet asservissement, et note que cela n'a pas été un processus linéaire, mais qu'il y a eu de nombreux retours à des enterrements égalitaires (suite à des révoltes contre les chefs ?)
Mais la civilisation ne s'est pas arrêtée à la religion et aux chefs. On a aussi inventé la guerre. Oui, bien sûr, il y avait de la violence avant. Mais des armées entières s'affrontant, alors que la plupart des soldats n'avaient aucune raison personnelle de se battre, ça date aussi de cette époque. (L'Europe en dehors de la Grèce était en retard sur la voie de la civilisation, et n'a donc connu sa première grande bataille que vers 1 200 AEC.) Effet amusant de la guerre, c'est aussi pendant cette période que le cannibalisme est devenu tabou…
Et la domination masculine ? A-t-elle toujours existé ? Et, si non, a-t-elle été inventée pendant cette période ? Contrairement aux batailles, cela ne laisse pas forcément beaucoup de traces analysables. Là, on est forcément davantage dans la spéculation, comme les théories de Bachofen, qui cherchait à tout prix à prouver que la domination masculine n'est pas universelle (alors qu'apparemment, elle l'est).
Et je vous laisse découvrir dans ce livre les autres étonnantes innovations de l'époque.
Date de publication du RFC : Décembre 2017
Auteur(s) du RFC : L. Velvindron (Hackers.mu), M. Baushke (Juniper Networks)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF curdle
Première rédaction de cet article le 6 décembre 2017
Un RFC de moins de quatre pages, boilerplate administratif inclus, pour passer la taille minimum des modules des groupes Diffie-Hellman utilisés par SSH, de 1 024 bits à 2 048.
L'échange Diffie-Hellman dans SSH est décrit dans le RFC 4419, que notre nouveau RFC met à jour. C'est dans le RFC 4419 (sa section 3) que se trouvait la recommandation d'accepter au moins 1 024 bits pour le module du groupe. Or, cette taille est bien faible face aux attaques modernes comme Logjam.
Voilà, c'est tout, on remplace « minimum 1 024 bits » par
« minimum 2 048 » et on peut continuer à utiliser SSH. Si vous
êtes utilisateur d'OpenSSH, la commande de
génération de clés, ssh-keygen
, peut
également générer ces modules (cf. la section Moduli
generation dans le manuel.) Les versions un peu
anciennes ne vous empêchent pas de faire des modules bien trop
petits. Ainsi, sur une version 7.2 :
% ssh-keygen -G moduli-512.candidates -b 512 Fri Oct 20 20:13:49 2017 Sieve next 4177920 plus 511-bit Fri Oct 20 20:14:51 2017 Sieved with 203277289 small primes in 62 seconds Fri Oct 20 20:14:51 2017 Found 3472 candidates % ssh-keygen -G moduli-256.candidates -b 256 Too few bits: 256 < 512 modulus candidate generation failed
Le RGS recommande quant à lui 3 072 bits minimum (la règle exacte est « RègleLogp-1. La taille minimale de modules premiers est de 2048 bits pour une utilisation ne devant pas dépasser l’année 2030. RègleLogp-2. Pour une utilisation au delà de 2030, la taille minimale de modules premiers est de 3072 bits. »)
Enfin, la modification d'OpenSSH pour se conformer à ce RFC est juste un changement de la définition de DH_GRP_MIN.
Date de publication du RFC : Décembre 2017
Auteur(s) du RFC : J. Brzozowski (Comcast
Cable), G. Van De Velde (Nokia)
Pour information
Réalisé dans le cadre du groupe de travail IETF v6ops
Première rédaction de cet article le 5 décembre 2017
Ce court RFC explique comment (et pourquoi) attribuer un préfixe IPv6 unique à chaque machine, même quand le média réseau où elle est connectée est partagé avec d'autres machines.
Ce RFC s'adresse aux gens qui gèrent un grand réseau de couche 2, partagé par de nombreux abonnés. Un exemple est celui d'un serveur dédié connecté à un Ethernet partagé avec les serveurs d'autres clients. Un autre exemple est celui d'une connexion WiFi dans un congrès ou un café. Dans les deux cas, la pratique sans doute la plus courante aujourd'hui est de donner une seule adresse IPv6 à la machine (ou, ce qui revient au même, un préfixe de 128 bits). C'est cette pratique que veut changer ce RFC. Le but est de mieux isoler les clients les uns des autres, et de bien pouvoir gérer les abonnements et leur utilisation. (Justement le problème de Comcast, dont un des auteurs du RFC est un employé, cf. section 1.) Les clients ne se connaissent en effet pas les autres et il est important que les actions d'un client ne puissent pas trop affecter les autres (et qu'on puisse attribuer les actions à un client précis, pour le cas où ces actions soient illégales). En outre, certaines options d'abonnement sont « par client » (section 3, qui cite par exemple le contrôle parental, ou la qualité de service, qui peut être plus faible pour ceux qui ne paient pas le tarif « gold ».)
Si chaque client a un préfixe IPv6 à lui (au lieu d'une seule adresse IP), toute communication entre clients passera forcément par le routeur géré par l'opérateur, qui pourra ainsi mieux savoir ce qui se passe, et le contrôler. (Les lecteurs férus de routage ont noté que le client, s'il est administrateur de sa machine, peut toujours changer les tables de routage, mais cela n'affectera que le trafic aller, le retour passera toujours par le routeur. De toute façon, je soupçonne que la technique décrite dans ce RFC ne marche que si le réseau donne un coup de main, pour isoler les participants.)
Le RFC affirme que cela protègera contre des attaques comme l'épuisement de cache Neighbor Discovery, les redirections malveillantes faites avec ICMP ou les RAcailles (RFC 6104). Cela éviterait de devoir déployer des contre-mesures comme le RA Guard (RFC 6105). Là aussi, il me semble personnellement que ça n'est complètement vrai que si l'attaquant n'est pas administrateur sur sa machine. Ou alors, il faut que le réseau ne soit pas complètement partagé, qu'il y ait un mécanisme de compartimentage.
Les mécanismes décrits ici supposent que la machine du client utilise SLAAC (RFC 4862) pour obtenir une adresse IP. Cette obtention peut aussi passer par DHCP (RFC 8415) mais c'est plus rare, relativement peu de clients étant capable de demander une adresse en DHCP (RFC 7934).
La section 4 du RFC décrit comment le client obtient ce
préfixe. Il va envoyer un message RS (Router
Solicitation, voir le RFC 4861,
section 3) et écouter les réponses, qui lui diront son adresse IP
mais aussi d'autres informations comme les résolveurs
DNS à utiliser (voir RFC 8106). Pas de changement côté client, donc (ce qui
aurait rendu ce mécanisme difficile à déployer). Par contre, côté
« serveur », il y a de légers changements. Le routeur qui reçoit les RS
et génère des RA (Router Advertisement), qu'ils
aient été sollicités ou pas, va devoir les envoyer uniquement à
une machine (puisque chaque client a un préfixe différent : il ne
faut donc pas diffuser bêtement). Comme le RFC 4861 (sections 6.2.4 et 6.2.6) impose que
l'adresse IP de destination soit
ff02::1
(« tous les nœuds IPv6 »), l'astuce
est d'utiliser comme adresse MAC, non pas
l'adresse multicast habituelle, mais une
adresse unicast (RFC 6085). Ainsi, chaque client ne recevra que
son préfixe.
Ce RA contient le préfixe que l'opérateur alloue à ce client particulier. Les options du RA (RFC 4861, section 4.2) sont :
Le bit A étant mis à un, la machine qui a obtenu le préfixe peut s'attribuer une adresse IP à l'intérieur de ce préfixe, , avec SLAAC, comme indiqué dans le RFC 4862. Elle doit suivre la procédure DAD (Duplicate Address Detection, RFC 4862, section 5.4) pour vérifier que l'adresse IP en question n'est pas déjà utilisée.
Voilà, l'essentiel de ce RFC était là. La section 5 concerne quelques détails pratiques, par exemple ce que peut faire la machine client si elle héberge plusieurs machines virtuelles ou containers (en gros, elle alloue leurs adresses en utilisant le préfixe reçu).
Ce mécanisme de préfixe IP spécifique à chaque client de l'opérateur n'est pas sans poser des questions liées à la vie privée, comme l'explique la section 7 du RFC. (Rappelez-vous la section 1, qui disait qu'un des buts de cette technique était de satisfaire aux « obligations légales », autrement dit de pouvoir suivre à la trace ses utilisateurs.) Bien sûr, la machine cliente peut utiliser le système du RFC 8981, mais, ici, il aurait peu d'impact. Même avec un identificateur d'interface temporaire et imprévisible, le préfixe resterait, et identifierait parfaitement le client. Le RFC mentionne (mais sans l'exiger) qu'on peut limiter les dégâts en changeant le préfixe de temps en temps.
Première rédaction de cet article le 2 décembre 2017
Beaucoup de gens utilisent désormais l'AC Let's Encrypt. Ce n'est pas la première autorité de certification qui permet d'avoir un certificat en faisant tout en ligne, ni la première gratuite, mais elle est néanmoins très utilisée (au point de devenir un nouveau SPOF de l'Internet). Par défaut, les outils Let's Encrypt comme certbot créent une nouvelle clé quand le certificat est renouvelé. Dans quels cas est-ce gênant et comment éviter cela ?
Un petit rappel sur les certificats : un certificat, c'est tout bêtement une clé publique, avec quelques métadonnées dont les plus importantes sont la signature de l'AC et la date d'expiration (qui, avec Let's Encrypt, est très rapprochée). Renouveler un certificat, c'est demander une nouvelle signature à l'AC. Si la clé n'est pas trop ancienne, ou n'a apparemment pas été compromise, il n'est pas nécessaire de la changer.
Mais les outils existants le font quand même systématiquement (c'est un choix des outils, comme certbot ou dehydrated, ce n'est pas une obligation du protocole ACME, ni de l'AC Let's Encrypt). Cette décision a probablement été prise pour garantir que la clé soit renouvelée de temps en temps (après tout, il est raisonnable de supposer que, tôt ou tard, elle sera compromise, et ce ne sera pas forcément détecté par le propriétaire).
Et pourquoi est-ce gênant de changer de clé à chaque renouvellement (donc tous les trois mois avec Let's Encrypt) ? Cela ne pose pas de problème pour l'utilisation habituelle d'un serveur HTTPS. Mais c'est ennuyeux si on utilise des techniques de sécurité fondées sur un épinglage de la clé, c'est-à-dire une authentification de la clé publique utilisée. Ces techniques permettent de résoudre une grosse faille de X.509, le fait que n'importe quelle AC, même si vous n'en êtes pas client, puisse émettre un certificat pour n'importe quel domaine. Parmi ces techniques de sécurité :
Si on utilise l'outil certbot, qui est celui officiellement recommandé par Let's Encrypt, la méthode normale d'utilisation est, la première fois :
% sudo certbot certonly --webroot --webroot-path /var/lib/letsencrypt -d www.example.com Saving debug log to /var/log/letsencrypt/letsencrypt.log Obtaining a new certificate Performing the following challenges: http-01 challenge for www.example.com Using the webroot path /var/lib/letsencrypt for all unmatched domains. Waiting for verification... Cleaning up challenges Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem ...
Let's Encrypt a testé la présence du défi sur le serveur, on le voit dans le journal du serveur HTTP :
2600:3000:2710:200::1d - - [13/Sep/2017:16:08:46 +0000] "GET /.well-known/acme-challenge/2IlM1PbP9QZlAA22xvE4Bz5ivJi5nsB5MHz52uY8xT8 HTTP/1.1" 200 532 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
On va alors créer l'enregistrement TLSA (DANE) :
% tlsa --create --selector 1 www.example.com Got a certificate with Subject: /CN=www.example.com _443._tcp.www.example.com. IN TLSA 3 1 1 f582936844ec355cfdfe8d9d1a42e9565940602c71c7abd2c36c732daa64b9db Got a certificate with Subject: /CN=www.example.com _443._tcp.www.example.com. IN TLSA 3 1 1 f582936844ec355cfdfe8d9d1a42e9565940602c71c7abd2c36c732daa64b9db
(L'option --selector 1
est pour faire
apparaitre dans l'enregistrement TLSA la clé publique seulement et
non pas tout le certificat, ce que ferait le sélecteur par défaut,
0. C'est expliqué plus en détail plus loin.)
À ce stade, on a un certificat Let's Encrypt, un enregistrement
DANE qui correspond et tout le monde est heureux :
% tlsa --verify --resolvconf="" www.example.com SUCCESS (Usage 3 [DANE-EE]): Certificate offered by the server matches the TLSA record (x.y.z.t)
Maintenant, si on renouvelle le certificat quelques mois plus tard :
% certbot --quiet --webroot --webroot-path /usr/share/nginx/local-html renew
Cela change la clé. Regardez avec OpenSSL :
# Avant % openssl x509 -pubkey -in /etc/letsencrypt/live/www.example.com/fullchain.pem -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsUd3mG5QK6EdTtYh0oLJ nkIovYkWXV8QLQMvAthzURbyeIlQ8CXeTNCT6odh/VVyMn49IwkRJl6B7YNhYiRz pbmIxxzceAhKNAg6TF/QylHa1HWvHPniZF02NJAXCxMO5Y8EZ7n0s0cGz4XD5PGA XctV6ovA3fR8b2bk9t5N+UHklWvIOT7x0nVXWmWmrXzG0LX/P4+utZJjRR6Kf5/H 9GDXprklFCbdCTBkhyPBgdiJDnqzdb6hB1aBEsAMd/Cplj9+JKtu2/8Pq6MOtQeu 364N+RKcNt4seEr6uMOlRXzWAfOHI51XktJT64in1OHyoeRMV9dOWOLWIC2KAlI2 jwIDAQAB -----END PUBLIC KEY----- # Après % openssl x509 -pubkey -in /etc/letsencrypt/live/www.example.com/fullchain.pem -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MF8Dw3JQ58n8B/GvWYI Vd+CG7PNFA+Ke7B+f9WkzEIUPzAHq7qk1v7dOitD3WsRKndJDPxZAq7JrgOiF/0y 4726HhYR1bXOTziAbk0HzR+HwECo1vz26fqPnNpZ3M46PQFQU9uq2pgHtBwCVMQ+ Hi1pYKnB2+ITl11DBLacSHP7WZZGHXbEqW5Cc710m6aTt18L+OgqxuQSgV+khh+W qWqd2bLq32actLEVmfR4uWX7fh/g6I7/p3ohY7Ax4WC30RfWZk3vLyNc/8R0/67c bVIYWmkDhgXy6UlrV2ZgIO2K8oKiJBMHjnaueHIfu1ktubqM1/u1yLKwXW16UAxm 5QIDAQAB -----END PUBLIC KEY-----
À partir de là, l'enregistrement DANE ne correspond plus, la clé copiée à l'extérieur n'est plus la clé utilisée.
Avec certbot, la solution est de ne pas laisser le client ACME choisir le certificat, mais de fabriquer un CSR et de lui indiquer de l'utiliser systématiquement (cf. la documentation officielle) :
% certbot certonly --webroot -w /usr/share/nginx/local-html -d dns-resolver.yeti.eu.org --csr /etc/letsencrypt-local/yeti-resolver.csr --cert-path /etc/letsencrypt-local/tmp.pem No handlers could be found for logger "certbot.crypto_util" Saving debug log to /var/log/letsencrypt/letsencrypt.log Performing the following challenges: http-01 challenge for dns-resolver.yeti.eu.org Using the webroot path /var/lib/letsencrypt for all unmatched domains. Waiting for verification... Cleaning up challenges Server issued certificate; certificate written to /etc/letsencrypt-local/dns-resolver.yeti.eu.org.pem
Comment on avait fabriqué un CSR ? OpenSSL le permet. Faisons-en un joli, utilisant la cryptographie à courbes elliptiques :
% openssl ecparam -out yeti-resolver.pem -name prime256v1 -genkey % openssl req -new -key yeti-resolver.pem -nodes -days 1000 -out yeti-resolver.csr You are about to be asked to enter information that will be incorporated into your certificate request. ... Organization Name (eg, company) [Internet Widgits Pty Ltd]:Dahu Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []:dns-resolver.yeti.eu.org Email Address []:yeti@eu.org
Avec ce CSR, et en appelant certbot depuis
cron avec les options indiquées plus haut,
indiquant le même CSR (certbot certonly --webroot -w /usr/share/nginx/local-html -d dns-resolver.yeti.eu.org --csr /etc/letsencrypt-local/yeti-resolver.csr --cert-path /etc/letsencrypt-local/tmp.pem
), la
clé reste constante, et DANE et HPKP fonctionnent. Petit
inconvénient : avec ces options, certbot renouvelle le certificat à
chaque fois, même quand ça n'est pas nécessaire. (Depuis l'écriture
initiale de cet article, certbot a ajouté l'option
--reuse-key
, qui résout proprement le problème,
et est donc une meilleure solution que d'utiliser son CSR.)
Et si on utilise dehydrated au lieu de
certbot, comme client ACME ? Là, c'est plus simple, on met dans le fichier de
configuration /etc/dehydrated/config
l'option :
PRIVATE_KEY_RENEW="no"
Et cela suffit. Ceci dit, dehydrated a un gros inconvénient, il est bavard. Quand on le lance depuis cron, il affiche systématiquement plusieurs lignes, même s'il n'a rien à dire.
Pour revenir à la question du choix du sélecteur DANE (RFC 6698 et RFC 7218), il faut
noter que tout renouvellement change le certificat (puisqu'il modifie
au moins la date d'expiration). Il ne faut donc pas utiliser le
sélecteur 0 « Cert
» (qui publie le
condensat du certificat entier dans
l'enregistrement TLSA) mais le sélecteur 1
« SPKI
» (qui ne met que le condensat de la clé).
Le problème existe avec toutes les AC mais est plus aigu
pour Let's Encrypt où on renouvelle souvent. L'annexe A.1.2 du RFC 6698 l'explique bien.
Enfin, un avertissement de sécurité : avec les méthodes indiquées ici, le client ACME ne change plus de clé du tout. C'est donc désormais à vous de penser à créer une nouvelle clé de temps en temps, pour suivre les progrès de la cryptanalyse.
Si vous voulez des exemples concrets,
dns-resolver.yeti.eu.org
(uniquement en
IPv6) utilise certbot, alors que
dns.bortzmeyer.org
et
mercredifiction.bortzmeyer.org
utilisent
dehydrated. Prenons dns.bortzmeyer.org
. Son
enregistrement TLSA :
% dig TLSA _443._tcp.dns.bortzmeyer.org ... ;; ANSWER SECTION: _443._tcp.dns.bortzmeyer.org. 80968 IN TLSA 1 1 1 ( C05BF52EFAB00EF36AC6C8E1F96A25CC2A79CC714F77 055DC3E8755208AAD0E4 ) ... ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Sat Dec 02 17:03:18 CET 2017 ;; MSG SIZE rcvd: 2311
Il déclare une contrainte sur le certificat du serveur (champ Certificate usage à 1, PKIX-EE), ne testant que la clé (champ Selector à 1), l'enregistrement est un condensat SHA-256 (champ Matching type à 1). On peut vérifier que l'enregistrement DANE est correct avec hash-slinger :
% tlsa --verify --resolvconf="" dns.bortzmeyer.org SUCCESS (Usage 1 [PKIX-EE]): Certificate offered by the server matches the one mentioned in the TLSA record and chains to a valid CA certificate (204.62.14.153) SUCCESS (Usage 1 [PKIX-EE]): Certificate offered by the server matches the one mentioned in the TLSA record and chains to a valid CA certificate (2605:4500:2:245b::42)
ou bien avec
.
https://check.sidnlabs.nl/dane/
Première rédaction de cet article le 28 novembre 2017
Le 26 novembre 2017, à la Cité des Sciences à Paris, j'ai fait un exposé lors de l'Ubuntu Party sur la question des critères de choix d'un bon résolveur DNS..
Les Ubuntu Party sont organisées par les bénévoles utilisateurs d'Ubuntu et comprennent une install party (des dizaines de personnes sont passées) et des exposés plus traditionnels. J'ai beaucoup aimé les deux exposés d'Elzen, le premier sur la piraterie et le piratage, et le second une « conférence dont vous êtes le héros », ainsi que l'exposé de Pamputt sur le remarquable Wiktionnaire, projet moins connu que Wikipédia mais tout aussi intéressant !
La question du choix d'un résolveur DNS fait toujours couler beaucoup d'encre sur les réseaux sociaux et a déjà fait l'objet de plusieurs articles sur ce blog, au sujet de Quad9, au sujet de Google Public DNS, à propos des résolveurs publics en général, de l'intérêt d'avoir son propre résolveur, de la protection de la vie privée (RFC 7626), de la censure, etc. Voici les transparents de l'exposé à l'Ubuntu Party :
L'exposé a été filmé, la vidéo est sur PeerTube.
Auteur(s) du livre : Andy Weir
Éditeur : Del Rey
9780091956943
Publié en 2017
Première rédaction de cet article le 21 novembre 2017
Le nouveau roman de l'auteur du Martien (oui, c'est nul de présenter un auteur par son bouquin précédent qui a eu du succès mais je n'avais pas d'autre idée). Il garde le côté « hard-tech », mais tend cette fois vers le polar.
Dans « Le Martien », le héros devait se débrouiller tout seul sur la planète Mars et l'un des principaux intérêts du roman était la description détaillée et correcte scientifiquement des différents problèmes qu'il affrontait, et de comment il les résolvait. Cette fois, ça se passe sur la Lune, et il s'agit de problèmes plus policiers, avec des méchants (très méchants), des complots et des sabotages (qui ont tout de suite un caractère plus dramatique quand il n'y a pas d'atmosphère dehors et qu'on ne peut donc pas sortir pour échapper au danger).
Comme le précédent, ce roman plaira aux geeks scientifiques. Ils y trouveront de la physique et de la chimie en quantité. Si vous le lisez en anglais, pensez à réviser la terminologie, notamment du monde de la soudure, qui joue un rôle essentiel dans le livre. « I wasn't sure what grade of steel [they] were made of, but most grades melt at or below 1450° C. So, just to be safe, my plate and stock rods were Grade 416 with a melting point of 1530° C. »
Mais les autres seront également ravis des personnages (surprenants), de la morale (curieuse), de l'intrigue (à rebondissements) et de l'(abondante) action. Mon fils, regardant par-dessus mon épaule, m'avait dit « c'est encore un livre où le héros affronte des épreuves, les surmonte puis pécho la jolie fille à la fin ? » Eh bien, non, ce n'est pas ça du tout, vous verrez en lisant.
Date de publication du RFC : Novembre 2017
Auteur(s) du RFC : P. Kampanakis (Cisco Systems), M. Suzuki (NICT)
Pour information
Réalisé dans le cadre du groupe de travail IETF mile
Première rédaction de cet article le 20 novembre 2017
Le format IODEF, dont la dernière version est décrite dans le RFC 7970, est un format structuré permettant l'échange de données sur des incidents de sécurité. Cela permet, par exemple, aux CSIRT de se transmettre des données automatiquement exploitables. Ces données peuvent être produites automatiquement (par exemple par un IDS, ou bien issues du remplissage manuel d'un formulaire). IODEF est riche, très riche, peut-être trop riche (certaines classes qu'il définit ne sont que rarement utilisées). Il peut donc être difficile de programmer des outils IODEF, et de les utiliser. (En pratique, il me semble qu'IODEF est peu utilisé.) Ce RFC, officiellement, est donc chargé d'aider ces professionnels, en expliquant les cas les plus courants et les plus importants, et en guidant programmeurs et utilisateurs.
Personnellement, je ne suis pas convaincu du résultat, ce RFC me semble plutôt un pot-pourri de diverses choses qui n'avaient pas été mises dans la norme.
La section 3 du RFC discute de l'utilisation de base d'IODEF. Reprenant la section 7.1 du RFC 7970, elle présente le document IODEF minimum, celui avec uniquement l'information obligatoire :
<?xml version="1.0" encoding="UTF-8"?> <IODEF-Document version="2.00" xml:lang="en" xmlns="urn:ietf:params:xml:ns:iodef-2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.iana.org/assignments/xml-registry/schema/iodef-2.0.xsd"> <Incident purpose="reporting" restriction="private"> <IncidentID name="csirt.example.com">492382</IncidentID> <GenerationTime>2015-07-18T09:00:00-05:00</GenerationTime> <Contact type="organization" role="creator"> <Email> <EmailTo>contact@csirt.example.com</EmailTo> <!-- Pas réellement obligatoire, mais le document serait vraiment sans intérêt sans lui. --> </Email> </Contact> </Incident> </IODEF-Document>
Un tel document, comportant
une instance de la classe Incident
, qui comprend elle-même une
instance de la classe Contact
, serait syntaxiquement correct mais
n'aurait guère d'intérêt pratique. Des documents un peu plus
réalistes figurent dans l'annexe B.
Le programmeur qui génère ou traite des fichiers IODEF n'a pas
forcément à mettre en œuvre la totalité des classes. Il peut se
contenter de ce qui est important pour son ou ses scénarios
d'utilisation. Par exemple, si on travaille sur les
dDoS, la classe Flow
est la plus
importante, puisqu'elle décrit le trafic de l'attaque. (L'annexe
B.2 du RFC contient un fichier IODEF décrivant une attaque faite
avec LOIC. Je l'ai copié ici dans ddos-iodef.xml
.) De même, si
on travaille sur le C&C d'un
logiciel malveillant, les classes
Flow
et ServiceName
sont cruciales. Bref, il faut analyser ce dont on a besoin.
La section 4 du RFC mentionne les extensions à IODEF. Si riche que soit ce format, on peut toujours avoir besoin d'autres informations et c'est pour cela qu'IODEF est extensible. Par exemple, le RFC 5901 décrit une extension à IODEF pour signaler des cas de hameçonnage. Évidemment, on ne doit définir une extension que s'il n'existe pas de moyen existant de stocker l'information dans de l'IODEF standard.
La section 4 rappelle aussi aux développeurs que, certes, IODEF
bénéfice d'un mécanisme d'indication de la
confidentialité (l'attribut
restriction
, qui se trouve dans les deux
exemples que j'ai cité), mais qu'IODEF ne fournit aucun
moyen technique de le faire respecter. Les documents IODEF étant
souvent sensibles, puisqu'ils parlent de problèmes de sécurité, le programmeur qui réalise un système de
traitement de fichiers IODEF doit donc mettre en œuvre des mesures
pratiques de protection de la confidentialité
(chiffrement des fichiers stockés, par
exemple).
Questions mise en œuvre d'IODEF, le RFC 8134 détaille des programmes existants, indique où les récupérer quand ils sont accessibles en ligne, et analyse leurs caractéristiques. C'est le cas par exemple d'iodeflib.
Première rédaction de cet article le 16 novembre 2017
Aujourd'hui a été annoncé la disponibilité du résolveur DNS Quad9 (prononcer « quoi de neuf » en français). C'est un résolveur DNS public, mais dont l'originalité est d'être accessible de manière sécurisée, avec TLS (RFC 7858). (There is also an english version of this article.)
Des résolveurs DNS publics, il y en a plein. Le plus connu est Google Public DNS mais il en existe beaucoup d'autres, avec des politiques et des caractéristiques techniques diverses. Le fait que tant d'utilisateurs se servent aveuglément de Google Public DNS, malgré l'énorme quantité de données que Google connait déjà sur nous est inquiétant. Mais il y aussi un problème technique, commun à la plupart des résolveurs publics : le lien avec eux n'est pas sécurisé. Cela permet les détournements, comme vu en Turquie, ainsi que la surveillance par un tiers.
Au contraire, le nouveau service Quad9, géré par l'organisme sans but lucratif bien connu PCH, qui gère une bonne partie de l'infrastructure du DNS, Quad9, donc, permet un accès par DNS sur TLS (RFC 7858). Cela permet d'éviter l'écoute par un tiers, et cela permet d'authentifier le résolveur (je n'ai pas encore testé ce point, Quad9 ne semble pas distribuer de manière authentifiée ses clés publiques).
Question politique, notons encore que Quad9 s'engage à ne pas stocker votre adresse IP. Et que leur résolveur est un résolveur menteur : il ne répond pas (délibérement) pour les noms de domaines considérés comme lié à des activités néfastes comme la distribution de logiciel malveillant. On peut avoir un résolveur non-menteur en utilisant d'autres adresses mais on perd DNSSEC et surtout Quad9 se met à utiliser alors l'indication du réseau du client (RFC 7871), une mauvaise pratique pour la vie privée. Espérons qu'on aura bientôt une adresse pour les réponses non-menteuses, avec DNSSEC et sans l'indication du réseau du client.
Bon, passons maintenant à la pratique, sur une machine Unix. L'adresse IPv4 de Quad9,
comme son nom l'indique, est 9.9.9.9
. Son
adresse IPv6 est 2620:fe::fe
(cf. la FAQ). D'abord, un accès
classique en UDP en clair :
% dig +nodnssec @9.9.9.9 AAAA irtf.org ; <<>> DiG 9.10.3-P4-Ubuntu <<>> +nodnssec @9.9.9.9 AAAA irtf.org ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11544 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;irtf.org. IN AAAA ;; ANSWER SECTION: irtf.org. 1325 IN AAAA 2001:1900:3001:11::2c ;; Query time: 4 msec ;; SERVER: 9.9.9.9#53(9.9.9.9) ;; WHEN: Thu Nov 16 09:49:41 +08 2017 ;; MSG SIZE rcvd: 65
On y voit que Quad9 valide avec DNSSEC (la réponse a bien le bit AD - Authentic Data).
Si le domaine est sur la liste noire de Quad9 (merci à Xavier Claude pour avoir trouvé un nom), le résolveur répond NXDOMAIN (No Such Domain, ce domaine n'existe pas) :
% dig @9.9.9.9 www.hjaoopoa.top ; <<>> DiG 9.10.3-P4-Debian <<>> @9.9.9.9 www.hjaoopoa.top ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 1143 ;; flags: qr rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;www.hjaoopoa.top. IN A ;; Query time: 17 msec ;; SERVER: 9.9.9.9#53(9.9.9.9) ;; WHEN: Sat Nov 18 20:30:41 CET 2017 ;; MSG SIZE rcvd: 45
(Avec un résolveur non-menteur, on aurait eu le code de retour
NOERROR et l'adresse IP 54.213.138.248
.)
Maintenant, testons la nouveauté importante de ce service, DNS sur TLS (RFC 7858). C'est du TLS donc on peut y aller avec openssl :
% openssl s_client -connect \[2620:fe::fe\]:853 -showcerts depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 verify return:1 depth=0 CN = dns.quad9.net verify return:1 --- Certificate chain 0 s:/CN=dns.quad9.net i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 -----BEGIN CERTIFICATE----- ... 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 i:/O=Digital Signature Trust Co./CN=DST Root CA X3 ... Server certificate subject=/CN=dns.quad9.net issuer=/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 --- Peer signing digest: SHA512 Server Temp Key: ECDH, P-256, 256 bits ... New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384 Server public key is 2048 bit Secure Renegotiation IS supported ... SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES256-GCM-SHA384 ...
On voit que Quad9 répond bien en TLS, et a un certificat Let's Encrypt.
Testons ensuite avec un client DNS, le
programme getdns_query
distribué avec getdns :
% getdns_query @9.9.9.9 -s -l L www.afnic.fr AAAA { "answer_type": GETDNS_NAMETYPE_DNS, "canonical_name": <bindata for lb01-1.nic.fr.>, "just_address_answers": [ { "address_data": <bindata for 2001:67c:2218:30::24>, "address_type": <bindata of "IPv6"> } ...
(Oui, getdns_query
est très bavard.) L'option
-l L
lui dit d'utiliser DNS sur TLS.
On va d'ailleurs utiliser tshark pour vérifier qu'on est bien en TLS :
% tshark -n -i wlp2s0 -d tcp.port==853,ssl host 9.9.9.9 Capturing on 'wlp2s0' 1 0.000000000 31.133.136.116 → 9.9.9.9 TCP 74 37874 → 853 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=233018174 TSecr=0 WS=128 2 0.002518390 9.9.9.9 → 31.133.136.116 TCP 74 853 → 37874 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1460 SACK_PERM=1 TSval=873811762 TSecr=233018174 WS=256 3 0.002551638 31.133.136.116 → 9.9.9.9 TCP 66 37874 → 853 [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=233018175 TSecr=873811762 4 0.002642065 31.133.136.116 → 9.9.9.9 SSL 371 Client Hello 5 0.022008585 9.9.9.9 → 31.133.136.116 TLSv1.2 1514 Server Hello 6 0.022042645 31.133.136.116 → 9.9.9.9 TCP 66 37874 → 853 [ACK] Seq=306 Ack=1449 Win=32128 Len=0 TSval=233018180 TSecr=873811781 7 0.022050371 9.9.9.9 → 31.133.136.116 TLSv1.2 108 [TCP Previous segment not captured] , Ignored Unknown Record 8 0.022054712 31.133.136.116 → 9.9.9.9 TCP 78 [TCP Window Update] 37874 → 853 [ACK] Seq=306 Ack=1449 Win=35072 Len=0 TSval=233018180 TSecr=873811781 SLE=2897 SRE=2939 9 0.022667110 9.9.9.9 → 31.133.136.116 TCP 1514 [TCP Out-Of-Order] 853 → 37874 [ACK] Seq=1449 Ack=306 Win=30208 Len=1448 TSval=873811781 TSecr=233018175 10 0.022679278 31.133.136.116 → 9.9.9.9 TCP 66 37874 → 853 [ACK] Seq=306 Ack=2939 Win=37888 Len=0 TSval=233018180 TSecr=873811781 11 0.023537602 31.133.136.116 → 9.9.9.9 TLSv1.2 192 Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message 12 0.037713598 9.9.9.9 → 31.133.136.116 TLSv1.2 117 Change Cipher Spec, Encrypted Handshake Message 13 0.037888417 31.133.136.116 → 9.9.9.9 TLSv1.2 225 Application Data 14 0.093441153 9.9.9.9 → 31.133.136.116 TCP 66 853 → 37874 [ACK] Seq=2990 Ack=591 Win=31232 Len=0 TSval=873811853 TSecr=233018184 15 0.742375719 9.9.9.9 → 31.133.136.116 TLSv1.2 178 Application Data ...
Le -d tcp.port==853,ssl
était là pour dire à
tshark d'interpréter ce qui passe sur le
port 853 (celui de DNS-sur-TLS) comme étant
du TLS. On voit bien le dialogue TLS mais
évidemment pas les questions et réponses DNS puisque tout est
chiffré.
Bien, maintenant que les tests se passent bien, comment utiliser Quad9 pour la vraie résolution de noms ? On va utiliser stubby pour parler à Quad9. Le fichier de configuration Stubby sera du genre:
listen_addresses: - 0::1@8053 # https://github.com/getdnsapi/getdns/issues/358 dns_transport_list: - GETDNS_TRANSPORT_TLS upstream_recursive_servers: # Quad9 - address_data: 9.9.9.9 tls_auth_name: "dns.quad9.net" - address_data: 2620:fe::fe tls_auth_name: "dns.quad9.net"
On indique à stubby d'écouter sur l'adresse locale
::1
, port 8053, et de faire suivre les
requêtes en DNS sur TLS à 9.9.9.9
ou
2620:fe::fe
. On lance stubby :
% stubby [12:28:10.942595] STUBBY: Read config from file /usr/local/etc/stubby/stubby.yml [12:28:10.942842] STUBBY: Starting DAEMON....
Et on peut le tester, en utilisant dig pour interroger à l'adresse et au port indiqué :
% dig @::1 -p 8053 A www.catstuff.com ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @::1 -p 8053 A www.catstuff.com ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20910 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 65535 ;; QUESTION SECTION: ;www.catstuff.com. IN A ;; ANSWER SECTION: www.catstuff.com. 600 IN A 216.157.88.24 ;; Query time: 974 msec ;; SERVER: ::1#8053(::1) ;; WHEN: Thu Nov 16 20:29:26 +08 2017 ;; MSG SIZE rcvd: 77
Et on peut vérifier avec tshark ou tcpdump que Stubby parle bien avec Quad9, et en utilisant TLS.
Si, à ce stade, vous obtenez une réponse DNS
FORMERR
(FORmat ERRor) au
lieu du NOERROR
qu'on voit ci-dessus, c'est à
cause de cette
bogue et il faut mettre à jour la bibliothèque getdns
utilisée par Stubby.
Stubby a l'avantage de bien gérer TCP, notamment en réutilisant les connexions (il serait très coûteux d'établir une connexion TCP pour chaque requête DNS, surtout avec TLS par dessus). Mais il n'a pas de cache des réponses, ce qui peut être ennuyeux si on est loin de Quad9. Pour cela, le plus simple est d'ajouter un vrai résolveur, ici Unbound. On le configure ainsi :
server: interface: 127.0.0.1 do-not-query-localhost: no forward-zone: name: "." forward-addr: ::1@8053
Avec cette configuration, Unbound va écouter sur l'adresse
127.0.0.1
(sur le port par défaut, 53, le
port du DNS) et relayer les requêtes pour lesquelles il n'a pas
déjà une réponse dans son cache vers Stubby
(::1
, port 8053). Interrogeons Unbound :
% dig @127.0.0.1 A mastodon.gougere.fr ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @127.0.0.1 A mastodon.gougere.fr ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40668 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;mastodon.gougere.fr. IN A ;; ANSWER SECTION: mastodon.gougere.fr. 600 IN A 185.167.17.10 ;; Query time: 2662 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Thu Nov 16 20:36:09 +08 2017 ;; MSG SIZE rcvd: 64
Unbound a une mémoire (le cache) donc si on recommance la requête aussitôt, la réponse arrivera bien plus vite et on verra le TTL (600 secondes ici) diminué.
Si vous trouvez que tout cela est bien compliqué à installer/configurer, vous pouvez vous servir d'une image Docker, faite par Juzam (voir aussi sur Github).
On note qu'il n'est pas évident de trouver une adresse IPv4
facilement mémorisable comme 9.9.9.9
. L'examen
de DNSDB montre que cette adresse a
beaucoup servi avant d'arriver chez PCH, et pour des activités… que
certains peuvent trouver contestables. Cette adresse, sérieusement
marquée, est donc noirlistée à plusieurs endroits. Si cela ne
marche pas de chez vous, essayez d'ailleurs, ou alors en IPv6. On
voit bien cette imparfaite connectivité en testant avec les sondes RIPE Atlas et le programme atlas-reach, comparant Quad9 et Google
Public DNS :
% atlas-resolve -r 200 -e 9.9.9.9 --nsid -t AAAA irtf.org Nameserver 9.9.9.9 [ERROR: SERVFAIL] : 1 occurrences [TIMEOUT] : 9 occurrences [2001:1900:3001:11::2c] : 177 occurrences Test #10205081 done at 2017-11-16T01:41:40Z % atlas-resolve -r 200 -e 8.8.8.8 -g 10205081 --nsid -t AAAA irtf.org Nameserver 8.8.8.8 [TIMEOUT] : 1 occurrences [2001:1900:3001:11::2c] : 186 occurrences Test #10205089 done at 2017-11-16T01:46:38Z
Et merci à Sara Dickinson pour son aide technique.
Si vous vous intéressez aux questions plus politiques, vous pouvez consulter l'article critique de sha_5_1_2 ainsi que la la réponse très détaillée du directeur de Quad9.
Date de publication du RFC : Novembre 2017
Auteur(s) du RFC : M. Tuexen (Muenster Univ. of Appl. Sciences), R. Stewart (Netflix), R. Jesup (WorldGate Communications), S. Loreto (Ericsson)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tsvwg
Première rédaction de cet article le 16 novembre 2017
Le protocole de transport SCTP est normalement prévu pour tourner directement sur IP. Pour diverses raisons, il peut être utile de le faire tourner sur un autre protocole de transport comme UDP (ce que fait le RFC 6951) ou même sur un protocole qui offre des services de sécurité comme DTLS (ce que fait notre nouveau RFC).
SCTP est défini dans le RFC 4960. C'est un concurrent de TCP, offrant un certain nombre de services que TCP ne fait pas. DTLS, normalisé dans le RFC 6347, est un protocole permettant d'utiliser les services de sécurité de TLS au-dessus d'UDP. Il est notamment très utilisé par WebRTC (RFC 8827), lui donnant ainsi une sécurité de bout en bout.
En théorie, SCTP peut fonctionner directement sur IP. Hélas, dans l'Internet actuel, très ossifié, plein d'obstacles s'y opposent. Par exemple, les routeurs NAT tel que la box de M. Michu à la maison, n'acceptent en général que TCP et UDP. Et bien des pare-feux bloquent stupidement les protocoles que leurs auteurs sont trop ignorants pour connaître. En pratique, donc, SCTP ne passe pas partout. L'encapsuler dans un autre protocole de transport, comme UDP (directement, ou bien via DTLS), est souvent la seule solution pour avoir une connectivité. L'intérêt de DTLS est qu'on a toute la sécurité de TLS, notamment la confidentialité via le chiffrement.
Cette encapsulation est simple (section 3) : on met le paquet SCTP, avec ses en-têtes et sa charge utile, dans les données du paquet DTLS.
Il y a toutefois quelques détails à prendre en compte (section 4 de notre RFC). Par exemple, comme toute encapsulation prend quelques octets, la MTU diminue. Il faut donc un système de PMTUD. Comme l'encapsulation rend difficile l'accès à ICMP (voir la section 6) et que les middleboxes pénibles dont je parlais plus haut bloquent souvent, à tort, ICMP, cette découverte de la MTU du chemin doit pouvoir se faire sans ICMP (la méthode des RFC 4821 et RFC 8899 est recommandée).
Cette histoire de MTU concerne tout protocole encapsulé. Mais il y a aussi d'autres problèmes, ceux liés aux spécificités de SCTP (section 6) :
Cette norme est surtout issue des besoins de WebRTC, dont les implémenteurs réclamaient un moyen facile de fournir sécurité et passage à travers le NAT. Elle est mise en œuvre depuis longtemps, dans des clients WebRTC comme Chrome, Firefox ou Opera.
Date de publication du RFC : Novembre 2017
Auteur(s) du RFC : C. Schmitt, B. Stiller (University of Zurich), B. Trammell (ETH Zurich)
Pour information
Première rédaction de cet article le 15 novembre 2017
Le format IPFIX, normalisé dans le RFC 7011, permet à un équipement réseau d'envoyer des données résumées à un collecteur, à des fins d'études ou de supervision. À l'origine, l'idée était que l'équipement réseau soit un routeur, une machine relativement grosse, munie de suffisamment de ressources pour pouvoir s'accomoder d'un protocole un peu compliqué, et qui nécessite l'envoi de pas mal d'octets. L'IPFIX original était donc peu adapté aux engins contraints, par exemple aux capteurs connectés. D'où cette variante d'IPFIX, TinyIPFIX, qui vise à être utilisable par des objets connectés comme ceux utilisant le 6LoWPAN du RFC 4944 (un compteur Linky ?)
Mais prenons plutôt comme exemple un capteur non connecté au réseau électrique (donc dépendant d'une batterie, qu'on ne peut pas recharger tout le temps, par exemple parce que le capteur est dans un lieu difficile d'accès) et n'ayant comme connexion au réseau que le WiFi. L'émission radio coûte cher en terme d'énergie et notre capteur va donc souvent éteindre sa liaison WiFi, sauf quand il a explicitement quelque chose à transmettre. Un protocole de type pull ne peut donc pas convenir, il faut du push, que le capteur envoie ses données quand il le décide. Ces contraintes sont détaillées dans « Applications for Wireless Sensor Networks », par l'auteure du RFC (pas trouvé en ligne, c'est publié dans le livre « Handbook of Research on P2P and Grid Systems for Service-Oriented Computing: Models, Methodologies and Applications », édité par Antonopoulos N.; Exarchakos G.; Li M.; Liotta A. chez Information Science Publishing).
Le RFC donne (section 3) l'exemple de l'IRIS de Crossbow : sa taille n'est que de 58 x 32 x 7 mm, et il a 128 ko de flash pour les programmes (512 ko pour les données mesurées), 8 ko de RAM et 4 d'EEPROM pour sa configuration. On ne peut pas demander des miracles à un objet aussi contraint. (Si c'est vraiment trop peu, le RFC cite aussi l'engin d'Advantic avec ses 48 ko de flash « programme », 1024 ko de flash « données », 10 ko de RAM et 16 d'EEPROM.) Question énergie, ce n'est pas mieux, deux piles AA de 2 800 mAh chacune peuvent donner en tout 30 240 J.
Autre contrainte vécue par ces pauvres objets connectés, les limites du protocole réseau (section 3.3 de notre RFC). 6LoWPAN (RFC 4944) utilise IEEE 802.15.4. Ce protocole ne porte que 102 octets par trame. Ce n'est pas assez pour IPv6, qui veut une MTU minimum de 1 280 octets. Il faut donc utiliser la fragmentation, un mécanisme problématique, notamment parce que, si un seul fragment est perdu (et ces pertes sont des réalités, sur les liaisons radio), il faut retransmettre tout le paquet. Il est donc prudent de s'en tenir à des paquets assez petits pour tenir dans une trame. C'est un des apports de TinyIPFIX par rapport au IPFIX classique : des messages plus petits.
Enfin, dernière contrainte, le protocole de transport. IPFIX impose (RFC 7011, section 10.1) que SCTP soit disponible, même s'il permet aussi UDP et TCP. Mais SCTP (et TCP) ne permettent pas d'utiliser les mécanismes de compression des en-têtes de 6LoWPAN. Et SCTP n'est pas toujours présent dans les systèmes d'exploitation des objets, par exemple TinyOS. TinyIPFIX utilise donc UDP. À noter que, comme le demande la section 6.2 du du RFC 5153, TinyIPFIX sur UDP n'est pas prévu pour l'Internet ouvert, mais uniquement pour des réseaux fermés.
TinyIPFIX est dérivé de IPFIX (RFC 7011) et en hérite donc de la plupart des concepts, comme la séparation des données (Data Sets) et de la description des données (dans des gabarits, transmis en Template Sets).
La section 4 du RFC décrit des scénarios d'usage. Comme TinyIPFIX (comme IPFIX) est undirectionnel (de l'exporteur vers le collecteur), et qu'il tourne sur UDP (où les messages peuvent se perdre), le développeur doit être conscient des limites de ce service. Si on perd un paquet de données, on perd des données. Pire, si on perd un paquet de gabarit (RFC 7011, sections 3.4.1 et 8), on ne pourra plus décoder les paquets de données suivants. On ne doit donc pas utiliser TinyIPFIX pour des systèmes où la perte de données serait critique. Un système d'accusés de réception et de retransmission (refaire une partie de TCP, quoi…) serait trop lourd pour ces engins contraints (il faudrait stocker les messages en attendant l'accusé de réception).
Le RFC recommande de renvoyer les paquets de gabarit de temps en temps. C'est normalement inutile (on n'imagine pas un capteur contraint en ressources changer de gabarit), mais cela permet de compenser le risque de perte. Le collecteur qui, lui, n'a pas de contraintes, a tout intérêt à enregistrer tous les messages, même quand il n'y a pas de gabarit, de manière à pouvoir les décoder quand le gabarit arrivera. (Normalement, on ne fait pas ça avec IPFIX, le gabarit ne peut s'appliquer qu'aux messages reçus après, mais, avec TinyIPFIX, il y a peu de chances que les gabarits changent.)
Le RFC donne un exemple animalier qui conviendrait au déploiement de TinyIPFIX, afin de surveiller des oiseaux (Szewczyk, R., Mainwaring, A., Polastre, J., et D. Culler, « An analysis of a large scale habitat monitoring application ».) Les capteurs notent ce que font ces charmants animaux et le transmettent.
Cet exemple sert à illustrer un cas où TinyIPFIX serait bien adapté : collecte de type push, efficacité en terme de nombre de paquets, perte de paquets non critique, pas de nécessité d'un estampillage temporel des messages (qui existe dans IPFIX mais que TinyIPFIX supprime pour alléger le travail).
La section 5 décrit l'architecture de TinyIPFIX, très similaire à celle d'IPFIX (RFC 5470).
Enfin, la section 6 décrit les aspects concrets de TinyIPFIX, notamment le format des messages. Il ressemble beaucoup à celui d'IPFIX, avec quelques optimisations pour réduire la taille des messages. Ainsi, l'en-tête de message IPFIX fait toujours 16 octets, alors que dans TinyIPFIX, il est de taille variable, avec seulement 3 octets dans le meilleur des cas. C'est ainsi que des champs comme le numéro de version (qui valait 11 pour IPFIX) ont été retirés. De même, l'estampille temporelle (« Export Time » dans IPFIX) est partie (de toute façon, les objets contraints ont rarement une horloge correcte).
Les objets contraints déployés sur le terrain n'ont souvent pas à un accès direct à Internet, à la fois pour des raisons de sécurité, et parce qu'un TCP/IP complet serait trop lourd pour eux. Il est donc fréquent qu'ils doivent passer par des relais qui ont, eux, un vrai TCP/IP, voire un accès Internet. (Cette particularité des déploiements d'objets connectés est une des raisons pour lesquelles le terme d'« Internet des Objets » n'a pas de valeur autre que marketing.)
TinyIPFIX va donc fonctionner dans ce type d'environnement et la section 7 de notre RFC décrit donc le mécanisme d'« intermédiation ». L'intermédiaire peut, par exemple, transformer du TinyIPFIX/UDP en TinyIPFIX/SCTP ou, carrément, du TinyIPFIX en IPFIX. Dans ce dernier cas, il devra ajouter les informations manquantes, comme l'estampille temporelle ou bien le numéro de version.
Côté mise en œuvre, Tiny IPFIX a été mis dans
TinyOS et
Contiki. Voir http://www.net.in.tum.de/pub/cs/TinyIPFIX_GUI_Licenced.zip
et http://www.net.in.tum.de/pub/cs/TinyIPFIX_and_Extentions_Licenced.zip
.
Auteur(s) du livre : Aude Seigne
Éditeur : Zoe
978-2-88927-458-1
Publié en 2017
Première rédaction de cet article le 5 novembre 2017
La cybercatastrophe (plus d'Internet, tout cassé, plus de vidéos de chat) est un thème fréquent dans les médias mais un peu moins dans les romans. Aude Seigne s'y est attelée dans son dernier roman, « Une toile large comme le monde ».
Le roman suit plusieurs personnages plutôt bien intégrés dans la société, plutôt mondialisés, n'ayant pas des boulots idiots (il n'y a jamais de caissière de supermarché, encore moins de vendeur des rues, dans les romans). Mais, pour des raisons diverses, ils décident d'arrêter Internet. Je ne vous en dis pas plus sur l'intrigue.
Disons-le tout de suite, je n'ai pas aimé le roman : l'auteure saute allègrement des pans entiers de son histoire, on ne comprend pas vraiment les motivations des personnages (qui passent pourtant tou·te·s beaucoup de temps à se regarder le nombril en en parlant), et les détails de la constitution et de la coordination de leur groupe sont à peine esquissés. Ça ne peut pourtant pas être aussi simple de monter un complot mondial !
Mais la partie de description de l'infrastructure d'Internet est bien faite, détaillant des aspects de son fonctionnement qui sont peu traités, que ce soit dans les cours d'informatique ou dans les romans. Elle a raison de pointer du doigt que tout le monde aujourd'hui dépend d'Internet, sans avoir la moindre idée de son fonctionnement (et je ne parle pas de la technique, juste des acteurs impliqués). J'ai bien aimé la citation « Ce n’est pas parce qu’on est YouTuber, joueur, programmeur, hacker ou community manager qu’on connaît l’existence des câbles », qui est quasiment celle par laquelle je commence mon cours au CELSA. Je vais réclamer des droits d'auteur 😃 Mais je préfère encore plus une autre phrase de l'auteure, « Internet n'est pas un esprit, il a besoin d'un corps » (et ce corps n'est pas uniquement composé de câbles et de machines).
Outre les câbles, largement décrits dans le roman (ainsi que les centres de données, à l'occasion d'une visite plus pédagogique que littéraire), l'auteure parle aussi des gens qui sont derrière le réseau, ce qui est utile, même si elle verse parfois dans le sensationnalisme (« Ils [les ingénieurs réseaux] sont seulement quelques centaines, à l'échelle mondiale, et ils se réunissent deux à trois fois par an [probablement une allusion aux réunions NANOG]. »).
Techniquement, n'attendez pas de détails sur les techniques utilisées pour arrêter l'Internet. Le livre est globalement bien documenté mais comprend plusieurs exagérations. C'est le cas, par exemple, de l'apparition dans une liste de pannes partielles de l'Internet, de « 2014 ― Panne importante à l'échelle mondiale, suscitée par le dépassement de la limite des 512 000 routes, que d'anciens modèles de routeurs ne peuvent prendre en charge » (alors qu'il ne s'était en fait pas passé grand'chose). Si vous voulez approfondir cette question de la résilience d'Internet, vous pouvez aussi lire mon article au SSTIC.
Il y a eu un article d'Usbek & Rica sur ce livre. Comme souvent dans les médias, l'article ne tient pas les promesses du titre. Le titre parle de couper Internet, et ensuite c'est juste « en 2016, une cyberattaque [celle contre Dyn] prive des millions d’Américains d’accès aux plus grands sites pendant une dizaine d’heures… ». C'est une erreur courante des analyses de la robustesse de l'Internet que de confondre les pannes locales et limitées dans le temps (relativement fréquentes et difficiles à empêcher) avec une vraie cybercastrophe stoppant presque tout l'Internet pendant des semaines.
On trouve aussi dans cet article des grosses bêtises du genre « avoir une boîte mail remplie à ras-bord et pleine de spams est désormais aussi condamnable que de ne pas trier ses déchets ».
Voici la page officielle du livre. Mais, comme vous vous en doutez, je préfère recommander, pour le lecteur intéressé par le « corps d'Internet », le livre Tubes d'Andrew Blum.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : N. ten Oever (Article 19), C. Cath (Oxford Internet Institute)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF hrpc
Première rédaction de cet article le 2 novembre 2017
Ce RFC très politique est le premier du groupe de recherche IRTF HRPC, dont le nom veut dire « Human Rights and Protocol Considerations ». À première vue, il n'y a pas de rapport entre les droits humains et les protocoles réseau. Les premiers relèvent de la politique, les seconds de la pure technique, non ? Mais, justement, le groupe HRPC a été créé sur la base de l'idée qu'il y a de la politique dans le travail de l'IETF, que les protocoles ne sont pas complètement neutres, et qu'il était nécessaire de creuser cette relation complexe entre protocoles et droits humains. Le premier RFC analyse le problème de base : « TCP/IP est-il politique ? »
Si vous êtes un concepteur de protocoles, plutôt porté sur le concret, et que les discussions politiques vous gonflent, vous pouvez passer directement à la section 6 du RFC, qui est la check-list Droits Humains pour votre prochain protocole ou format. En la suivant, vous pourrez plus facilement vérifier que votre création n'a pas trop d'effets néfastes, question droits humains. (Depuis, il vaut mieux consulter le RFC 9620, qui la remplace.)
Ce RFC n'est pas le premier RFC « politique », et il ne faudrait pas croire que les ingénieur·e·s qui participent à l'IETF sont tou·ṫe·s des nerds asociaux avec la conscience politique d'un poisson rouge. Parmi les RFC politiques, on peut citer le RFC 1984 (refus d'affaiblir la cryptographie), le RFC 7258 (post-Snowden, RFC affirmant que la surveillance de masse est une attaque contre l'Internet, et qu'il faut déployer des mesures techniques la rendant plus difficile), et bien sûr l'excellent RFC 6973, sur la vie privée, qui sert largement de modèle à notre RFC 8280.
Le groupe de recherche IRTF HRPC va donc travailler sur deux axes (section 3 de notre RFC) :
Ce RFC particulier a eu une gestation de plus de deux ans. Deux des étapes importantes avaient été la réunion IETF 92 à Dallas, et la réunion IETF 95 à Buenos Aires. À la seconde, vu les opinions politiques de beaucoup des participant·e·s, l'après-réunion s'est tenu dans un restaurant végétarien. En Argentine…
La section 1 du RFC rappelle les nombreux débats qui ont agité le monde de l'Internet sur le rôle politique de ce réseau. Deux belles citations ouvrent cette section, une de Tim Berners-Lee qui dit « There's a freedom about the Internet: As long as we accept the rules of sending packets around, we can send packets containing anything to anywhere. » et un extrait du RFC 3935 « The Internet isn't value-neutral, and neither is the IETF. ». Et le RFC 3935 continue : « We want the Internet to be useful for communities that share our commitment to openness and fairness. We embrace technical concepts such as decentralized control, edge-user empowerment and sharing of resources, because those concepts resonate with the core values of the IETF community. These concepts have little to do with the technology that's possible, and much to do with the technology that we choose to create. ». Le succès immense de l'Internet, contre tous les prophètes de malheur qui prétendaient que ce réseau, qui n'avait pas été conçu par des Messieurs Sérieux, ne pourrait jamais marcher, fait que l'impact social et politique des techniques de la famille TCP/IP est énorme. On trouve donc logiquement de nombreux textes qui affirment que « ce grand pouvoir donne à l'Internet de grandes responsabilités », par exemple cette résolution des Nations Unies, ou bien la déclaration de NETmundial. Une position plus radicale est qu'il faut défendre et renforcer l'Internet, car il serait intrinsèquement un outil aux services des droits humains.
En effet, la connectivité de bout en bout, tout le monde peut parler à tous ceux qui veulent bien, Alice et Bob peuvent échanger sans autorisation, est à la fois un principe fondamental de l'Internet (cf. RFC 1958) et un puissant soutien aux droits humains. Pour citer Benjamin Bayart, « L’imprimerie a permis au peuple de lire, Internet va lui permettre d’écrire. » L'architecture de l'Internet est ouverte (je me souviens de techniciens d'un opérateur de télécommunications historique qui avaient poussé des cris d'horreur quand je leur avais montré traceroute, au début des années 1990. Ils avaient tout de suite demandé comment empêcher le client de regarder l'intérieur du réseau de l'opérateur.) Les normes techniques de l'Internet sont développées selon un processus ouvert, et sont librement distribuées (ce que ne font toujours pas les dinosaures de la normalisation comme l'AFNOR ou l'ISO). En prime, une bonne partie de l'infrastructure de l'Internet repose sur du logiciel libre.
L'Internet a prouvé qu'il pouvait continuer à fonctionner en environnement hostile (RFC 1984 et RFC 3365). Les RFC politiques cités plus haut défendent tous des valeurs qui vont dans le sens des droits humains (par exemple la vie privée, dans les RFC 6973 et RFC 7258). Cela ne va pas de soi : une organisation comme l'UIT n'en a jamais fait autant et développe au contraire des technologies hostiles aux droits humains comme les techniques de surveillance dans le NGN.
On pourrait peut-être même dire que non seulement l'Internet défend les droits humains, mais que ceux-ci sont à la base de l'architecture de l'Internet. (Cf. Cath, C. and L. Floridi, « The Design of the Internet's Architecture by the Internet Engineering Task Force (IETF) and Human Rights », 2017.) On peut citer ici Bless, R. et C. Orwat, « Values and Networks » : « to a certain extent, the Internet and its protocols have already facilitated the realization of human rights, e.g., the freedom of assembly and expression. In contrast, measures of censorship and pervasive surveillance violate fundamental human rights. » ou bien Denardis, L., « The Internet Design Tension between Surveillance and Security » « Since the first hints of Internet commercialization and internationalization, the IETF has supported strong security in protocol design and has sometimes served as a force resisting protocol-enabled surveillance features. ».
Mais la question reste chaudement débattue à l'IETF. Nombreux sont les techniciens qui grommelent « tout ça, c'est de la politique, cela ne nous concerne pas », voire reprennent l'argument classique de la neutralité de la technique « un outil est neutre, c'est l'usage qu'on en fait qui compte, le fabricant du couteau n'est pas responsable d'un meurtre qu'on commet avec ce couteau, donc on ne doit pas se poser la question des droits humains ». Avant Snowden, c'était sans doute l'opinion dominante à l'IETF, mais cela a changé depuis.
Mais, au fait, ce sont quoi, les Droits Humains avec leur majuscule ? Ce sont des droits universels, indivisibles et inaliénables, formalisés dans des textes comme la Déclaration Universelle des Droits de l'Homme ou comme le pacte international relatif aux droits civils et politiques ou le Pacte international relatif aux droits économiques, sociaux et culturels. La section 2 du RFC, sur la terminologie, discute en détail cette définition. Si vous voulez un document unique sur les droits humains, la DUDH citée plus haut est une lecture recommandée. Le fait qu'ils soient universels est important : on entend régulièrement des dirigeants ou des lécheurs de bottes des dirigeants prétendre que les droits humains ne sont pas bons pour le peuple qu'ils oppriment, qu'ils seraient uniquement pour certaines catégories de l'humanité. Si c'était le cas, il serait en effet inutile de discuter des droits humains sur l'Internet, puisque celui-ci connecte tout le monde. Mais, bien sûr, cette soi-disant relativité des droits humains est de la pure rhétorique malhonnête au service de dictateurs locaux.
On notera que, à l'époque de la rédaction de la DUDH, le seul risque de violation envisagée était l'œuvre des États, mais que l'idée s'est imposée depuis que des acteurs non-étatiques pouvaient également être concernés.
Cela ne veut pas dire que les textes comme la DUDH citée plus haut sont parfaits. Ce ne sont pas des textes sacrés, mais le résultat d'un processus politique. Comme toute œuvre humaine, ils peuvent être améliorés, mais il faut juste garder en tête que ceux qui les critiquent ne cherchent pas en général à les améliorer, mais à les affaiblir, voire à les détruire.
Par contre, les droits humains ne sont pas absolus. Un exemple de ce caractère non-absolu des droits humains est qu'ils peuvent être en conflit entre eux. Par exemple, le droit à la liberté d'expression peut rentrer en contradiction avec le droit de ne pas être insulté ou harcelé. Ou avec le droit à la vie privée. Les droits humains ne pourront donc pas être mis en algorithmes.
La section 2 de notre RFC est consacrée à la terminologie. Sujet très difficile car elle est souvent floue dans les domaines liés à la sécurité. Je ne vais pas la reproduire en entier ici (la section est longue, en partie en raison du caractère transversal de notre RFC, cf. section 5.2.1.3), juste noter quelques définitions qui ont fait des histoires (listées dans l'ordre alphabétique de l'original en anglais). Notez que notre RFC 8280 ne fait souvent que reprendre des définitions de RFC précédents. Ainsi, la définition de « connectivité Internet complète » vient du RFC 4084 (et est nécessaire car bien des malhonnêtes vendent comme « accès Internet » des offres plus ou moins bridées). De même le RFC 4949, sur le vocabulaire de la sécurité, et le RFC 6973, sur la vie privée, sont très mis à contribution.
En parlant de vie privée, la définition d'« anonymat » est un des premiers problèmes de terminologie. Le terme est utilisé à tort et à travers dans les médias (« Bitcoin est une monnaie anonyme ») et souvent confondu avec pseudonymat. À leur décharge, il faut dire que les définitions du RFC 4949 et du RFC 6973 sont très abstraites.
Parmi les autres définitions plutôt vagues, notons celle de « neutralité par rapport au contenu » (content agnosticism). C'est bien sûr un concept très important, d'autant plus que cette neutralité est menacée, mais la définition ne va pas très loin. Ce n'est pas mieux pour un autre concept important mais flou, et difficile à saisir, celui de décentralisation, un peu utilisé à toutes les sauces aujourd'hui (cf. mon article pour JRES).
Passons maintenant au principe de bout en bout. C'est un des concepts clés de l'Internet (RFC 2775) : l'« intelligence » (les traitements compliqués) doit être aux extrémités, pas dans le réseau. Plusieurs raisons militent en faveur de ce principe mais, pour en rester aux droits humains, notons surtout que ce principe se traduit par « touche pas à mes données ».
Autre sujet difficile à définir, les « normes ouvertes » (open standards). Il y a plein de SDO, dont le degré d'ouverture varie considérablement. Par exemple, l'ISO ou l'IEEE ne publient pas leurs normes en ligne et, même si on les acquiert, on n'a pas le droit de les redistribuer. L'UIT ne permet de participer que si vous êtes gouvernement ou grande entreprise. L'IETF, sans doute la SDO la plus ouverte, n'a pas de définition claire de ce qu'est une norme ouverte (cf. RFC 2026), à part dans le RFC 6852, qui est surtout un document politicien (et hypocrite).
Un concept important de l'Internet est celui d'« innovation sans autorisation ». Pour le comprendre, regardons l'invention du World-Wide Web. Tim Berners-Lee, Robert Cailliau et les autres ont pu inventer le Web et le déployer, sans rien demander à personne. Aucun comité Machin, aucun gouvernement, n'a été sollicité pour donner son avis ou son autorisation. Au contraire, dans les réseaux de télécommunication pré-Internet, il fallait l'accord préalable de l'opérateur pour tout déploiement d'une application. Sans l'« innovation sans autorisation », nous n'aurions pas le Web.
Et la « vie privée », on la définit comment ? Le RFC 4949 la définit comme le droit à contrôler ce qu'on expose à l'extérieur. C'est actuellement un des droits humains les plus menacés sur l'Internet, en raison des possibilités de surveillance massive que permet le numérique, possibilités largement utilisées par les États. Or, ce droit est lui-même à la base de nombreux autres droits. Ainsi, la liberté d'expression est sérieusement en danger si on n'a pas de droit à la vie privée, par exemple parce que des gens hésiteront à lire certains textes s'ils savent que leurs habitudes de lecture sont surveillées.
La section 4 du RFC est consacrée à un examen du débat (très ancien) sur la neutralité de la technique, et sur les relations entre technique et politique. La littérature scientifique et philosophique dans ce domaine est riche ! (À une réunion de HRPC, la discussion avait tourné à la pure philosophie, et on y avait abondemment cité Foucault, Heidegger, Wittgenstein, Derrida et Kant, ce qui est plutôt rare à l'IETF.)
Les deux opinions extrêmes à ce sujet sont :
Il n'est pas compliqué de trouver plein d'exemples de luttes politiques autour des protocoles Internet, dans les RFC cités plus haut comme le RFC 7258, ou bien dans des articles comme celui de Denardis « The Internet Design Tension between Surveillance and Security ». Les participants à l'IETF ne vivent pas dans une bulle, ils vivent dans un contexte politique, social, historique, culturel, et cela affecte certainement leurs décisions.
Notre RFC cite un grand nombre de publications sur ces sujets, de Francesca Musiani « Giants, Dwarfs and Decentralized Alternatives to Internet-based Services » à Lawrence Lessig, Jonathan Zittrain (« The future of the Internet ») et Milton Mueller. Si vous avez quelques mois de libres devant vous, je vous encourage à lire tous ces livres et articles.
Il y a aussi des études plus spécifiques au rôle des SDO, parmi lesquelles « Engineering 'Privacy by Design' in the Internet Protocols - Understanding Online Privacy both as a Technical and a Human Rights Issue in the Face of Pervasive Monitoring » ou le célèbre article de Clark et ses collègues, « Tussle in Cyberspace ».
Le RFC dégage cinq opinions différentes sur les relations entre le travail des ingénieurs et les droits humains, et sur la question de savoir si les droits humains doivent être intégrés dans les protocoles Internet. La première est celle citée dans l'article de Clark et al., qu'on peut résumer par « ce serait dangereux d'essayer de faire respecter les droits humains par les protocoles » :
L'article résume en disant que les ingénieurs doivent concevoir le terrain, pas le résultat du match.
Une deuxième position est que certaines valeurs universelles, dont les droits humains tels que formalisés dans la DUDH, devraient être incluses dans l'architecture même du réseau. (Cf. l'article « Should Specific Values Be Embedded In The Internet Architecture? », et attention, c'est un article collectif, avec plusieurs points de vue. Celui résumé ici est celui de Brown.) L'idéal serait que le réseau lui-même protège ces droits. En effet, les techniciens, de part le pouvoir qu'ils ont, ont une obligation « morale » de faire tout ce qui est possible pour faire respecter les droits humains.
Une troisième position, qui part sur un plan différent, est d'estimer qu'on ne peut pas inclure le respect des droits humains dans les protocoles, mais que c'est bien dommage et, qu'à défaut, il faudrait déclarer clairement que le réseau est un bien commun, et que toute tentative de l'utiliser pour le mal est en soi une violation des droits humains. Si on suit ces auteurs (« The public core of the Internet. An international agenda for Internet governance »), l'Internet lui-même, et les protocoles tels que normalisés dans les RFC, seraient un bien commun qu'on ne peut pas tripoter, comme un parc naturel, par exemple. Si le DNS était inclus comme « bien commun », des manipulations comme les résolveurs menteurs deviendraient illégales ou en tout cas immorales.
Les auteurs de « Values and Networks » sont plus prudents. Ils estiment que les protocoles Internet ont effectivement des conséquences sur les droits humains, mais qu'on n'est pas sûrs de quelles conséquences exactement, et qu'il est important de poursuivre les recherches. Cette quatrième position va donc tout à fait dans le sens de la constitution de HRPC comme groupe de recherche de l'IRTF.
Enfin, cinquième possibilité (et vous avez vu qu'elles ne sont pas forcément incompatibles), Berners-Lee et Halpin disent que l'Internet crée également de nouveaux droits. Ainsi, dans une société connectée où ne pas avoir l'Internet est un handicap social, le droit à un accès Internet devient un droit humain.
Quel bilan tirer de cette littérature scientifique et philosophique existante ? D'abord, d'un point de vue pratique, on ne sait pas si créer un réseau qui, par construction, assurerait le respect des droits humains est faisable (avant même de savoir si c'est souhaitable). Mais, au moins, on peut arrêter de croire que la technique est complètement neutre, étudier les conséquences des protocoles sur les droits humains (ce que fait la section 5 de notre RFC) et essayer d'améliorer ces protocoles à la lumière de cette analyse (la section 6 du RFC).
Voyons donc une série d'étude de cas de protocoles Internet existants, et en quoi ils affectent les droits humains (section 5). Une anecdote personnelle au passage : les premières versions de ces études de cas comportaient d'énormes erreurs techniques. Il est en effet difficile de trouver des gens qui sont à la fois sensibilisés aux droits humains et compétents techniquement. Comme le note le RFC, un travail interdisciplinaire est nécessaire. Le travail collectif à l'IRTF fait que cette section 5 est maintenant correcte.
Avant les études de cas techniques, le point de départ est une analyse des discours (selon la méthodologie présentée dans l'article de Cath). Elle s'est faite à la fois informellement (discussion avec des auteurs de RFC, interviews de participants à l'IETF) et formellement, par le biais d'un outil d'analyse automatique. Ce dernier, écrit en Python avec Big Bang, a permis de déterminer les « éléments de langage » importants dans les normes Internet. Et cela donne de jolis graphes.
La partie informelle s'est surtout faite pendant la réunion IETF 92 à Dallas, et a donné le film « Net of Rights ». Mais il y a eu aussi l'observation des groupes de travail IETF en action.
Les protocoles Internet sont bâtis en utilisant des concepts techniques (connectivité, confidentialité, accessibilité, etc) et la section 5.2.2 met en correspondance ces concepts avec les droits humains tels que définis dans la DUDH. Par exemple, le droit de s'assembler s'appuie sur la connectivité, mais aussi sur la résistance à la censure, et sur la sécurité en général.
Maintenant, place à la première partie technique de notre RFC, la section 5.2.3. Elle étudie en détail les conséquences de divers protocoles pour les droits humains. Attention, la conception d'un protocole est une activité complexe, avec des cahiers de charges épais où le respect des droits humains (quand il est présent…) n'est qu'une partie. Et le travail d'ingéniérie nécessite toujours des compromis. Le RFC prévient donc que ce travail est forcément étroit : on n'examine les protocoles que sous l'angle des droits humains, alors qu'une évaluation complète de ces protocoles nécessiterait la prise en compte de bien d'autres aspects. Comme exemple de compromis auquel il faut parfois se résoudre, avoir un serveur distinct de la machine de l'utilisat·eur·rice, possiblement géré par un tiers (c'est le cas de SMTP et XMPP), est certainement mauvais pour la résistance à la censure, car il va fournir un point de contrôle évident, sur lequel des autorités peuvent taper. D'un autre côté, sans un tel serveur, comment communiquerait-on avec des utilisat·eur·rice·s qui ne sont pas connecté·e·s en permanence ou qui sont coincé·e·s derrière un réseau qui interdit les connexions entrantes ? Bref, les protocoles qui sont souvent vertement critiqués par la suite ne sont pas forcément mauvais, encore moins délibérement mauvais. L'idée de cette section est de bien illustrer, sur des cas concrets, que les décisions techniques ont des conséquences politiques. (Ce point avait fait l'objet de vives discussions à l'IETF, des gens estimant que le RFC était trop négatif, et qu'il aurait également fallu indiquer les aspects positifs de l'Internet.)
Donc, pour commencer la série, évidemment, IP lui-même, plus précisement IPv4 (RFC 791). Malgré la normalisation d'IPv6, IPv4 est toujours le principal protocole du réseau. C'est un succès fou, connectant des centaines de millions de machines (et bien plus via les systèmes de traduction d'adresses). Il est conçu pour en faire le moins possible : l'intelligence doit être dans les machines terminales, pas dans le réseau, pas dans la couche 3. (Cf. RFC 3724.) En pratique, toutefois, on voit des intermédiaires agir au niveau IP et, par exemple, ralentir certains types de trafic, ou bien bloquer certaines machines. IP expose en effet certaines informations qui peuvent faciliter ce genre de violations de la neutralité du réseau.
Par exemple, les adresses IP source et destination sont visibles en clair (même si tout le reste du paquet est chiffré) et à un endroit fixe du paquet, ce qui facilite la tâche des routeurs mais aussi des dispositifs de blocage. Avant que vous ne me dites « ben, évidemment, sinon le réseau ne pourrait pas marcher », faites attention. L'adresse IP de destination est effectivement nécessaire aux routeurs pour prendre des décisions de transmission, mais ce n'est pas le cas de l'adresse source. En outre, IP expose le protocole de transport utilisé, encore une information dont les routeurs n'ont pas besoin, mais qui peut aider des intermédiaires à traiter certains types de trafic différemment.
Aujourd'hui, beaucoup de décisions de blocage sont prises sur la base des adresses IP ainsi exposées, ce qui illustre les conséquences d'une décision apparemment purement technique. (Pour les amateurs d'histoire alternative, X.25 n'exposait pas obligatoirement les adresses source et destination dans chaque paquet. Même le serveur final ne les voyait pas forcément. X.25 avait plein de défauts, mais cette anecdote montre que d'autres choix étaient possibles. Il faut juste se rappeler qu'ils avaient leurs propres inconvénients.) Si vous êtes enseignant·e en réseaux informatiques, voici un exercice intéressant faire faire à vos étudiant·e·s : « concevoir un réseau qui n'expose pas à tous des identificateurs uniques mondiaux ». Des alternatives au mécanisme d'IP ont été conçues (comme Hornet ou APIP) mais aucune n'a connu de déploiement significatif. Le routage par la source (combiné avec de la triche sur les adresses IP) aurait également permis de limiter l'exposition des adresses IP sur le trajet mais il pose bien d'autres problèmes. La principale solution employée aujourd'hui, lorsqu'on veut dissimuler les adresses IP des machines qui communiquent, est Tor.
Une autre particularité d'IPv4, qui n'était pas présente à ses débuts, est l'utilisation massive de la traduction d'adresses (RFC 3022). Elle est très répandue. Mais elle casse le modèle de bout en bout, et met le routeur qui fait la traduction dans une position privilégiée (par exemple, beaucoup refusent de faire passer d'autres protocoles de transport que TCP ou UDP). C'est donc une sérieuse limite à la connectivité et donc aux droits humains qui en dépendent.
Et le DNS ? Voilà un protocole dont la relation aux droits humains a été largement discutée. Comme toute opération sur l'Internet commence par une requête DNS, il est un point de contrôle évident. On peut notamment l'utiliser pour la censure. Autre question politique liée au DNS et qui a fait s'agiter beaucoup d'électrons, le pouvoir des organismes qui gèrent les TLD et, bien sûr, la racine du DNS. On peut dire sans exagérer que l'essentiel des débats sur la « gouvernance de l'Internet » ont tourné sur la gestion de la racine du DNS, qui ne représente pourtant pas, et de loin, le seul enjeu politique.
Pourquoi est-ce un enjeu pour les droits humains ? Le DNS a une
structure arborescente, avec
l'ICANN à la racine. Le contrôle de l'ICANN
fait donc saliver bien du monde. Ensuite, les
TLD, qu'ils soient contrôlés par l'ICANN
(les gTLD) ou pas, ont un rôle
politique important, via leur politique d'enregistrement. Celle-ci
varie selon les TLD. Les gTLD historiques
comme .com
ont une politique déterminée par
des organisations états-uniennes, l'ICANN et leur registre
(Verisign dans le cas de
.com
). Les nouveaux
gTLD ont des registres de nationalité différentes mais dépendent
tous des règles ICANN (cf. l'excellente étude de
l'EFF comparant ces politiques dans l'optique des droits
humains). Les ccTLD, eux, dépendent
de lois nationales très variables. Elles sont par exemple plus ou
moins protectrices de la liberté d'expression. (Voir le fameux
cas
lybien.)
Est-ce que les centaines de nouveaux
gTLD créés depuis quelques années ont amélioré les choses
ou pas, pour cette liberté d'expression ? Certains disent que non
car beaucoup de ces nouveaux TLD ont une politique
d'enregistrement restrictive (cf. le rapport de l'EFF cité plus
haut), d'autres disent que oui car ces nouveaux TLD ont élargi le
choix. Et que la liberté d'association peut ne pas bien s'entendre
avec la liberté d'expression (la première peut justifier des
règles restrictives, pour que des minorités discriminées puissent
se rassembler sans être harcelées). Une chose est sûre, les débats
ont été chauds, par exemple autour d'une éventuelle création du
.gay
(un rapport
du Conseil de l'Europe détaille cette question « TLD et
droits humains »).
Le DNS soulève plein d'autres questions liées aux droits humains. Par exemple, il est indiscret (RFC 7626), et des solutions partielles comme le RFC 9156 semblent très peu déployées.
Et, comme trop peu de zones DNS sont protégées par DNSSEC (et, de toute façon, DNSSEC ne protège pas contre toutes les manipulations), il est trop facile de modifier les réponses envoyées. C'est aujourd'hui une des techniques de censure les plus déployées, notamment en Europe (voir à ce sujet le très bon rapport du Conseil Scientifique de l'AFNIC). Parmi les moyens possibles pour censurer via les noms de domaine :
Le RFC étudie ensuite le cas de HTTP, le protocole vedette de l'Internet (RFC 7230 et suivants). Sa simplicité et son efficacité ont largement contribué à son immense succès, qui a à son tour entrainé celui de l'Internet tout entier. On voit même aujourd'hui des tas de services non-Web utiliser HTTP comme substrat. Du fait de cette utilisation massive, les conséquences des caractéristiques de HTTP pour les droits humains ont été beaucoup plus étudiées que le cas du DNS.
Premier problème, HTTP est par défaut peu sûr, avec des communications en clair, écoutables et modifiables. Si la solution HTTPS est très ancienne (le RFC 2828 a dix-sept ans…, et SSL avait été décrit et mis en œuvre avant), elle n'a été massivement déployée que depuis peu, essentiellement grâce au courage d'Edward Snowden.
En attendant ce déploiement massif de HTTPS, d'innombrables équipements réseau de censure et de détournement de HTTP ont été fabriqués et vendus (par exemple par Blue Coat mais ils sont loin d'être les seuls). Celui qui veut aujourd'hui empêcher ou perturber les communications par HTTP n'a pas besoin de compétences techniques, les solutions toutes prêtes existent sur le marché.
Un autre RFC qui touchait directement aux droits humains et qui avait fait pas mal de bruit à l'IETF est le RFC 7725, qui normalise le code d'erreur 451, renvoyé au client lorsque le contenu est censuré. Il permet une « franchise de la censure », où celle-ci est explicitement assumée.
Les discussions à l'IETF avaient été chaudes en partie parce que l'impact politique de ce RFC est évident, et en partie parce qu'il y avait des doutes sur son utilité pratique. Beaucoup de censeurs ne l'utiliseront pas, c'est clair, soit parce qu'ils sont hypocrites, soit parce que les techniques de censure utilisées ne reposent pas sur HTTP mais, par exemple, sur un filtrage IP. Et, lorsque certains l'utilisent, quelle utilité pour les programmes ? Notre RFC explique que le principal intérêt est l'étude du déploiement de la « censure honnête » (ou « censure franche »). C'est le cas de projets comme Lumen. Du code est d'ailleurs en cours de développement pour les analyses automatiques des 451 (on travaillera là-dessus au hackathon de la prochaine réunion IETF).
Outre la censure, l'envoi du trafic en clair permet la surveillance massive, par exemple par les programmes Tempora ou XKeyscore. Cette vulnérabilité était connue depuis longtemps mais, avant les révélations de Snowden, la possibilité d'une telle surveillance de masse par des pays supposés démocratiques était balayée d'un revers de main comme « paranoïa complotiste ». Pour la France, souvenons-nous qu'une société française vend des produits d'espionnage de leur population à des dictatures, comme celle du défunt Khadafi.
D'autre part, l'attaque active, la modification des données en transit, ne sert pas qu'à la censure. Du trafic HTTP changé en route peut être utilisé pour distribuer un contenu malveillant (possibilité utilisée dans QUANTUMINSERT/FOXACID) ou pour modifier du code envoyé lors d'une phase de mise à jour du logiciel d'une machine. Cela semble une attaque compliquée à réaliser ? Ne vous inquiétez pas, jeune dictateur, des sociétés vous vendent ce genre de produits clés en main.
HTTPS n'est évidemment pas une solution magique, qui assurerait la protection des droits humains à elle seule. Pour ne citer que ses limites techniques, sa technologie sous-jacente, TLS (RFC 5246) a été victime de plusieurs failles de sécurité (sans compter les afaiblissements délibérés comme les célèbres « algorithmes pour l'exportation »). Ensuite, dans certains cas, un·e utilisat·eur·rice peut être incité·e à utiliser la version en clair (attaque par repli, contre laquelle des techniques comme celles du RFC 6797 ont été mises au point).
HTTPS n'étant pas obligatoire, la possibilité d'une attaque par repli existe toujours. Pour HTTP/2, il avait été envisagé d'imposer HTTPS, pour qu'il n'y ait plus de version non sûre, mais le RFC 7540 n'a finalement pas entériné cette idée (que le RFC 8164 a partiellement ressorti depuis.)
Autre protocole étudié, XMPP (RFC 6120). Un de ses principes est que le logiciel client (par exemple pidgin) ne parle pas directement au logiciel du correspondant, mais passe forcément par un (ou deux) serveur(s). Cette architecture présente des avantages pratiques (si le correspondant est absent, son serveur peut indiquer cette absence à l'appelant) mais aussi en matière de protection (on ne voit pas l'adresse IP de l'appelant). Ces serveurs sont fédérés entre eux, XMPP, contrairement à des protocoles inférieurs comme Slack ne peut donc pas être arrêté par décision supérieure.
Mais XMPP a aussi des inconvénients. Les utilisat·eurs·rices sont
identifiés par un JID comme
bortzmeyer@example.com/home
qui comprend une
« ressource » (le terme après la barre oblique) qui, en pratique,
identifie souvent une machine particulière ou un lieu
particulier. En général, ce JID est présenté tel quel aux
correspondants, ce qui n'est pas idéal pour la vie privée. D'autre
part, les communications sont en clair par défaut, mais peuvent
être chiffrées, avec TLS. Sauf que l'utilisat·eur·rice ne sait pas si
son serveur chiffre avec le serveur suivant, ou bien le serveur
final avec son correspondant. Sans possibilité d'évaluation de la
sécurité, il faut donc faire une confiance
aveugle à tous les serveurs pour prendre des précautions. Et
espérer qu'ils suivront tous le « XMPP
manifesto ».
Si XMPP lui-même est fédéré et donc relativement résistant à la censure, les salles collectives de discussion sont centralisées. Chaque salle est sur un serveur particulier, une sorte de « propriétaire », qui peut donc contrôler l'activité collective, même si aucun des participants n'a de compte sur ce serveur. (En prime, ces salles sont une extension du protocole, spécifiée dans le XEP-0045, pas mise en œuvre de manière identique partout, ce qui est un problème non-politique fréquent avec XMPP.)
Et le pair-à-pair, lui, quelles sont ses implications pour les droits humains ? D'abord, il faut évidemment noter que ce terme ne désigne pas un protocole particulier, qu'on pourrait analyser en détail, mais une famille de protocoles très divers (RFC 5694). L'application la plus connue du pair-à-pair est évidemment l'échange de fichiers culturels, mais le pair-à-pair est une architecture très générale, qui peut servir à plein de choses (Bitcoin, par exemple).
À l'époque des GAFA, monstres centralisés qui contrôlent toutes les interactions entre utilisat·eur·rice·s, le pair-à-pair est souvent présenté comme la solution idéale à tous les problèmes, notamment à la censure. Mais la situation est plus compliquée que cela.
D'abord, les réseaux en pair-à-pair, n'ayant pas d'autorité centrale de certification des contenus, sont vulnérables aux diverses formes d'empoisonnement des données. On se souvient des faux MP3 sur eDonkey, avec un nom prometteur et un contenu décevant. Un attaquant peut aussi relativement facilement corrompre, sinon les données, en tout cas le routage qui y mène.
Comme les protocoles pair-à-pair représentent une bonne part du trafic Internet, et qu'ils sont souvent identifiables sur le réseau, le FAI peut être tenté de limiter leur trafic.
Plus gênant, question droits humains, bien des protocoles pair-à-pair ne dissimulent pas l'adresse IP des utilisat·eur·rice·s. En BitTorrent, si vous trouvez un pair qui a le fichier qui vous intéresse, et que vous le contactez, ce pair apprendra votre adresse IP. Cela peut servir de base pour des lettres de menace ou pour des poursuites judiciaires (comme avec la HADOPI en France). Il existe des réseaux pair-à-pair qui déploient des techniques de protection contre cette fuite d'informations personnelles. Le plus ancien est Freenet mais il y a aussi Bitmessage. Ils restent peu utilisés.
Autre danger spécifique aux réseaux pair-à-pair, les attaques Sybil. En l'absence d'une vérification que l'identité est liée à quelque chose de coûteux et/ou difficile à obtenir, rien n'empêche un attaquant de se créer des millions d'identités et de subvertir ainsi des systèmes de vote. L'attaque Sybil permet de « bourrer les urnes » virtuelles. (Ne ratez pas l'article de Wikipédia sur l'astroturfing.)
C'est pour lutter contre cette attaque que Bitcoin utilise la preuve de travail et que CAcert utilise des certifications faites pendant des rencontres physiques, avec vérification de l'identité étatique. Le RFC note qu'on n'a pas actuellement de solution générale au problèmes des attaques Sybil, si on exige de cette solution qu'elle soit écologiquement durable (ce que n'est pas la preuve de travail) et entièrement pair-à-pair (ce que ne sont pas les systèmes d'enrôlement typiques, où un acteur privilégié vérifie les participants à l'entrée). Quant aux solutions à base de « réseaux de connaissances » (utilisées dans le Web of Trust de PGP), elles sont mauvaises pour la vie privée, puisqu'elles exposent le graphe social des participants.
Bref, le pair-à-pair n'est pas actuellement la solution idéale à tous les problèmes de droits humains, et les recherches doivent se poursuivre.
Un autre outil est souvent présenté comme solution pour bien des problèmes de respect des droits humains, notamment pour la sécurité de ceux qui vivent et travaillent dans des pays dictatoriaux, le VPN. On entend parfois des discussions entre militants des droits humains, ou bien entre journalistes, sur les avantages comparés de Tor et du VPN pour regarder le Web en toute sécurité. En fait, les deux ne fournissent pas réellement le même service et, pire, les propriétés du VPN sont souvent mal comprises. Le VPN fonctionne en établissant une liaison sécurisée (authentifiée, chiffrée) avec un fournisseur, qui va ensuite vous connecter à l'Internet. Il existe plusieurs systèmes techniques ouverts pour cela (IPsec, OpenVPN) mais la question centrale et difficile est le choix du fournisseur. Les VPN sont très populaires, et il existe donc une offre commerciale abondante. Mais, en général, il est impossible d'évaluer sa qualité, aussi bien technique (même si le protocole est standard, le fournisseur impose souvent un logiciel client à lui, binaire non auditable, et des failles ont déjà été découvertes dans certains VPN) que politique (ce fournisseur de VPN qui dit ne pas garder de journaux dit-il la vérité ?) On est très loin de l'attention qui a été portée à la sécurité de Tor, et des innombrables évaluations et analyses dont Tor a fait l'objet !
Il existe aussi des attaques plus sophistiquées (et pas à la portée de la première police venue) comme la corrélation de trafic (entre ce qui entre dans le VPN et ce qui en sort) si l'attaquant peut observer plusieurs points du réseau (la NSA le fait).
Donc, un rappel à tou·te·s les utilisat·eur·rices·s de VPN : la question la plus importante est celle de votre fournisseur. Le VPN peut vous promettre l'anonymat, vous ne serez pas pour autant anonyme vis-à-vis de votre fournisseur. Celui-ci peut vous trahir ou, tout simplement, comme il est situé dans un pays physique, être forcé par les autorités de ce pays de vous dénoncer.
Une question bien plus délicate avait fait l'objet de nombreux débats à l'IETF, celle d'une possibilité de considérer certaines attaques dDoS comme « légitimes ». C'est par exemple un point de vue qui a été défendu par Richard Stallman. La position classique de l'IETF est qu'au contraire toutes les attaques dDoS sont négatives, impactant l'infrastructure (y compris des tas d'innocents) et sont au bout du compte une attaque contre la liberté d'expression. En simplifiant, il existe trois types d'attaques dDoS, les volumétriques (on envoie le plus de paquets ou d'octets possibles, espérant épuiser les ressources du réseau), les attaques sur les protocoles intermédiaires (comme les SYN flood ou comme le très mal nommé ping of death), attaques qui permettent à l'assaillant de n'envoyer que peu de paquets/octets, et enfin les attaques applicatives, visant les failles d'une application. Une attaque faite par LOIC tient de l'attaque volumétrique (on envoie le plus de requêtes HTTP possibles) et de l'attaque applicative, puisqu'elle ne fonctionne que parce que l'application n'arrive pas à suivre (sur la plupart des sites Web, où il faut exécuter des milliers de lignes de code PHP ou Java pour afficher la moindre page, l'application craque avant le réseau).
Dans les trois cas, cette possibilité d'attaque est avant tout une menace contre les médias indépendants, contre les petites associations ou les individus qui ne peuvent pas ou ne veulent pas payer la « protection » (le mot a un double sens en anglais…) des sociétés spécialisées. Et les attaques dDoS peuvent faciliter la tâche des censeurs hypocrites : il suffit de déguiser une censure en une attaque par déni de service. Une des principales raisons pour lesquelles on ne peut pas comparer l'attaque dDoS à une manifestation est que, dans une attaque dDoS, la plupart des participants ne sont pas volontaires, ce sont des zombies. Lorsque des gens manifestent dans la rue, ils donnent de leur temps, et parfois prennent des risques personnels. Lorsqu'une organisation puissante loue les services d'un botnet pour faire taire par dDoS un gêneur, elle ne dépense qu'un peu d'argent.
Il y a bien sûr quelques exceptions (l'opération Abibil ou bien le Green Movement) mais elles sont rares. Il est donc parfaitement justifié que l'IETF fasse tout son possible pour rendre les attaques dDoS plus difficiles (RFC 3552, section 4.6). Dans la discussion menant à ce nouveau RFC 8280, certaines voix se sont élevées pour demander qu'on puisse lutter seulement contre les « mauvaises » dDoS. Mais c'est aussi absurde que l'idée récurrente des ministres de faire de la cryptographie « légale » qui ne pourrait protéger que les gens honnêtes !
Nous en arrivons maintenant à la partie la plus utilitaire de ce RFC, la section 6, qui est la méthodologie qui devrait être suivie lors du développement d'un nouveau protocole, pour comprendre son impact sur les droits humains, et pour essayer de minimiser les conséquences négatives, et maximiser les positives. Cette section 6 (depuis largement remplacée par le RFC 9620) concerne donc surtout les développeurs de protocole, par exemple les auteurs des RFC techniques. (C'est pour cela que le début de la section 6 répète beaucoup de choses dites avant : on pense que pas mal de techniciens ne liront que cette section.) Évidemment, les conséquences (bonnes ou mauvaises) d'un protocole, ne sont pas uniquement dans la norme technique qui le définit. La façon dont le protocole est mis en œuvre et déployé joue un rôle crucial. (Par exemple, la domination excessive de Gmail n'est pas inscrite dans le RFC 5321.)
Un bon exemple d'une telle démarche est donnée par le RFC 6973, sur la protection de la vie privée. La première responsabilité du développeur de protocole est d'examiner les menaces sur les droits humains que ce protocole peut créer ou aggraver. De même qu'il est recommandé de réfléchir aux conséquences d'un nouveau protocole pour la sécurité de l'Internet (RFC 3552), et sur les conditions dans lesquelles ce protocole est utile, de même il faut désormais réfléchir aux conséquences de son protocole sur les droits humains. Notons que notre RFC ne dit pas « voici ce qu'il faut faire pour respecter les droits humains ». Cela serait clairement irréaliste, vu la variété des menaces et la diversité des protocoles. Notre RFC demande qu'on se pose des questions, il ne fournit pas les réponses. Et il n'impose pas d'avoir dans chaque RFC une section « Human Rights Considerations » comme il existe une « Security Considerations » obligatoire.
Bon, maintenant, la liste des choses à vérifier quand vous concevez un nouveau protocole (section 6.2). À chaque fois, il y a une ou plusieurs questions, une explication, un exemple et une liste d'impacts. Par exemple, pour la question de la connectivité, les questions sont « Est-ce que votre protocole nécessite des machines intermédiaires ? Est-ce que ça ne pourrait pas être fait de bout en bout, plutôt ? Est-ce que votre protocole marchera également sur des liens à faible capacité et forte latence ? Est-ce que votre protocole est à état (alors que les protocoles sans état sont souvent plus robustes) ? » L'explication consiste surtout à répéter l'intérêt des systèmes de bout en bout (RFC 1958). L'exemple est évidemment celui des conséquences négatives des middleboxes. Et les impacts sont les conséquences sur la liberté d'expression et la liberté d'association. Bien sûr, tous les protocoles IETF se préoccupent peu ou prou de connectivité, mais ce n'était pas considéré jusqu'à présent comme pouvant impacter les droits humains.
Sur le deuxième point à vérifier, la vie privée, notre RFC renvoie au RFC 6973, qui demandait déjà aux auteurs de protocoles de faire attention à ce point.
Le troisième point est celui de la neutralité vis-à-vis du contenu. Il reste un peu vague, il n'y a pas actuellement d'exemple de protocole IETF qui soit activement discriminant vis-à-vis du contenu.
Quatrième point qui nécessite l'attention du développeur de protocole, la sécurité. Il est déjà largement traité dans de nombreux autres RFC (notamment le RFC 3552), il faut juste rappeler que ce point a des conséquences en matières de droits humains. Si un protocole a une faille de sécurité, cela peut signifier l'emprisonnement, la torture ou la mort pour un dissident.
En prime, le RFC rappelle que, contrairement à une utilisation réthorique fréquente, il n'y a pas une sécurité mais plusieurs services de sécurité. (Et certaines de ses propriétés peuvent avoir des frictions, par exemple la disponibilité et la confidentialité ne s'entendent pas toujours bien.)
Cinquième point que le développeur de protocoles doit vérifier, l'internationalisation (voir aussi le douzième point, sur la localisation). Eh oui, restreindre l'utilisation de l'Internet à ceux qui sont parfaitement à l'aise en anglais n'irait pas vraiment dans le sens des droits humains, notamment des droits à participer à la vie politique et sociale. D'où les questions « Est-ce que votre protocole gère des chaînes de caractères qui seront affichées aux humains ? Si oui, sont-elles en Unicode ? Au passage, avez-vous regardé le RFC 6365 ? » Dans le contexte IETF (qui s'occupe de protocoles et pas d'interfaces utilisateur), l'essentiel du travail d'internationalisation consiste à permettre de l'Unicode partout. Partout ? Non, c'est un peu plus compliqué que cela car l'IETF distingue les textes prévus pour les utilisat·eur·rice·s de ceux prévus pour les programmes (RFC 2277). Seuls les premiers doivent absolument permettre Unicode. (Cette distinction ne marche pas très bien pour les identificateurs, qui sont prévus à la fois pour les utilisat·eur·rice·s et pour les programmes, c'est le cas par exemple des noms de domaine.)
En prime, petite difficulté technique, il ne suffit pas
d'accepter Unicode, il faut encore, si on accepte d'autres
jeux de
caractères et/ou encodages,
l'indiquer (par exemple le charset=
de
MIME), sinon on risque le mojibake. Ou alors, si on n'accepte qu'un
seul jeu de caractères / encodage, ce doit être UTF-8.
Sixième point sur la liste, une question dont les conséquences pour les droits humaines sont évidentes, la résistance à la censure. « Est-ce que votre protocole utilise des identificateurs qui peuvent être associés à des personnes ou à un contenu spécifique ? Est-ce que la censure peut être explicite ? Est-ce que la censure est facile avec votre protocole ? Si oui, ne pourrait-on pas le durcir pour la rendre plus difficile ? »
Un exemple est bien sûr la longue discussion du passé au sujet d'une méthode de fabrication des adresses IPv6. Le mécanisme recommandé à l'origine mettait l'adresse MAC dans l'adresse IP. Outre l'atteinte à la vie privée, cela facilitait la censure, permettant de bloquer un contenu pour seulement certaines personnes. (Ce mécanisme a été abandonné il y a longtemps, cf. RFC 4941.) Quand au cas de rendre la censure explicite, c'est une référence au code 451 (RFC 7725).
Septième point, les « normes ouvertes ». Intuitivement, il est évident qu'il vaut mieux des normes ouvertes que fermées. Mais attention, il n'existe pas de définition claire et largement adoptée, même pas à l'intérieur de l'IETF (qui est certainement une organisation très ouverte). Les questions posées dans ce RFC 8280 donnent une idée des critères qui peuvent permettre de décider si une norme est ouverte ou pas : « Le protocole est-il documenté publiquement ? Sa mise en œuvre peut-elle être faite sans code privateur ? Le protocole dépend t-il d'une technologie contrôlée par une entreprise particulière ? Y a-t-il des brevets (RFC 3979 et RFC 6701) ? »
Ce sont les normes ouvertes de la famille TCP/IP qui ont permis le développement et le déploiement massif de l'Internet. Les appropriations intellectuelles comme le secret industriel ou comme les brevets auraient tué l'Internet dans l'œuf. Il est donc logique que l'IETF soit une organisation particulièrement ouverte : les RFC sont publics et librement redistribuables, bien sûr (ce qui n'est pas le cas des normes d'autres SDO comme l'AFNOR, l'ISO ou l'IEEE), mais l'IETF publie également ses documents temporaires, ses listes de diffusion et ses réunions (ce que ne fait pas, par exemple, l'UIT).
(On note que le RFC 6852 traite également cette question mais c'est un document purement tactique, qui fait du « open washing » en faisant comme si l'IEEE était ouverte.)
Je saute le point huit, sur l'acceptation de l'hétérogénéité du réseau, et j'en arrive à l'important point neuf, sur l'anonymat. Le terme est très galvaudé (« Bitcoin est une monnaie anonyme » et autres erreurs). Il est souvent utilisé par les non-spécialistes comme synonyme de « une autre identité que celle de l'état civil » (ce qui est en fait la définition du pseudonyme, traité au point suivant). Normalement, même si la définition du RFC 4949 est très peu claire, l'anonymat implique la non-traçabilité : si un système est réellement anonyme, il ne doit pas être possible d'attribuer deux actions à la même entité.
Autre erreur courante quand on parle d'anonymat, la considérer comme une propriété binaire. C'est ce qui est fait quand un responsable ignorant affirme « les données sont anonymisées » (cela doit en général déclencher un signal d'alarme). En effet, il existe de nombreuses techniques, et en progrès rapide, pour « désanonymiser », c'est-à-dire pour relier des actions qui ne l'étaient a priori pas.
Cette confusion est d'autant plus dommage que l'anonymat est une propriété essentielle pour la sécurité du citoyen dans un monde numérique. Autrefois, la plupart des actions qu'on faisait dans la journée étaient anonymes, au sens où un observateur extérieur ne pouvait pas facilement les relier entre elles. Aujourd'hui, si vous mettez une photo sur Instagram, achetez un livre sur Amazon, et écrivez un document sur Google Docs, toutes ces actions sont facilement reliables entre elles, même si vos comptes se nomment respectivement « titi75 », « jean.durand » et « le_type_du_coin ». Par défaut, dans le monde numérique, tout est traçable, et il faut déployer des technologies compliquées pour retrouver un peu d'obscurité. En tout cas, rappelez-vous que l'anonymat n'est jamais parfait : c'est un but souhaitable, mais pas forcément atteignable.
Par exemple, la présence de votre adresse IP dans chaque paquet est un moyen simple de relier toutes vos activités (il en existe d'autres). Il est donc tout à fait légitime que l'adresse IP soit regardée comme une donnée personnelle.
Le pseudonymat, dixième point, est une propriété moins forte. C'est simplement le fait d'utiliser une identité persistante qui n'est pas l'identité officielle. On va utiliser un pseudonyme quand on veut masquer son orientation sexuelle, ou sa transidentité, ou l'entreprise où on travaille, mais tout en gardant une identité constante, par exemple pour avoir une réputation en ligne. C'est souvent une protection nécessaire contre le harcèlement, dont les femmes sont particulièrement fréquemment victimes en ligne. Outre les pseudonymes qu'on choisit, la nature du monde numérique fait que plein d'identificateurs attribués automatiquement sont des pseudonymes. Ainsi, une adresse IP est un pseudonyme (elle cesse de l'être dès que votre FAI communique le nom et l'adresse de l'abonné aux autorités). Une adresse Bitcoin est un pseudonyme (Bitcoin est très traçable, c'est nécessaire pour son fonctionnement).
L'auteur·e de protocoles doit donc se méfier des informations qu'ielle expose. Si elles permettent de retrouver la personne à l'origine de la communication, ce sont des données personnelles. Par exemple, si vous exportez des données contenant des adresses IP (exemples en RFC 7011 ou bien pour les journaux d'un serveur), une des façons de brouiller la traçabilité (et donc de passer du pseudonymat à un relatif anonymat) est de ne garder qu'un préfixe assez général. Condenser les adresses IP n'est pas très efficace, un attaquant, un « désanonymiseur » peut condenser toutes les adresses possibles et ainsi retrouver l'information. D'une manière générale, soyez modeste : réellement anonymiser est très difficile.
Le onzième point concerne un sujet dont les conséquences en
matière de droits humains sont claires pour quiconque a suivi les
conférences Paris Web :
l'accessibilité à tou·te·s, y compris en cas
de handicap. L'accessibilité est une propriété nécessaire pour
faire respecter le droit à ne pas être victime de
discrimination. Cela ne concerne a priori pas l'IETF, qui ne fait
pas d'interface utilisateur, mais c'est utile pour d'autres
SDO. Ainsi, le RFC donne l'exemple de
HTML, où l'obligation de mettre un attribut
alt
pour les images, oblige à réfléchir à
l'accessibilité de la page Web aux malvoyants.
Le treizième point porte sur un concept très flou et d'autant plus répété qu'il n'a pas de définition claire : la « décentralisation ». Mon article à JRES sur ce sujet donne une idée de la complexité de la question. Le RFC traite la question par plusieurs questions : « Est-ce que le protocole a un point de contrôle unique ? Est-ce que le protocole ne pourrait pas être fédéré plutôt ? » Ça reste à mon avis trop vague mais, au moins, ça pousse les concepteurs de protocoles à réfléchir. La plupart des protocoles de base de l'Internet sont décentralisés ou fédérés comme vous voulez (et c'est explicitement souhaité par le RFC 3935), mais les services à la mode sont en général centralisés. (XMPP est fédéré mais dans les startups, on est obligés d'utiliser Slack qui est centralisé.)
Passons sur le point 14, la fiabilité (c'est sûr qu'un réseau qui marche, c'est mieux) et voyons le point 15, la confidentialité. Son impact sur les droits humains est clair : si des gens peuvent lire ma correspondance privée, mes droits sont clairement violés. La solution technique la plus évidente est le chiffrement et, aujourd'hui, le RFC 8280 estime à juste titre qu'un protocole sans possibilité de chiffrement (par exemple RFC 3912) est à éviter (RFC 3365). Et, même si le RFC ne le dit pas explicitement, il doit évidemment choisir d'un chiffrement sérieux, donc sans portes dérobées, sans affaiblissement délibéré.
Rédigé il y a trop longtemps, cette section dit que le DNS ne dispose pas de cette possibilité de chiffrement, mais c'est faux, depuis le RFC 7858.
Le RFC note à juste titre que le chiffrement ne protège pas contre les intermédiaires légitimes, comme dans l'exemple de XMPP cité plus haut : le chiffrement entre le client et le premier serveur XMPP ne protège pas contre ce serveur, qui voit tout passer en clair. Mais le RFC oublie de dire qu'il y a également le problème des extrémités : faire du HTTPS avec Gmail ne vous protège pas contre PRISM. Il faut donc également prévoir de la minimisation (envoyer le moins de données possible) pas seulement du chiffrement.
Je saute les points 16 (intégrité), 17 (authentification) et 18 (adaptabilité), je vous laisse les lire dans le RFC. Le dernier point, le dix-neuvième, porte sur la transparence : les utilisat·eur·rice·s peuvent-il·elle·s voir comment fonctionne le protocole et notamment quels sont les acteurs impliqués ? Par exemple, un service qui semble « gratuit » peut l'être parce qu'il y a derrière une grosse activité économique, avec de la publicité ciblée en exploitant vos données personnelles. Bref, si c'est gratuit, c'est peut-être que vous êtes le produit. Et vous ne le voyez peut-être pas clairement.
Voilà, si vous voulez en savoir plus, le RFC a une colossale bibliographie. Bonne lecture. Si vous préférez les vidéos, il y a mon intervention à Radio-France sur ce sujet.
Première rédaction de cet article le 29 octobre 2017
La société AXA (assurance et autres
services financiers) utilise souvent pour sa communication des
URL où le nom de domaine est
go.axa.fr
. On le trouve dans les messages envoyés aux
clients, par exemple. Quel est le problème de sécurité et
d'éducation que cela pose ?
Si vous voulez voir de tels URL,
demandez simplement à un moteur de recherche un truc du genre
inurl:go.axa.fr
et vous en trouverez
plein, apparemment utilisés dans des campagnes de promotion
(« téléchargez une application permettant de mieux gérer votre
compte AXA Banque » ou bien voir la
page Facebook officielle d'AXA Banque). Prenons par exemple
http://go.axa.fr/promium/
, « le programme
d'AXA France destiné aux Professionnels ». Normalement, le client,
même méfiant,
n'hésitera pas à suivre ce lien : il est bien situé sous le
domaine d'AXA,
axa.fr
. Si
on suit les conseils
habituels « En passant la souris au-dessus du lien proposé,
vous pouvez repérer s’il pointe bien vers l’adresse du site
annoncée dans le message. Si l’adresse est différente, soyez
méfiant, et évitez de cliquer sur le lien. De manière générale, il
est préférable de saisir manuellement l’adresse dans le
navigateur. », on ne devrait pas se faire avoir. (Oui, je sais
que, dans le monde réel,
personne ne suit ces conseils.)
Sauf que… Le nom go.axa.fr
pointe vers
l'adresse IP
74.217.253.90
. Hébergée aux États-Unis chez
Internap, elle ne semble pas a priori avoir
de relation avec AXA. Pourtant, s'y connecter en
HTTP redirige bien vers la bonne page :
% curl -v http://go.axa.fr/promium/ * Trying 74.217.253.90... * TCP_NODELAY set * Connected to go.axa.fr (74.217.253.90) port 80 (#0) > GET /promium/ HTTP/1.1 ... < HTTP/1.1 301 Moved Permanently < Server: post/2.0 < Location: https://mediazone.axa/communique-de-presse/axa-programme-promium#
On est bien renvoyé vers une page d'AXA, dans le
TLD .axa
. Mais,
minute, il y a une chose bizarre dans la réponse, le champ
Server:
. Il indique post
qui identifie le serveur de redirection utilisé par le service
po.st
, un moteur de redirection comme
bit.ly
. D'ailleurs, si on visite
http://go.axa.fr/
(sans rien après la
dernière barre oblique), on arrive bien chez
po.st
:
% curl -v http://go.axa.fr/ * Trying 74.217.253.90... * TCP_NODELAY set * Connected to go.axa.fr (74.217.253.90) port 80 (#0) > GET / HTTP/1.1 ... < HTTP/1.1 302 Found < Server: post/2.0 < Location: https://www.po.st
Si vous voulez un autre signe, vous pouvez essayer en
HTTPS,
https://go.axa.fr/promium/
. Le
certificat ne sera pas accepté car il ne
couvre que po.st
, pas go.axa.fr
.
Bon, donc, en fait, AXA utilise
po.st
. Est-ce grave ? En tout cas, c'est
ennuyeux, car ces redirecteurs/raccourcisseurs d'URL ont deux problèmes :
Paranoïa complète de ma part ? po.st
est
propriété de RadiumOne (source : https://www.po.st/about/post
), société états-unienne, et ces derniers ne cachent pas leurs
activités : « a company that generates first-party data
about actual customers -- from their behaviors, actions and
interests demonstrated across the web and mobile. We access tens
of billions of real-time impressions each day across the Web,
video, social and mobile to reach consumers in real-time no matter
where they are. Our intelligent software and methodologies
increase the relevance and personalization of ads through
sophisticated algorithms that find valuable characteristics, gauge
consumer behaviors, and target ads with laser focus to the right
audiences ». Bref, rediriger à travers
po.st
, c'est donner plein d'informations à
une enterprise états-unienne, non tenue par le
RGPD. Le but de surveillance à des fins
marketing est encore plus clair sur l'image en
https://www.po.st/assets/img/sharing/sharing-brands-1.png
ou sur la page
https://www.po.st/sharing/brands
.
(Concernant le RGPD, des juristes m'ont fait
remarquer que po.st
est bien tenu de
respecter le RGPD, du moment que les données concernées sont
celles de citoyens européens, ce qui est le cas ici (principe
d'extra-territorialité du RGPD). Ils ont bien sûr raison, mais la
question est plutôt « en pratique, que se passera-t-il ? » Je
doute fort qu'AXA soit inquiété pour ces traitements.)
Je ne cherche pas spécialement à taper sur AXA. Des tas
d'entreprises font cela. En général, le service communication
ignore complètement tous les conseils de sécurité. Ainsi, on
s'inscrit à la
newsletter d'une société
dont le domaine est mabanque.example
et on
reçoit ensuite des messages envoyés depuis un tout autre
domaine. Impossible dans ces conditions de mettre en pratique les
conseils de l'ANSSI indiqués plus haut !
Mais, ici, le cas est un peu plus original dans le mesure où le
nom affiché au client (go.axa.fr
) est conçu
pour inspirer confiance, dissimulant à l'usager qu'il verra ses
données être enregistrées par po.st
. Ce genre
de pratiques met donc en péril toutes les tentatives d'éducation à
la sécurité qui ont été faites depuis des années, et qui
encourageaient à faire attention au nom de domaine dans les URL.
(Notons qu'utiliser un nom de domaine à soi pour rediriger via
po.st
nécessite l'accord de
po.st
. Si, dans
http://go.axa.fr/promium/
, vous remplacez
go.axa.fr
par po.st
,
cela ne marchera pas.)
Et puis c'est dommage que cette utilisation d'un redirecteur étranger et peu soucieux de protection des données personnelles soit le fait d'une société qui s'était permis de promouvoir un « permis Internet » qui avait été, à juste titre, fortement critiqué.
Mes remerciements à Philippe Meyer pour avoir attiré mon attention sur cet intéressant cas.
Auteur(s) du livre : Eric H. Cline
Éditeur : Princeton University Press
978-0-691-16838-8
Publié en 2014
Première rédaction de cet article le 29 octobre 2017
Vous savez pourquoi tant de civilisations de la Méditerranée orientale ont disparu assez rapidement, autour de 1 177 AEC ? Moi non, mais Eric Cline s'est penché sur le problème, sur lequel je ne connaissais rien. Les grandes catastrophes sont toujours fascinantes. Que savons-nous sur celle-ci ?
Posons donc le décor. Vers 1 200 AEC, dans l'âge du bronze tardif, la Méditerranée orientale abrite une concentration de civilisations avancées. On connait l'Égypte des pharaons, bien sûr, mais il y a aussi les Hittites, les Mycéniens, les Minoens et plein d'autres, ainsi que beaucoup de cités-États riches du commerce, comme Ougarit ou Troie. Tout cela se passait longtemps avant la Grèce antique et a fortiori longtemps avant Rome. Tout ce monde échangeait, s'écrivait (bénédiction pour les archéologues du futur), se visitait et se faisait la guerre.
Mais, quelques dizaines d'années après, tout a disparu. Si l'Égypte a continué, ce ne sera que sous forme d'un royaume très diminué (privé de ses possessions asiatiques). Les malheurs de Troie seront écrits sous forme d'un poème épique, bien plus tard. D'autres disparaitront en laissant peu de traces, comme le royaume hittite, pourtant la deuxième superpuissance de l'époque, après l'Égypte.
Est-ce qu'on connait la cause ? Pas vraiment (oui, désolé, j'anticipe sur la fin du livre). Des tas d'explications ont été proposées, des tremblements de terre, des sécheresses, des invasions par les peu connus peuples de la mer… Aucune ne semble pouvoir tout expliquer, il y a peut-être eu simplement combinaison de facteurs au mauvais moment. On ne sait même pas si les « peuples de la mer » étaient vraiment une armée d'invasion, ou simplement des réfugiés fuyant d'autres lieux touchés par la crise.
C'est que les traces archéologiques sont incomplètes et difficiles à interpréter. Quand on trouve une cité en ruines, était-ce le résultat d'un tremblement de terre ou d'une invasion, ou simplement du temps qui s'est écoulé depuis son abandon ? Eric Cline explique très bien comment l'archéologue d'aujourd'hui doit travailler, déchiffrant des tablettes d'argile, et regardant si on trouve des pointes de flèche (bon indice d'une attaque militaire). Tout est indice de nos jours, et l'auteur déplore le comportement des archéologues d'autrefois, comme Schliemann, qui y allait à la brutale, sans même noter ce qu'ils détruisaient au passage.
Cline fait revivre tout ce monde peu connu des profanes. On a tous entendu parler des pharaons égyptiens et de leurs pyramides, mais beaucoup moins des autres peuples de l'époque. J'ai beaucoup aimé la description du navire d'Uluburun, chargé d'innombrables richesses provenant de toute la région, et qui symbolise l'importance des échanges commerciaux pendant cette « première mondialisation ». Comme le note l'auteur, un pharaon égyptien de l'époque était sans doute autant préoccupé de l'approvisionnement de son pays en étain (indispensable pour le bronze, donc pour les armes), qu'un président états-unien aujourd'hui avec le remplissage des 4x4 en essence.
Bon, et après cette description d'une époque dynamique et d'une civilisation avancée, pourquoi cette disparition relativement rapide ? Bien sûr, le titre du livre est un peu exagéré. L'effondrement des civilisations méditerranéennes n'a pas duré qu'une seule année, le processus a pris plus de temps. Et tout n'a pas été perdu : si les échanges commerciaux se sont effondrés, si les grands royaumes ont disparu, si de nombreuses riches cités sont devenues des ruines, les gens n'ont pas oublié l'écriture, ni l'art, ni leurs techniques et, quelques siècles plus tard, c'est reparti comme avant.
Néanmoins, il y a bien eu une redistribution des cartes spectaculaires, dont les causes ne sont pas bien connues. Cline passe en revue toutes les hypothèses (comme il le note dans la postface, il ne manque que l'épidémie…) mais on reste à la merci de nouvelles découvertes archéologiques, qui changeront notre opinion sur cette crise. Cline note que, contrairement aux cas étudiés par Diamond dans son livre « Effondrement », qui ne couvre que des civilisations isolées, ici, c'est peut-être la forte interconnexion des civilisations méditerranéennes qui a causé leur chute en dominos.
En attendant, si vous voulez aider les scientifiques, je vous rappelle que le linéaire A est toujours à déchiffrer… (Il a cessé d'être utilisé avant l'effondrement, mais il pourrait permettre de mieux comprendre la civiisation minoenne.)
Première rédaction de cet article le 28 octobre 2017
Le système DANE, normalisé dans le RFC 6698, permet de vérifier l'authenticité du certificat d'un serveur TLS, grâce à des enregistrements signés dans le DNS. Comme ces enregistrements doivent toujours être synchrones avec le certificat, il est prudent de les superviser.
DANE fonctionne en publiant dans le DNS un enregistrement qui résume le certificat utilisé par le serveur TLS. Les certificats changents (renouvellement, révocation) et l'enregistrement dans le DNS doit donc suivre ces changements. En outre, dans la plupart des organisations de taille moyenne ou grosse, ce n'est pas la même équipe qui gère le DNS et les serveurs TLS. Il y a donc un gros potentiel de mécommunication, et c'est pour cela que la supervision est cruciale.
J'utilise le logiciel Icinga pour cette
supervision, mais l'essentiel des techniques présentées ici
marcherait avec tous les logiciels compatibles avec
l'API des Monitoring
Plugins. Première étape, il nous faut un programme qui
teste l'état des enregistrements
DANE. Nous allons nous appuyer sur le
programme tlsa
, qui fait partie de hash-slinger. Cet ensemble de programme est dans un paquetage
Debian, donc l'installer est aussi simple que :
% aptitude install hash-slinger
Une fois qu'il est installé, testons-le avec un site Web qui a des enregistrements DANE :
% tlsa --verify dns.bortzmeyer.org SUCCESS (Usage 1 [PKIX-EE]): Certificate offered by the server matches the one mentioned in the TLSA record and chains to a valid CA certificate (204.62.14.153) SUCCESS (Usage 1 [PKIX-EE]): Certificate offered by the server matches the one mentioned in the TLSA record and chains to a valid CA certificate (2605:4500:2:245b::42)
(Il y a deux lignes car le nom dns.bortzmeyer.org
a deux adresses IP et,
potentiellement, il pourrait s'agir de serveurs
HTTP, voire de deux machines différentes.)
En prime, tlsa
définit évidemment le code de
retour :
% tlsa --verify dns.bortzmeyer.org ; echo $? ... 0
Si l'enregistrement TLSA a un problème, le code de retour est différent de zéro :
% tlsa --verify dougbarton.us ; echo $? FAIL: Certificate offered by the server does not match the TLSA record (208.79.90.218) FAIL: Certificate offered by the server does not match the TLSA record (2607:f2f8:ab14::2) 2
Rien de magique dans la commande tlsa
, on
aurait pu faire la vérification manuellement, par exemple pour
mercredifiction.bortzmeyer.org
:
% dig TLSA _443._tcp.mercredifiction.bortzmeyer.org ... ;; ANSWER SECTION: _443._tcp.mercredifiction.bortzmeyer.org. 86400 IN TLSA 1 1 1 ( 928DDE55DF4CD94CDCF998C55085FBB5228B561CC237 A122D950260029C5B8C9 ) _443._tcp.mercredifiction.bortzmeyer.org. 86400 IN RRSIG TLSA 8 5 86400 ( 20171113165155 20171025071419 50229 bortzmeyer.org. eRNZfL8ERWhs0SIGYHOIMh1l+tzFFfvDmV7XlPxd8BYa ...
Puis comparer avec ce que donne openssl
. Les commandes
exactes à utiliser dépendent évidemment de l'usage et du
sélecteur indiqués dans l'enregistrement TLSA, cf. RFC 6698. Par exemple, avec le sélecteur 0 (on
prend tout le certificat, pas juste la clé), on pourrait utiliser
openssl x509 -fingerprint
. Ici, avec le
sélecteur 1 (seulement la clé) :
% openssl s_client -servername mercredifiction.bortzmeyer.org -showcerts -connect mercredifiction.bortzmeyer.org:443 | \ openssl x509 -noout -pubkey | openssl pkey -outform der -pubin | openssl dgst -sha256 ... (stdin)= 928dde55df4cd94cdcf998c55085fbb5228b561cc237a122d950260029c5b8c9
On voit qu'on a le même condensat,
928dde55df4cd94cdcf998c55085fbb5228b561cc237a122d950260029c5b8c9
,
donc tout va bien.
(Ne pas oublier le SNI - RFC 6066, l'option
-servername
, sans laquelle vous risquez de
récupérer un mauvais certificat.)
Bien, on a désormais un moyen de tester qu'un serveur TLS a bien un certificat qui correspond à l'enregistrement DANE. Maintenant, il faut emballer tout ça dans un script qui est conforme à l'API des monitoring plugins. Le script (trivial), écrit en shell, est disponible ici. Testons-le :
% /usr/local/lib/nagios/plugins/check_dane_http -H www.afnic.fr ; echo $? OK - SUCCESS (Usage 3 [DANE-EE]): Certificate offered by the server matches the TLSA record (192.134.5.24) SUCCESS (Usage 3 [DANE-EE]): Certificate offered by the server matches the TLSA record (2001:67c:2218:30::24) 0 % /usr/local/lib/nagios/plugins/check_dane_http -H dougbarton.us ; echo $? DANE server 'dougbarton.us' has a problem: FAIL: Certificate offered by the server does not match the TLSA record (208.79.90.218) FAIL: Certificate offered by the server does not match the TLSA record (2607:f2f8:ab14::2) 2 % /usr/local/lib/nagios/plugins/check_dane_http -H ssi.gouv.fr ; echo $? DANE server 'ssi.gouv.fr' has a problem: Unable to resolve ssi.gouv.fr.: Unsuccessful DNS lookup or no data returned for rrtype TLSA (52). 2
Le premier domaine a un enregistrement TLSA et marche bien. Le second, comme vu plus haut, a un enregistrement TLSA mais incorrect. Le troisième n'a pas d'enregistrement TLSA.
On peut désormais configurer Icinga pour utiliser ce script :
object CheckCommand "dane_http" { command = [ PluginContribDir + "/check_dane_http" ] arguments = { "-H" = "$dane_domain$" } } ... apply Service for (dane_domain => config in host.vars.dane_domains) { import "generic-service" check_command = "dane_http" vars.dane_domain = dane_domain } ... vars.dane_domains["dns.bortzmeyer.org"] = true
(Bon, ça serait mieux si on pouvait spécifier indépendemment le nom à tester, l'adresse IP du serveur, et le nom qui va apparaitre dans l'interface Web d'Icinga. Mais j'étais paresseux et j'ai fait simple.)
Voilà, désormais Icinga surveille périodiquement l'enregistrement DANE et le certificat TLS et tout va bien :
S'il y a un problème, on sera averti et on verra le problème dans l'interface Web d'Icinga. Ici, par exemple, l'enregistrement TLSA était pour le certificat entier (sélecteur 0) et un renouvellement a changé le certificat (mais pas la clé). Le message d'Icinga indiquait :
CRITICAL since 00:59 Service: mercredifiction.bortzmeyer.org Plugin Output DANE server 'mercredifiction.bortzmeyer.org' has a problem: FAIL: Certificate offered by the server does not match the TLSA record (217.70.190.232) FAIL: Certificate offered by the server does not match the TLSA record (2001:4b98:dc0:41:216:3eff:fece:1902)
Cet incident a permis de tester que la supervision de DANE marchait
bien 😁. Mais il mérite davantage de discussion. Dans ce cas
précis, l'enregistrement TLSA commençait par « 1 0 1 ». Cela veut
dire « Usage 1, Sélecteur 0, Correspondance 0 » (voir les
explications dans le RFC 6698) ou bien, en
utilisant les mnémoniques du RFC 7218 (que
tlsa
affiche), « Usage PKIX-EE, Sélecteur Cert,
Correspondance SHA-256 ». Et en français ? L'usage PKIX-EE veut dire que
l'enregistrement TLSA doit désigner un certificat qui est à la fois
valide selon les règles PKIX (RFC 5280) traditionnelles et selon DANE,
avec le certificat du serveur, pas celui de
l'AC. Ensuite, le sélecteur Cert indique que
l'enregistrement TLSA doit être un condensat
du certificat entier. Or, le renouvellement d'un certificat change
les métadonnées (comme, évidemment, la date
d'expiration) et donc le condensat. Dans ces conditions, il vaut
mieux utiliser le sélecteur 1 (SPKI), où l'enregistrement TLSA va
désormais indiquer uniquement la clé, pas tout le certificat. C'est
également l'avis
de Let's Encrypt..
Voilà, maintenant que ça marche, prochaine étape : faire la même chose pour
SMTP (RFC 7672),
tlsa
sachant gérer le TLS lancé après la
connexion. Exemple :
% tlsa --verify --port 25 --starttls smtp mail.bortzmeyer.org SUCCESS (Usage 2 [DANE-TA]): A certificate in the certificate chain offered by the server (including the end-entity certificate) matches the TLSA record (217.70.190.232) SUCCESS (Usage 2 [DANE-TA]): A certificate in the certificate chain offered by the server (including the end-entity certificate) matches the TLSA record (2001:4b98:dc0:41:216:3eff:fece:1902)
Autres solutions possibles pour tester DANE :
openssl
seul, avec
ses options -dane_tlsa_domain
et
-dane_tlsa_rrdata
: ainsi, openssl
s_client -dane_tlsa_domain mercredifiction.bortzmeyer.org
-dane_tlsa_rrdata "$(dig +short +nodnssec TLSA
_443._tcp.mercredifiction.bortzmeyer.org)" -connect
mercredifiction.bortzmeyer.org:443
vous affichera un
succès (DANE TLSA 1 1 1 ...c237a122d950260029c5b8c9
matched EE certificate at depth 0
) et le test sur un
domaine à problème (openssl s_client -dane_tlsa_domain
dougbarton.us -dane_tlsa_rrdata "$(dig +short +nodnssec TLSA
_443._tcp.dougbarton.us)" -connect dougbarton.us:443
)
donnera Verification error: No matching DANE TLSA
records
.Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : M. Nottingham
Chemin des normes
Première rédaction de cet article le 25 octobre 2017
Le lien est à la base du Web. Mais c'est seulement récemment, avec le RFC 5988 que notre RFC remplace, que certains manques ont été comblés :
Bien sûr, des formats normalisés qui permettent des liens, il y en a
plusieurs, et avant tout HTML, avec le fameux
élément <A>
(pour
anchor). Il y a aussi le plus récent
Atom (RFC 4287, notamment
la section 4.2.7). Comme HTML, Atom avait l'idée de registre
des types de liens, mais ces types étaient spécifiques à Atom. L'une
des idées phares du RFC 5988 et de celui-ci, son successeur RFC 8288, est de
généraliser le concept de type de lien et de le rendre accessible à
tous les formats et protocoles qui en ont besoin. Ce RFC
décrit un cadre général pour les types de liens, en partant de celui
d'Atom.
Second apport de cette norme, une (re-)définition de
l'en-tête HTTP Link:
,
utilisant évidemment le nouveau cadre général. Cet en-tête permettant
d'indiquer un lien dans la réponse HTTP, indépendamment du document
servi, avait été normalisé dans le RFC 2068, section 19.6.2.4,
puis, peu utilisé, avait été supprimé par le RFC 2616, avant de faire sa
réapparition ici, sous une forme quasi-identique à l'original. On peut
voir cet en-tête comme une représentation concrète du cadre de notre
RFC. D'autres apparaîtront sans doute.
Pour un exemple réel, regardez les en-têtes
Link:
de mon blog, il y en a un de type
licence
, suivant le RFC 4946. Avec Apache, cela se configure
simplement avec le module headers
et la
directive Header set Link "<http://www.gnu.org/copyleft/fdl.html>; rel=\"license\"; title=\"GFDL\""
.
Donc, qu'est-ce qu'un lien ? La section 2, la principale du RFC, le définit comme une connexion typée entre deux ressources (une ressource étant typiquement une page Web), nommées respectivement contexte et cible. Les deux ressources sont représentées par leur IRI (cf. RFC 3987, en notant que, dans la plupart des cas, les IRI des liens seront des URI). Le lien comprend :
Par exemple, dans le flux de syndication
Atom de mon blog, on trouvera un lien
<atom:link rel="alternate"
href="http://www.bortzmeyer.org/expose-go.html"/>
qui se décompose
en un contexte (l'entrée Atom dont l'IRI est
tag:bortzmeyer.org,2006-02:Blog/expose-go
), un
type (alternate
, qui indique une version
alternative de la ressource, ici une page HTML au lieu d'une entrée
Atom), et une cible (ici http://www.bortzmeyer.org/expose-go.html
). Il n'y a pas dans
cet exemple
d'attributs de la cible mais Atom en permet (par exemple
hfrelang
pour indiquer la langue de la cible ou bien
length
pour indiquer sa longueur - afin de
prévenir d'un long téléchargement, par exemple).
Cette définition du lien ne place aucune limite sur la cardinalité. Il peut y avoir zéro, un ou plusieurs liens partant d'une ressource et c'est la même chose pour les lients entrants.
La section 2 s'arrête là. Puisque ce RFC propose un cadre général, il ne formalise pas une syntaxe unique pour représenter les liens. Chaque format, chaque protocole, aura la sienne, la sérialisation.
Un des points les plus importants de cette définition des liens,
et qui est souvent ignorée des gens qui écrivent des pages Web, est
la notion de type d'un lien (section 2.1). Par
exemple, on a un type copyright
qui associe, via un lien, un document à l'auteur de
celui-ci. Point à retenir : ce type du lien ne doit pas être
confondu avec le type de médium du RFC 6838
comme text/html
ou
audio/ogg
.
Il y a deux sortes de type de lien : enregistrés ou bien
extensions. Les premiers font l'objet de la section 2.1.1. Ils ont
fait l'objet d'un processus formel d'enregistrement et leur liste est publiée sous forme d'un
registre
IANA. On y trouve par exemple via
(RFC 4287) ou hub
(https://github.com/pubsubhubbub
). La politique
d'enregistrement est délibérement ouverte, l'idée étant que si un
type est utilisé sur le Web, il faut vraiment qu'il soit indiqué
dans le registre, sauf s'il est gravement dangereux pour la santé
du Web.
Les extensions sont spécifiées dans la section 2.1.2. L'idée est
que, si on n'a pas envie de se fatiguer à enregistrer un type de
lien, et qu'on veut quand même créer un type
unique, n'ayant pas de risque de collision
avec le travail des autres, on peut simplement se servir d'un URI
(forcément unique) pour indiquer le type. Cette URI peut (mais ce
n'est pas obligé) mener à une page Web qui décrira le type en
question. Ainsi, on pourrait imaginer de réécrire le lien plus haut
en <atom:link
rel="http://www.bortzmeyer.org/reg/my-link-type"
href="http://www.bortzmeyer.org/expose-go.html"/>
(en
pratique, le format Atom ne permet pas actuellement de telles
valeurs pour l'attribut rel
.)
Après le cadre général de description des liens, notre RFC
introduit une syntaxe concrète pour le cas de l'en-tête
Link:
des requêtes HTTP. Les autres formats et
protocoles devront s'ajuster à ce cadre chacun de son côté. Pour
HTTP, la section 3 décrit l'en-tête Link:
. La
cible doit être un URI (et un éventuel IRI doit donc être
transformé en URI), le contexte (l'origine) est la ressource qui
avait été demandée en HTTP et le type est indiqué dans le paramètre
rel
. (Le paramètre rev
qui avait été utilisé dans des vieilles versions est officiellement
abandonné.) Plusieurs attributs sont possibles comme
hreflang
, type
(qui est
le type MIME, pas le type du lien) ou
title
(qui peut être noté
title*
s'il utilise les en-têtes étendus du
RFC 8187). Pour la plupart de ces attributs,
leur valeur est juste une indication, la vraie valeur sera obtenue
en accédant à la ressource cible. Ainsi,
hreflang
dans le lien ne remplace pas
Content-Language:
dans la réponse et
type
ne gagnera pas si un
Content-Type:
différent est indiqué dans la
réponse.
Voici des exemples d'en-têtes, tirés de la section 3.5 du RFC :
Link: <http://www.example.com/MyBook/chapter2> rel="previous"; title="Previous chapter"
Ici, cet en-tête, dans une réponse HTTP, indique que
http://www.example.com/MyBook/chapter2
est une
ressource liée à la ressource qu'on vient de récupérer et que ce lien
est de type previous
, donc précède la ressource
actuelle dans l'ordre de lecture. L'attribut
title
indique un titre relatif, alors que la
vraie ressource
http://www.example.com/MyBook/chapter2
aura
probablement un titre du genre « Chapitre 2 ». En application des
règles de la section 3.4, c'est ce dernier titre qui gagnera au
final.
Un seul en-tête Link:
peut indiquer plusieurs
liens, comme dans l'exemple suivant :
Link: </TheBook/chapter2>; rel="previous"; title*=UTF-8'de'letztes%20Kapitel, </TheBook/chapter4>; rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel
Ce dernier montre également les en-têtes complètement
internationalisés du RFC 8187, ici en
allemand (étiquette de langue de
).
Cet en-tête a été enregistré à l'IANA, en application du RFC 3864 dans le registre des en-têtes (section 4.1).
D'autre part, un registre des types de liens existe. La section 4.2 décrit en détail ce registre. Voici, à titre d'exemple, quelques-uns des valeurs qu'on peut y trouver :
blocked-by
indique l'entité qui a exigé
le blocage d'une page Web (avec le fameux code 451, voir RFC 7725),copyright
qui indique
le copyright du document (issu de la norme HTML),edit
qui indique l'URI à utiliser pour une
modification de ce document, comme le permet le protocole
APP (RFC 5023),first
, qui pointe vers le premier document
de la série (défini par ce RFC 8288, même s'il était déjà enregistré),hub
qui indique l'endroit où s'abonner pour
des notifications ultérieures, suivant le protocole
PubSubHubbub),latest-version
qui indique où trouver la
dernière version d'un document versionné (RFC 5829),licence
, qui associe un document à sa
licence d'utilisation (RFC 4946),nofollow
, qui indique qu'on ne recommande
pas la ressource vers laquelle on pointe, et qu'il ne faut donc pas
considérer ce lien comme une approbation,related
, qui indique un document qui a un
rapport avec celui-ci (créé pour Atom, dans le RFC 4287),replies
, qui indique les réponses faites à
ce document (pour mettre en œuvre le
threading, RFC 4685),Ce registre est peuplé par le mécanisme dit Spécification
Nécessaire (cf. RFC 8126), avec exigence
d'un examen par un expert (l'actuel expert est Mark Nottingham, auteur de
plusieurs RFC, dont celui-ci). Pour chaque type, il faudra indiquer le type (aussi nommé
relation, comme par exemple previous
plus haut), une
description et une référence à la norme qui le formalise. Les demandes
d'enregistrement sont reçues par link-relations@ietf.org
.
Attention, ce n'est pas parce qu'il y a un lien qu'il faut le suivre automatiquement. La section 5, sur la sécurité, met en garde contre la confiance accordée à un lien.
L'annexe A.1 contient une discussion de l'utilisation des liens avec
HTML 4, d'où le cadre actuel de définition des liens est issu. Le type
y est indiqué par l'attribut rel
. Un exemple
indiquant la licence en XHTML est donc :
<link rel="license" type="text/html" title="GFDL in HTML format" href="http://www.gnu.org/copyleft/fdl.html"/>
L'annexe A.2
discute, elle, de l'utilisation du cadre de définition des liens en
Atom, qui utilise l'élément
<atom:link>
avec les attributs
href
pour indiquer la cible et
rel
pour le type. Par exemple,
<atom:link rel="license" type="text/html"
title="GFDL in HTML format"
href="http://www.gnu.org/copyleft/fdl.html"/>
indiquera
la licence du flux Atom qui contient cet élément (et, oui, Atom et
XHTML ont quasiment la même syntaxe).
L'annexe C de notre RFC indique les changements depuis son prédécesseur, le RFC 5988. Rien de très crucial :
Ce nouveau RFC a fait l'objet de peu de discussions à l'IETF, mais il y en a eu beaucoup plus sur GitHub.
Notez qu'à Paris Web
2017, le t-shirt officiel
portait une allusion à ce RFC, avec le texte <link
rel="human" ... >
, encourageant les relations entre humains.
Première rédaction de cet article le 23 octobre 2017
L'ICANN a pris la bonne initiative de publier sous un format structuré (JSON) la liste des TLD qui dépendent d'elles. Cela permet de faire facilement des statistiques sur ces TLD.
Attention, il n'y a pas tous les TLD, seulement ceux qui dépendent de l'ICANN (qu'on nomme souvent gTLD mais qui ne sont pas tous « génériques »).
Le fichier est en JSON, ce qui permet des analyses faciles avec jq. (Une version précédente, moins complète, était en CSV.) Commençons par télécharger :
% wget https://www.icann.org/resources/registries/gtlds/v2/gtlds.json
Combien y a-t-il de ces gTLD ?
% jq '.gTLDs | length' gtlds.json 1260
Elle est loin, l'époque où on parlait de .cno
(
.com
,
.net
,
.org
) car il n'y avait
guère que ces trois-là ! Mais attention, 21 gTLD ont
renoncé :
% jq '.gTLDs | map(select(.contractTerminated)) | map(.gTLD)' gtlds.json [ "africamagic", "chloe", "doosan", "dstv", "dwg", "emerson", "flsmidth", "gotv", "iinet", "kyknet", "mnet", "mtpc", "multichoice", "mutuelle", "mzansimagic", "naspers", "orientexpress", "payu", "supersport", "theguardian", "xn--4gq48lf9j" ]
Certains ont même renoncé après avoir été délégués dans la racine :
% jq '.gTLDs | map(select(.delegationDate!=null and .contractTerminated)) | map(.gTLD)' gtlds.json [ "chloe", "doosan", "flsmidth", "iinet", "mtpc", "mutuelle", "orientexpress" ]
Et certains ont déjà été retirés de la racine mais n'apparaissent pas encore comme « contrat terminé » :
% jq '.gTLDs | map(select((.contractTerminated | not) and .removalDate!=null)) | map(.gTLD)' gtlds.json [ "mcd", "mcdonalds", "montblanc", "pamperedchef" ]
Il n'y a donc que 1 234 TLD délégués actuellement (jq
'.gTLDs | map(select(.delegationDate!=null and
(.contractTerminated | not) and (.removalDate==null))) | length' gtlds.json
).
Le fichier indique également le nom du registre. Par exemple, on peut avoir tous ceux de Google (46 en tout) :
% jq '.gTLDs | map(select(.registryOperator == "Charleston Road Registry Inc.")) | map(.gTLD)' gtlds.json [ "ads", "android", "app", "boo", "cal", "channel", "chrome", "dad", "day", "dclk", "dev", ...
À propos de Google, combien des gTLD sont des
« .corp
» (ou « .brand
»), des TLD non ouverts au public,
prévus pour une seule entreprise ?
% jq '.gTLDs | map(select(.specification13)) | length' gtlds.json 470
Un gros tiers, donc. Au fait, si vous vous demandez ce qu'est
cette mystérieure « spécification 13 », voyez cette
documentation et le
texte complet.
Notez qu'il n'y a pas (encore ?) de mécanisme pour identifier les
« .geo
», les TLD identifiant une zone
géographique, ni d'ailleurs les « .community
». (Les .corp
ont des règles
particulières, d'où leur marquage.)
Et les IDN ? Il n'y en a que 96 dont 94 délégués actuellement. Ce sont (attention, préparez vos polices) :
% jq '.gTLDs | map(select(.delegationDate!=null and (.contractTerminated | not) and (.removalDate==null) and (.uLabel!=null))) | map(.uLabel + " (" + .gTLD + ")") ' gtlds.json [ "कॉम (xn--11b4c3d)", "セール (xn--1ck2e1b)", "佛山 (xn--1qqw23a)", "慈善 (xn--30rr7y)", "集团 (xn--3bst00m)", "在线 (xn--3ds443g)", "大众汽车 (xn--3oq18vl8pn36a)", "点看 (xn--3pxu8k)", "คอม (xn--42c2d9a)", "八卦 (xn--45q11c)", "موقع (xn--4gbrim)", "公益 (xn--55qw42g)", "公司 (xn--55qx5d)", "香格里拉 (xn--5su34j936bgsg)", "网站 (xn--5tzm5g)", "移动 (xn--6frz82g)", "我爱你 (xn--6qq986b3xl)", "москва (xn--80adxhks)", "католик (xn--80aqecdr1a)", "онлайн (xn--80asehdb)", "сайт (xn--80aswg)", "联通 (xn--8y0a063a)", "קום (xn--9dbq2a)", "时尚 (xn--9et52u)", "微博 (xn--9krt00a)", "淡马锡 (xn--b4w605ferd)", "ファッション (xn--bck1b9a5dre4c)", "орг (xn--c1avg)", "नेट (xn--c2br7g)", "ストア (xn--cck2b3b)", "삼성 (xn--cg4bki)", "商标 (xn--czr694b)", "商店 (xn--czrs0t)", "商城 (xn--czru2d)", "дети (xn--d1acj3b)", "ポイント (xn--eckvdtc9d)", "新闻 (xn--efvy88h)", "工行 (xn--estv75g)", "家電 (xn--fct429k)", "كوم (xn--fhbei)", "中文网 (xn--fiq228c5hs)", "中信 (xn--fiq64b)", "娱乐 (xn--fjq720a)", "谷歌 (xn--flw351e)", "電訊盈科 (xn--fzys8d69uvgm)", "购物 (xn--g2xx48c)", "クラウド (xn--gckr3f0f)", "通販 (xn--gk3at1e)", "网店 (xn--hxt814e)", "संगठन (xn--i1b6b1a6a2e)", "餐厅 (xn--imr513n)", "网络 (xn--io0a7i)", "ком (xn--j1aef)", "诺基亚 (xn--jlq61u9w7b)", "食品 (xn--jvr189m)", "飞利浦 (xn--kcrx77d1x4a)", "手表 (xn--kpu716f)", "手机 (xn--kput3i)", "ارامكو (xn--mgba3a3ejt)", "العليان (xn--mgba7c0bbn0a)", "اتصالات (xn--mgbaakc7dvf)", "بازار (xn--mgbab2bd)", "موبايلي (xn--mgbb9fbpob)", "ابوظبي (xn--mgbca7dzdo)", "كاثوليك (xn--mgbi4ecexp)", "همراه (xn--mgbt3dhd)", "닷컴 (xn--mk1bu44c)", "政府 (xn--mxtq1m)", "شبكة (xn--ngbc5azd)", "بيتك (xn--ngbe9e0a)", "عرب (xn--ngbrx)", "机构 (xn--nqv7f)", "组织机构 (xn--nqv7fs00ema)", "健康 (xn--nyqy26a)", "рус (xn--p1acf)", "珠宝 (xn--pbt977c)", "大拿 (xn--pssy2u)", "みんな (xn--q9jyb4c)", "グーグル (xn--qcka1pmc)", "世界 (xn--rhqv96g)", "書籍 (xn--rovu88b)", "网址 (xn--ses554g)", "닷넷 (xn--t60b56a)", "コム (xn--tckwe)", "天主教 (xn--tiq49xqyj)", "游戏 (xn--unup4y)", "vermögensberater (xn--vermgensberater-ctb)", "vermögensberatung (xn--vermgensberatung-pwb)", "企业 (xn--vhquv)", "信息 (xn--vuq861b)", "嘉里大酒店 (xn--w4r85el8fhu5dnra)", "嘉里 (xn--w4rs40l)", "广东 (xn--xhq521b)", "政务 (xn--zfr164b)" ]
(Si vous ne savez pas ce qu'est un « U-label », voyez le RFC 5890.)
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : JM. Valin (Mozilla
Corporation), K. Vos (vocTone)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF codec
Première rédaction de cet article le 22 octobre 2017
Le codec Opus, normalisé dans le RFC 6716 a une particularité rare à l'IETF : il est spécifié par un programme, pas par une description écrite en langue naturelle. Les programmes ont des bogues et ce nouveau RFC corrige quelques petites bogues pas graves trouvées dans le RFC 6716.
C'est un vieux débat dans le monde de la normalisation : faut-il décrire un protocole ou un format par une description en langue naturelle (vecteur souvent ambigu) ou par une mise en œuvre dans un langage de programmation (qui fournira directement un programme utilisable), dans laquelle il sera difficile de distinguer ce qui est réellement obligatoire et ce qui n'est qu'un détail de cette mise en œuvre particulière ? Presque tout le temps, à l'IETF, c'est la voie de la description en anglais qui est choisie. Mais le RFC 6716, qui normalisait Opus, avait choisi une autre voie, celle du code source, écrit en C. C'est ce code qui est la loi.
Dans les deux cas, en anglais ou en C, les humains qui rédigent les normes font des erreurs. D'où ce RFC de correction, qui répare le RFC 6716 sur des points mineurs (la compatibilité est maintenue, il ne s'agit pas d'une nouvelle version d'Opus).
Chaque section du RFC est ensuite consacrée à une des erreurs. La section 3, par exemple, corrige un simple oubli dans le code de réinitialiser l'état du décodeur lors d'un changement. Le patch ne fait que deux lignes. Notez qu'il change le résultat produit par le décodeur, mais suffisamment peu pour que les vecteurs de test de l'annexe A.4 du RFC 6716 soient inchangés.
L'erreur en section 9 est également une erreur de logique dans la programmation. Sa correction, par contre, nécessite de changer les vecteurs de tests (cf. section 11).
Le reste des bogues, en revanche, consiste en erreurs de programmation
C banales. Ainsi, en section 4, il y a un
débordement d'entier si les données font
plus de 2^31-1 octets, pouvant mener à lire en dehors de la mémoire. En
théorie, cela peut planter le décodeur (mais, en pratique, le
code existant n'a pas planté.) Notez que cela ne peut pas arriver
si on utilise Opus dans RTP, dont les
limites seraient rencontrées avant qu'Opus ne reçoive ces données
anormales. Cette bogue peut quand même avoir des conséquences de
sécurité et c'est pour cela qu'elle a reçu un
CVE, CVE-2013-0899. Le
patch est très court. (Petit
rappel de C : la norme de ce
langage ne spécifie pas ce qu'il faut faire lorsqu'on
incrémente un entier qui a la taille maximale. Le résultat dépend
donc de l'implémentation. Pour un entier
signé, comme le type
int
, le comportement le plus courant est de
passer à des valeurs négatives.)
Notez qu'une autre bogue, celle de la section 7, a eu un CVE, CVE-2017-0381.
En section 5, c'est un problème de typage : un entier de 32 bits utilisé au lieu d'un de 16 bits, ce qui pouvait mener une copie de données à écraser partiellement les données originales.
Dans la section 6 du RFC, la bogue était encore une valeur trop grande pour un entier. Cette bogue a été découverte par fuzzing, une technique très efficace pour un programme traitant des données externes venues de sources qu'on ne contrôle pas !
Bref, pas de surprise : programmer en C est difficile, car de trop bas niveau, et de nombreux pièges guettent le programmeur.
La section 11 du RFC décrit les nouveaux vecteurs de test rendus nécessaires par les corrections à ces bogues. Ils sont téléchargeables.
Une version à jour du décodeur normatif figure désormais sur le site officiel. Le patch traitant les problèmes décrits dans ce RFC est en ligne. Ce patch global est de petite taille (244 lignes, moins de dix kilo-octets, ce qui ne veut pas dire que les bogues n'étaient pas sérieuses).
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : J. Klensin, J. Hakala (The National Library of Finland)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF urnbis
Première rédaction de cet article le 21 octobre 2017
Pour avoir un espace de noms d'URN, autrefois, il fallait un examen par l'IETF, ce qui était un peu contraignant (et risquait de mener au « squat », avec des gens utilisant un espace de noms qu'ils ne se sont pas donné la peine d'enregistrer). Le RFC 8141 ayant changé cette politique d'enregistrement pour celle, plus facile, d'examen par un expert, il a fallu clarifier quelques processus, ce que fait ce RFC. Dans la foulée, ce RFC change le statut de quelques RFC précédents, qui avaient servi de base à l'enregistrement de certains espaces de noms. Bref, que de la procédure et de la bureaucratie (mais pas inutile).
Un des buts des URN (désormais normalisés dans le RFC 8141) était d'intégrer des systèmes d'identificateurs existants. C'est ainsi que le RFC 2288 avait décrit un espace de noms pour les ISBN, mais sans l'enregistrer formellement à l'IANA, en partie parce que le mécanisme d'enregistrement n'était pas clair à l'époque. Par la suite, le RFC 3187 a remplacé le RFC 2288 et comblé ce manque (dans sa section 5). Idem pour les ISSN dans le RFC 3044. Ces deux RFC 3187 et RFC 3044 sont reclassés comme étant désormais d'intérêt historique uniquement (section 2 de notre RFC), les enregistrements de ces espaces de noms ayant été refaits selon les nouvelles règles (section 5). Voyez le nouvel enregistrement d'isbn et celui d'issn.
Pour ISBN, le principal changement est que l'espace de noms URN fait désormais référence à la nouvelle version de la norme, ISO 2108:2017 (cf. la page officielle et, comme toutes les normes du dinosaure ISO, elle n'est pas disponible en ligne.) Elle permet les ISBN de treize caractères (les ISBN avaient dix caractères au début). Pour ISSN, la norme du nouvel enregistrement est ISO 3297:2007, qui permet notamment à un ISSN de référencer tous les médias dans lesquels a été fait une publication (il fallait un ISSN par média, avant).
Et les NBN (National
Bibliography Number, section 3) ? C'est un cas rigolo,
car NBN ne désigne pas une norme spécifique, ni même une famille
d'identificateurs, mais regroupe tous les mécanismes
d'identificateurs utilisés par les bibliothèques nationales
partout dans le monde. En effet, les bibibliothèques peuvent
recevoir des documents qui n'ont pas d'autre identificateur. Si la
bibliothèque nationale de Finlande reçoit
un tel document, son identificateur national pourrait être,
mettons, fe19981001
, et, grâce à l'espace de
noms URN nbn
, il aura un URN,
urn:nbn:fi-fe19981001
.
Les NBN sont spécifiés dans le RFC 3188, pour lequel il n'y a pas encore de mise à jour prévue, contrairement aux ISBN et ISSN.
Outre les NBN, il y a bien d'autres schémas d'URN qui mériteraient une mise à jour, compte-tenu des changements du RFC 8141 (section 4). Mais il n'y a pas le feu, même si leur enregistrement actuel à l'IANA n'est pas tout à fait conforme au RFC 8141. On peut continuer à les utiliser.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : T. Lemon
(Nominum), R. Droms, W. Kumari
(Google)
Pour information
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 20 octobre 2017
Le RFC 6761 créait un
registre de « noms de
domaines d'usage spécial », où l'IETF
enregistrait des noms qui pouvaient nécessiter un traitement
spécial par une des entités de la chaîne d'avitaillement et de
résolution des noms de domaines. Un exemple est celui des noms de
domaine qui n'utilisaient pas le DNS, comme le
.onion
du RFC 7686. Certaines personnes ont émis des
réserves sur ce registre, par exemple parce qu'il marchait sur une
pelouse que l'ICANN considère comme sienne. Ce
nouveau RFC,
très controversé, fait la liste de tous les reproches qui ont été
faits au RFC 6761 et à son registre des noms
spéciaux.
La résolution de noms - partir d'un nom de domaine et obtenir des informations
comme l'adresse IP - est une des fonctions
cruciales de l'Internet. Il est donc normal
de la prendre très au sérieux. Et les noms ont une valeur pour les
utilisateurs (la vente record semble être celle de
business.com
). Souvent, mais pas
toujours, cette résolution se fait avec le
DNS. Mais, je l'ai dit, pas toujours : il
ne faut pas confondre les noms de domaines (une organisation
arborescente, une syntaxe, les composants séparés par des
points, par exemple
_443._tcp.www.bortzmeyer.org.
,
réussir-en.fr.
ou
sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion.
) et le DNS, un
protocole réseau particulier (section 2 du RFC, pour la
terminologie). La section 2 de ce RFC, suivant la définition du
RFC 8499, ne considère pas qu'un nom de
domaine utilise forcément le DNS. (Le problème n° 20, en section 3 revient sur
cette question.)
Le registre
des noms de domaines spéciaux a été créé en 2013 par le
RFC 6761. Contrairement à ce que croient
beaucoup de gens, il ne contient pas que des TLD (par exemple, il
a aussi a.e.f.ip6.arpa
ou bien
example.org
). Depuis sa création, plusieurs
noms ont été ajoutés, le plus spectaculaire ayant été le
.onion
en 2015, via le
RFC 7686. Beaucoup d'autres noms ont été
proposés, sans être explicitement rejetés mais sans être acceptés
non plus (voir les
Internet-Drafts
draft-grothoff-iesg-special-use-p2p-gns
et draft-chapin-additional-reserved-tlds
). Ces
enregistrements ne se sont pas très bien passés, avec beaucoup
d'engueulades, notamment pour les TLD.
En effet, la bagarre a toujours fait rage pour la gestion des noms dans la racine du DNS. Il y a cinq sortes de TLD :
.bo
,
.net
,
.guru
,
etc. Contrairement à ce que dit le RFC, ils ne sont pas
forcément délégués par l'ICANN
(.de
a été créé
bien avant l'ICANN, et
.su
résiste depuis
longtemps à toutes les tentatives de l'ICANN de le supprimer)..arpa
. Les domaines
du registre des noms spéciaux en font partie. Les fanas de
gouvernance noteront
qu'ils ne passent jamais par l'ICANN..eth
pour Ethereum ou
.bit
pour
Namecoin. Cela inclut aussi des noms
très utilisés localement comme .home
ou .corp
..macron
sera
utilisé par les fans du Président ?Cette classification est imparfaite, comme toutes les classifications. Par exemple, un nom peut être sur la liste noire de l'ICANN et être quand même utilisé par des organisations locales, ou via un logiciel particulier.
Ce RFC veut présenter une liste exhaustive des problèmes posés lors de l'enregistrement de ces noms spéciaux. Comme elle est exhaustive (« an unfiltered compilation of issues »), elle n'est pas consensuelle, loin de là ! Plusieurs des problèmes ne sont pas considérés comme tels par tout le monde, ce que le RFC note bien dans les sections 1 et 3.
Le gros du RFC est donc cette liste de problèmes, en section 3. Je ne vais pas tous les citer. Notez tout de suite que ce RFC décrit des problèmes, ou perçus comme tels par certains, pas des solutions. Il ne propose pas de réforme du RFC 6761. Notez aussi que les problèmes ne sont pas classés (leur ordre d'exposition, et le numéro qu'ils reçoivent, n'est pas un ordre d'importance.)
Premier problème cité, la coordination avec
l'ICANN. Les cinq catégories de noms de
TLD cités
plus haut coexistent dans le même espace de noms. S'il y a deux
.macron
, il y aura un problème (voir par
exemple le
rapport SAC 090, mais en se rappelant que c'est l'ICANN qui
s'auto-défend). L'IETF et
l'ICANN ont un mécanisme d'information
réciproque mais pas de processus bureaucratique formel de
coordination. Si l'IETF veut réserver .home
(RFC 7788), il n'y a officiellement pas de
mécanisme pour communiquer cette décision à l'ICANN et obtenir
d'elle la confirmation que ce nom n'est pas par ailleurs en cours
de réservation. (Cf. 4.1.4 qui revient sur ce mécanisme
- « liaison » en anglais.)
(Personnellement, cet argument me fait plutôt rire : beaucoup de gens sont actifs à la fois à l'IETF et à l'ICANN, les deux organisations opèrent de manière assez publique, surtout l'IETF, et la possibilité qu'elles réservent chacune un nom sans savoir ce qu'a fait l'autre est purement théorique.)
Le problème 2 est celui de la définition de « pour des raisons techniques », introduit dans le RFC 2860, et qui fonde le droit de l'IETF à réserver des TLD sans passer par la caisse de l'ICANN (« assignments of domain names for technical uses [...] are not considered to be policy issues, and shall remain subject to the provisions of this Section 4 »). Le problème est que ces raisons techniques ne sont définies nulle part et que personne ne sait trop ce que cela veut dire.
Les problèmes 3 et 4 sont qu'il n'y a pas de Directeur Chef de
l'Internet. Ni l'ICANN, ni l'IETF (ni évidemment
l'UIT) ne sont reconnus, ni en droit, ni en
fait, comme ayant une autorité quelconque (le raccourci
journalistique de présenter l'ICANN comme « le régulateur mondial
de l'Internet » est ridiculement faux). Imaginons qu'une
développeuse crée un nouveau logiciel qui utilise le TLD
.zigzag
pour son nommage, et diffuse le
logiciel en question : personne ne peut l'en empêcher, même si
l'ICANN souffre de la perte de revenus potentiels, même si des
grognons à l'IETF murmurent bruyamment que c'est du
squatting (voir aussi le début de la section 4). Et heureusement que c'est ainsi :
c'est le côté « innover sans autorisation »
(« permissionless innovation » qui a été si
essentiel pour le succès de l'Internet). Un autre exemple est bien sûr le
projet .42, aujourd'hui abandonné mais qui
illustrait ce côté décentralisé de l'Internet.
La définition du problème 5 note que les organisations (ou les individus) utilisent des TLD (ou des domaines quelconques, mais c'est surtout sur les TLD que se focalise la discussion) sans suivre les procédures. Elles ont plusieurs raisons pour agir ainsi :
.corp
et .home
sont des TLD locaux très
populaires et il y a des candidatures (actuellement gelées
de facto) pour ces TLD à
l'ICANNEnsuite, le problème 6 : il y a plusieurs protocoles de résolution de
nom, le DNS n'étant que le principal. En l'absence de
métadonnées indiquant le protocole à
utiliser (par exemple dans les URL),
se servir du TLD comme « aiguillage » est une solution
tentante (if tld == "onion" then use Tor elsif tld ==
"gnu" then use GnuNet else use DNS
…)
Le registre des noms de domaines spéciaux est essentiellement
en texte libre, sans grammaire formelle. Cela veut dire que le
code spécifique qui peut être nécessaire pour traiter tous ces
domaines spéciaux (un résolveur doit, par exemple, savoir que
.onion
n'utilise pas
le DNS et qu'il ne sert donc à rien de l'envoyer à la racine) doit
être fait à la main, il ne peut pas être automatiquement dérivé du
registre (problème 7). Résultat, quand un nouveau domaine est
ajouté à ce registre, il sera traité « normalement » par les
logiciels pas mis à jour, et pendant un temps assez long. Par
exemple, les requêtes seront envoyées à la racine, ce qui pose des
problèmes de vie privée (cf. RFC 7626).
Comme noté plus haut, certains candidats à un nom de domaine
spécial sont inquiets du temps que prendra l'examen de leur
candidature, et du risque de rejet (problème 8). Ils n'ont pas
tort. L'enregistrement du
.local
(RFC 6762) a pris dix ans, et les efforts de Christian Grothoff
pour enregistrer .gnu
se sont enlisés dans
d'épaisses couches de bureaucratie. Ce n'est peut-être pas un
hasard si Apple a fini par avoir son
.local
(non sans mal) alors que des projets
de logiciel libre
comme GNUnet se sont vu fermer la porte au
nez.
C'est que l'IETF n'est pas toujours facile et qu'un certain nombre de participants à cette organisation ont souvent une attitude de blocage face à tout intervenant extérieur (problème 9). Ils n'ont pas tort non plus de se méfier des systèmes non-DNS, leurs raisons sont variées :
.zkey
? »). De plus, un certain nombre de
participants à l'IETF estiment qu'il ne faut de toute façon
qu'un seul protocole de résolution, afin de limiter la
complexité..onion
, cela peut durer
longtemps et coûter cher. L'ICANN court le même risque mais,
elle, elle a des avocats en quantité, et de l'argent pour les payer.Le problème 11 est davantage lié au RFC 6761 lui-même. Ce RFC a parfois été compris de travers,
comme pour l'enregistrement de ipv4only.arpa
(RFC 7050, corrigé ensuite dans le RFC 8880) et surtout celui de
.home
(RFC 7788, qui
citait le RFC 6761 mais n'avait tenu aucune
de ses obligations). Voir aussi le problème 16.
Les problèmes 12 et 13 concernent les TLD existants (au sens où ils sont utilisés) mais pas enregistrés officiellement (voir par exemple le rapport de l'ICANN sur les « collisions »). Cela serait bien de documenter quelque part cette utilisation, de façon à, par exemple, être sûrs qu'ils ne soient pas délégués par l'ICANN. Mais cela n'a pas marché jusqu'à présent.
Le problème 14 concerne le fait que des noms de domaine spéciaux sont parfois désignés comme spéciaux par leur écriture dans le registre, mais parfois simplement par leur délégation dans le DNS.
Le problème 15 est celui de la différence entre enregistrer un
usage et l'approuver. Avec la règle « géré par l'IETF pour un
usage technique » du RFC 2860, il n'y a pas
moyen d'enregistrer un nom sans une forme d'« approbation ». (Pas
mal d'articles sur l'enregistrement de .onion
avaient ainsi dit, à tort, que « l'IETF approuvait officiellement
Tor ».)
Le problème 17 est un bon exemple du fait que la liste
de Prévert qu'est ce RFC est en effet « non filtrée » et que
toute remarque soulevée y a été mise, quels que soient ses
mérites. Il consiste à dire que l'utilisation du registre des noms
spéciaux est incohérente, car les enregistrements donnent des
règles différentes selon le nom. Cela n'a rien d'incohérent,
c'était prévu dès le départ par le RFC 6761 (section 5) :
il n'y a pas de raison de traiter de la même façon
.onion
(qui n'utilise pas le DNS du tout) et
.bit
(passerelle entre le DNS et
Namecoin).
Le problème 19 découle du fait que les noms de domaine ne sont
pas de purs identificateurs techniques comme le sont, par exemple,
les adresses MAC. Ils
ont un sens pour les utilisateurs. Bien que, techniquement
parlant, les développeurs de Tor auraient
pu choisir le nom .04aab3642f5
ou onion.torproject.org
comme suffixe pour les services en oignon,
ils ont préféré un nom d'un seul composant, et compréhensible,
.onion
. Ce désir
est bien compréhensible (une
proposition à l'IETF est de reléguer tous les noms spéciaux sous
un futur TLD .alt
, qui ne connaitra
probablement aucun succès même s'il est créé un jour). Mais il
entraine une pression accrue sur la racine des noms de domaine :
si deux projets réservent .zigzag
, lequel des
deux usages faut-il enregistrer ?
Enfin, le dernier problème, le 21, est davantage technique : il
concerne DNSSEC. Si un TLD est enregistré
comme domaine spécial, faut-il l'ajouter dans la racine du DNS et,
si oui, faut-il que cette délégation soit signée ou pas ? S'il n'y
a pas de délégation, le TLD sera considéré comme invalide par les
résolveurs validants. Par exemple, si je fais une requête pour
quelquechose.zigzag
, la racine du DNS va
répondre :
% dig @k.root-servers.net A quelquechose.zigzag ; <<>> DiG 9.10.3-P4-Debian <<>> @k.root-servers.net A quelquechose.zigzag ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 7785 ;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;quelquechose.zigzag. IN A ;; AUTHORITY SECTION: zero. 86400 IN NSEC zip. NS DS RRSIG NSEC zero. 86400 IN RRSIG NSEC 8 1 86400 ( 20171017050000 20171004040000 46809 . wyKfrNEygGCbDscCu6uV/DFofs5DKYiV+jJd2s4xkkAT ... . 86400 IN NSEC aaa. NS SOA RRSIG NSEC DNSKEY . 86400 IN RRSIG NSEC 8 0 86400 ( 20171017050000 20171004040000 46809 . kgvHoclQNwmDKfgy4b96IgoOkdkyRWyXYwohW+mpfG+R ... . 86400 IN SOA a.root-servers.net. nstld.verisign-grs.com. ( 2017100400 ; serial 1800 ; refresh (30 minutes) 900 ; retry (15 minutes) 604800 ; expire (1 week) 86400 ; minimum (1 day) ) . 86400 IN RRSIG SOA 8 0 86400 ( 20171017050000 20171004040000 46809 . GnTMS7cx+XB+EmbMFWt+yEAg29w17HJfUaOqvPsTn0eJ ... ;; Query time: 44 msec ;; SERVER: 2001:7fd::1#53(2001:7fd::1) ;; WHEN: Wed Oct 04 12:58:12 CEST 2017 ;; MSG SIZE rcvd: 1036
Et l'enregistrement NSEC prouvera qu'il n'y a
rien entre .zero
et
.zip
, amenant le résolveur validant à
considérer que .zigzag
ne peut pas
exister. Si le nom devait être traité par le DNS (et, par exemple,
résolu localement comme ceux du RFC 6303,
c'est une réponse correcte : la requête n'aurait pas dû aller à la
racine). Dans d'autres cas, cela peut être gênant. De toute façon,
le débat est assez théorique : l'IETF n'a aucun pouvoir sur la
racine du DNS, et aucun moyen d'y ajouter un nom.
Après cet examen des possibles et potentiels problèmes, la section 4 du RFC examine les pratiques existantes. Plusieurs documents plus ou moins officiels examinent déjà ces questions. Mais je vous préviens tout de suite : aucun ne répond complètement au·x problème·s.
Commençons par le RFC 2826 sur la
nécessité d'une racine unique. C'est un document
IAB et qui n'engage donc pas l'IETF, même
s'il est souvent cité comme texte sacré. Sa thèse principale est
qu'il faut une racine unique, et que .zigzag
,
.pm
ou
.example
doivent donc avoir la même signification partout, sous peine de
confusion importante chez l'utilisateur. Cela n'interdit pas des
noms à usage local à condition que cela reste bien local. Comme
les utilisateurs ne vont pas faire la différence, ces noms locaux
vont forcément fuiter tôt ou tard. (Par exemple, un utilisateur
qui regarde http://something.corp/
ne va pas
réaliser que ce nom ne marche qu'en utilisant les résolveurs de
l'entreprise, et va essayer d'y accéder depuis chez lui. Un autre
exemple serait celui d'un utilisateur qui essaierait de taper
ping sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion
depuis la ligne
de commande, sans se rendre compte que ping
ne connait pas Tor.)
Bref, le RFC 2826 dit clairement qu'une racine unique est nécessaire, et que les noms locaux sont casse-gueule.
Le second RFC à lire est évidemment le RFC 6761, qui a créé le registre des noms de domaine spéciaux. Lui, c'est un document IETF sur le Chemin des Normes. Quelques points qui sont souvent oubliés par les lecteurs du RFC 6761 méritent d'être répétés :
in-addr.arpa
,
par exemple, et rappelez-vous bien à son sujet que les noms de
domaine spéciaux ne sont pas forcément des TLD).10.in-addr.arpa
(pour les adresses IP
du RFC 1918), qui doit être traité par le
résolveur, sans jamais interroger la racine (cf. RFC 6303)..onion
(qui doit
utiliser Tor) ou de
.local
(qui doit utiliser mDNS, RFC 6762).Notez qu'à l'heure actuelle, tous les noms enregistrés comme noms de domaine spéciaux sont des TLD ou bien sont résolus avec le DNS.
Troisième RFC à lire absolument avant de dire des bêtises sur les noms de domaine spéciaux, le RFC 2860, qui fixe le cadre des relations compliquées entre l'IETF et l'ICANN. En gros, la règle par défaut est que l'ajout (ou le retrait) de TLD dans la racine est une prérogative de l'ICANN sauf les « noms de domaine à usage technique » (notion non définie…) où l'IETF décide. Notez que ce document concerne uniquement les relations entre l'IETF et l'ICANN. Si le W3C, ou la développeuse du logiciel ZigZag, veut créer un TLD, que se passe-t-il ? Ce point n'est pas traité dans le RFC 2860. Certains exégètes estiment que cela veut dire que ces tiers sont implicitement exclus.
Il y a aussi d'autres documents mais moins cruciaux. Le RFC 6762 qui normalise mDNS est celui qui a
réservé .local
et c'est donc un
exemple d'un enregistrement réussi (mais qui fut laborieux, plus
de douze années de développement furent nécessaires, cf. l'annexe
H du RFC 6762).
Autre exemple réussi, le RFC 7686 sur le
.onion
. .onion
était
utilisé depuis longtemps quand le RFC 6761 a
créé le registre des noms de domaine spéciaux. L'enregistrement
a
posteriori a réussi, malgré de
vigoureuses oppositions mais il faut noter que le consensus
approximatif de l'IETF a été facilité par une décision du
CA/B Forum de ne plus allouer
de certificats pour des TLD internes.
Encore un autre RFC à lire, le RFC 6303,
qui décrit les noms qui devraient idéalement être résolus
localement, c'est-à-dire par le résolveur de l'utilisateur, sans
demander aux serveurs faisant autorité. C'est par exemple le cas
des in-addr.arpa
correspondant aux adresses IPv4 privées du RFC 1918. Il ne sert à rien de demander à la racine
l'enregistrement PTR de
3.2.1.10.in-addr.arpa
: ces adresses IP étant
purement locales, il ne peut pas y avoir de réponse intelligente
de la racine. Les noms en 10.in-addr.arpa
doivent donc être résolus localement, et sont donc, eux aussi, des
« noms de domaine spéciaux ». Par contre, contrairement à
.local
ou à .onion
, ils
sont résolus par le DNS.
Pas fatigué·e·s ? Encore envie de lire ? Il y aussi l'étude
d'Interisle sur les « collisions ». Derrière ce nom
sensationnaliste conçu pour faire peur, il y a un vrai problème,
le risque qu'un TLD récent masque, ou soit masqué par, un TLD
alloué localement sans réfléchir (comme .dev
). L'étude
montrait par exemple que .home
était celui
posant le plus de risques.
Sur un sujet proche, il y a aussi une étude du SSAC, un comité ICANN.
On a dit plus haut que les noms de domaine « spéciaux »
n'étaient pas forcément des TLD. C'est par exemple le cas d'un nom
utilisé pour certaines manipulations IPv6,
ipv4only.arpa
, créé par le RFC 7050, mais qui, par suite d'un cafouillage dans le
processus, n'avait pas été ajouté immédiatement au registre des noms de domaine
spéciaux. Dommage : ce nom, n'étant pas un TLD et n'ayant pas de
valeur particulière, n'avait pas posé de problème et avait été
accepté rapidement.
Enfin, un dernier échec qu'il peut être utile de regarder, est
la tentative d'enregistrer comme noms de domaine spéciaux des TLD
très souvent alloués localement, et qu'il serait prudent de ne pas
déléguer dans la racine, comme le .home
cité
plus haut. Un projet
avait été rédigé en ce sens, mais n'avait jamais abouti,
enlisé dans les sables procéduraux.
Si vous n'avez pas mal à la tête à ce stade, vous pouvez encore
lire la section 5, qui rappelle l'histoire tourmentée de ce
concept de noms de domaine spéciaux. Lorsque le DNS a été produit
(RFC 882 et RFC 883)
pour remplacer l'ancien système HOSTS.TXT
(RFC 608), la transition ne s'est pas faite sans
douleur, car plusieurs systèmes de résolution coexistaient (le
plus sérieux étant sans doute les Yellow
Pages sur Unix, mais il y avait aussi
NetBIOS name
service/WINS, qui ne tournait pas que sur
Windows). Encore aujourd'hui, des anciens
systèmes de résolution fonctionnent toujours. Le
HOSTS.TXT
survit sous la forme du
/etc/hosts
d'Unix (et de son équivalent
Windows). Les systèmes d'exploitation ont en général
un « commutateur » qui permet d'indiquer quel mécanisme de
résolution utiliser pour quel nom. Voici un exemple d'un
/etc/nsswitch.conf
sur une machine
Debian qui, pour résoudre un nom de
domaine, va utiliser successivement
/etc/hosts
, LDAP, puis
le DNS :
hosts: files ldap dns
Le concept de TLD privé, connu uniquement en local, a été (bien à tort) recommandé par certaines entreprises comme Sun ou Microsoft. Il a survécu à la disparition des technologies qui l'utilisaient, comme Yellow Pages. Aujourd'hui, c'est une source d'ennuis sans fin, et bien des administrateurs réseau ont maudit leur prédécesseur pour avoir configuré ainsi tout le réseau local, allumant ainsi une bombe qui allait exploser quand le TLD « privé » s'est retrouvé délégué.
La discussion à l'IETF, notamment dans son groupe de travail
DNSOP a été très chaude. Un premier document avait été
élaboré, draft-adpkja-dnsop-special-names-problem
,
puis l'ancêtre de ce RFC avait été écrit, le premier document
étant abandonné (il était très proche du point de vue de
l'ICANN comme quoi seule l'ICANN devrait
pouvoir créer des TLD, les autres acteurs n'étant que de vilains
squatteurs).
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : S. Bensley, D. Thaler, P. Balasubramanian (Microsoft), L. Eggert (NetApp), G. Judd (Morgan Stanley)
Pour information
Réalisé dans le cadre du groupe de travail IETF tcpm
Première rédaction de cet article le 19 octobre 2017
DCTCP (Datacenter TCP), décrit dans ce RFC (qui n'est pas une norme, attention), est une variante de TCP avec un contrôle de congestion moins prudent. Elle est conçue pour le cas particulier des centres de données et ne doit pas être utilisée sur l'Internet public.
DCTCP repose sur la technique ECN du RFC 3168. Normalement, cette technique permet de signaler la congestion plus tôt qu'avec la méthode traditionnelle d'attendre les pertes de paquets. L'ECN est binaire : il y a eu de la congestion ou il n'y en a pas eu. DCTCP va plus loin et utilise ECN pour estimer le nombre d'octets qui ont rencontré de la congestion. DCTCP va ensuite refermer sa fenêtre (le nombre d'octets qu'on peut envoyer avant d'avoir reçu des accusés de réception) plus lentement que le TCP classique (et c'est pour cela que la concurrence entre eux est inégale, et que DCTCP ne doit pas être activé dans l'Internet, mais seulement dans des environnements fermés).
Quelles sont les particularités du réseau dans un centre de données ? Il faut beaucoup de commutateurs pour relier ces machines. Il est tentant d'utiliser pour cela des commutateurs bon marché. Mais ceux-ci ont des tampons de taille limitée, et le risque de congestion est donc plus élevé. Dans un centre de données, les flots sont de deux types : des courts et des longs. Les courts veulent en général une faible latence et les longs sont souvent davantage intéressés par une forte capacité. Enfin, le trafic est souvent très synchronisé. Si on fait du MapReduce, tous les serveurs vont voir de l'activité réseau en même temps (quand le travail est réparti, et quand il se termine).
Le cahier des charges des commutateurs est donc plein de contradictions :
Avec le TCP traditionnel (pré-ECN), l'indicateur de congestion est la perte de paquets, détectée par l'absence d'accusé de réception. (Voir le RFC 5681 pour une bonne synthèse sur le contrôle de congestion dans TCP.) Attendre la perte de paquets pour ralentir n'est pas très efficace : pour un flot court qui rencontre de la congestion au début, la majorité des paquets aura été jetée avant que TCP ne puisse ralentir. D'où l'invention d'ECN (RFC 3168). ECN permet de réagir avant qu'on perde des paquets. Mais, comme expliqué plus haut, il est binaire : il détecte la congestion, pas son importance. Il va donc souvent mener TCP à refermer trop énergiquement la fenêtre d'envoi.
La section 3 du RFC présente les algorithmes à utiliser. Les
commutateurs/routeurs
détectent la congestion et la signalent via
ECN (RFC 3168). Les
récepteurs des données renvoient l'ECN à l'émetteur et celui-ci
réduit sa fenêtre de congestion (cwnd
pour
Congestion WiNDow, cf. RFC 5681, section 2). Tout ceci est
le fonctionnement classique d'ECN. C'est surtout dans la dernière étape,
le calcul de la réduction de la fenêtre, que DCTCP apporte des
nouveautés. Mais, avant, quelques détails sur les deux premières
étapes.
D'abord, la décision des commutateurs et·ou routeurs de considérer qu'il y a congestion. Fondamentalement, c'est une décision locale, qui n'est pas standardisée. En général, on décide qu'il y a congestion dès que le temps de séjour des paquets dans les tampons du commutateur/routeur augmente « trop ». On n'attend donc pas que les files d'attente soient pleines (si elles sont grandes - bufferbloat - la latence va augmenter sérieusement bien avant qu'elles ne soient pleines). Une fois que l'engin décide qu'il y a congestion, il marque les paquets avec ECN (bit CE - Congestion Experienced, cf. RFC 3168).
Le récepteur du paquet va alors se dire « ouh là, ce paquet a rencontré de la congestion sur son trajet, il faut que je prévienne l'émetteur de se calmer » et il va mettre le bit ECE dans ses accusés de réception. Ça, c'est l'ECN normal. Mais pour DCTCP, il faut davantage de détails, puisqu'on veut savoir précisément quels octets ont rencontré de la congestion. Une possibilité serait d'envoyer un accusé de réception à chaque segment (paquet TCP), avec le bit ECE si ce segment a rencontré de la congestion. Mais cela empêcherait d'utiliser des optimisations très utiles de TCP, comme les accusés de réception retardés (on attend un peu de voir si un autre segment arrive, pour pouvoir accuser réception des deux avec un seul paquet). À la place, DCTCP utilise une nouvelle variable booléenne locale chez le récepteur qui stocke l'état CE du précédent segment. On envoie un accusé de réception dès que cette variable change d'état. Ainsi, l'accusé de réception des octets M à N indique, selon qu'il a le bit ECE ou pas, que tous ces octets ont eu ou n'ont pas eu de congestion.
Et chez l'émetteur qui reçoit ces nouvelles notifications de
congestion plus subtiles ? Il va s'en servir pour déterminer quel
pourcentage des octets qu'il a envoyé ont rencontré de la
congestion. Les détails du calcul (dont une partie est laissé à
l'implémenteur, cf. section 4.2) figurent en section 3.3. Le résultat est stocké
dans une nouvelle variable locale,
DCTCP.Alpha
.
Une fois ces calculs faits et cette variable disponible,
lorsque la congestion apparait, au lieu de diviser brusquement sa
fenêtre de congestion, l'émetteur la fermera plus doucement, par
la formule cwnd = cwnd * (1 - DCTCP.Alpha /
2)
(où cwnd
est la taille de la
fenêtre de congestion ; avec l'ancien algorithme, tout se passait
comme si tous les octets avaient subi la congestion, donc
DCTCP.Alpha
= 1).
La formule ci-dessus était pour la cas où la congestion était signalée par ECN. Si elle était signalée par une perte de paquets, DCTCP se conduit comme le TCP traditionnel, divisant sa fenêtre par deux. De même, une fois la congestion passée, Datacenter TCP agrandit sa fenêtre exactement comme un TCP normal.
Voilà, l'algorithme est là, il n'y a plus qu'à le mettre en œuvre. Cela mène à quelques points subtils, que traite la section 4. Par exemple, on a dit que DCTCP, plus agressif qu'un TCP habituel, ne doit pas rentrer en concurrence avec lui (car il gagnerait toujours). Une implémentation de DCTCP doit donc savoir quand activer le nouvel algorithme et quand garder le comportement conservateur traditionnel. (Cela ne peut pas être automatique, puisque TCP ne fournit pas de moyen de négocier l'algorithme de gestion de la congestion avec son pair.) On pense à une variable globale (configurée avec sysctl sur Unix) mais cela ne suffit pas : la même machine dans le centre de données peut avoir besoin de communiquer avec d'autres machines du centre, en utilisant DCTCP, et avec l'extérieur, où il ne faut pas l'utiliser. Il faut donc utiliser des configurations du genre « DCTCP activé pour les machines dans le même /48 que moi ».
Une solution plus rigolote mais un peu risquée, serait d'activer DCTCP dès que la mesure du RTT indique une valeur inférieure à N millisecondes, où N est assez bas pour qu'on soit sûr que seules les machines de la même tribu soient concernées.
Après le programmeur en section 4, l'administrateur réseaux en section 5. Comment déployer proprement DCTCP ? Comme on a vu que les flots TCP traditionnels et DCTCP coexistaient mal, la section 5 recommande de les séparer. Par exemple, l'article « Attaining the Promise and Avoiding the Pitfalls of TCP in the Datacenter » décrit un déploiement où le DSCP (RFC 2474) d'IPv4 est utilisé pour distinguer les deux TCP, ce qui permet d'appliquer de l'AQM (RFC 7567) à DCTCP et des méthodes plus traditionnelles (laisser tomber le dernier paquet en cas de congestion) au TCP habituel. (Il faut aussi penser au trafic non-TCP, ICMP, par exemple, quand on configure ses commutateurs/routeurs.)
Aujourd'hui, DCTCP est déjà largement déployé et ce RFC ne fait que prendre acte de ce déploiement On trouve DCTCP dans Linux (cf. ce commit de 2014, notez les mesures de performance qui accompagnent sa description), dans FreeBSD (ce commit, et cette description de l'implémentation), et sur Windows (cette fois, on ne peut pas voir le source mais il y a une documentation). Sur Linux, on peut voir la liste des algorithmes de gestion de la congestion qui ont été compilés dans ce noyau :
% sysctl net.ipv4.tcp_available_congestion_control net.ipv4.tcp_available_congestion_control = cubic reno
Si DCTCP manque, c'est peut-être parce qu'il faut charger le module :
% modprobe tcp_dctcp % sysctl net.ipv4.tcp_available_congestion_control net.ipv4.tcp_available_congestion_control = cubic reno dctcp
Si DCTCP se trouve dans la liste, on peut l'activer (c'est une activation globale, par défaut) :
% sysctl -w net.ipv4.tcp_congestion_control=dctcp
Pour le faire uniquement vers certaines destinations (par exemple à l'intérieur du centre de données) :
% ip route add 192.168.0.0/16 congctl dctcp
Le choix des algorithmes de gestion de la congestion peut
également être fait par chaque application (setsockopt(ns, IPPROTO_TCP, TCP_CONGESTION, …
).
Enfin, la section 6 du RFC rassemble quelques problèmes non résolus avec DCTCP :
Enfin, la section 7 du RFC, portant sur la sécurité, note que DCTCP hérite des faiblesses de sécurité d'ECN (les bits ECN dans les en-têtes IP et TCP peuvent être modifiés par un attaquant actif) mais que c'est moins grave pour DCTCP, qui ne tourne que dans des environnements fermés.
Si vous aimez lire, l'article original décrivant DCTCP en 2010 est celui de Alizadeh, M., Greenberg, A., Maltz, D., Padhye, J., Patel, P., Prabhakar, B., Sengupta, S., et M. Sridharan, « Data Center TCP (DCTCP) ». Le dinosaure ACM ne le rendant pas disponible librement, il faut le récupérer sur Sci-Hub (encore merci aux créateurs de ce service).
Merci à djanos pour ses nombreuses corrections sur la gestion de DCTCP dans Linux.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Wouters (Red
Hat), D. Migault, J. Mattsson
(Ericsson), Y. Nir (Check
Point), T. Kivinen
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF ipsecme
Première rédaction de cet article le 18 octobre 2017
Le protocole de cryptographie IPsec vient avec une liste d'obligations concernant les algorithmes cryptographiques qu'il faut inclure. Cette liste figure dans ce RFC 8221 (qui remplace l'ancien RFC 7321, avec beaucoup de changements). Ainsi, les différentes mises en œuvre d'IPsec sont sûres d'avoir un jeu d'algorithmes corrects en commun, assurant ainsi l'interopérabilité. Cette nouvelle version marque notamment l'officialisation de l'algorithme ChaCha20.
Ce nouveau RFC concerne les deux services d'IPsec, ESP (Encapsulating Security Payload, RFC 4303) et AH (Authentication Header, RFC 4302). Les RFC normatifs sur IPsec se veulent stables, alors que la cryptographie évolue. D'où le choix de mettre les algorithmes dans un RFC à part. Par exemple, la section 3.2 du RFC 4303 note « The mandatory-to-implement algorithms for use with ESP are described in a separate RFC, to facilitate updating the algorithm requirements independently from the protocol per se » (c'était à l'époque le RFC 4305, remplacé depuis par le RFC 4835, puis par le RFC 7321, puis par notre RFC 8221, trois ans après son prédécesseur).
Ce RFC « extérieur » à IPsec spécifie les algorithmes obligatoires, ceux sur lesquels on peut toujours compter que le pair IPsec les comprenne, ceux qui ne sont pas encore obligatoires mais qu'il vaut mieux mettre en œuvre car ils vont sans doute le devenir dans le futur, et ceux qui sont au contraire déconseillés, en général suite aux progrès de la cryptanalyse, qui nécessitent de réviser régulièrement ce RFC (voir section 1.1). Cette subtilité (différence entre « obligatoire aujourd'hui » et « sans doute obligatoire demain ») mène à une légère adaptation des termes officiels du RFC 2119 : MUST- (avec le signe moins à la fin) est utilisé pour un algorithme obligatoire aujourd'hui mais qui ne le sera sans doute plus demain, en raison des avancées cryptanalytiques, et SHOULD+ est pour un algorithme qui n'est pas obligatoire maintenant mais le deviendra sans doute.
Les section 5 et 6 donnent la liste des algorithmes. Je ne les répète pas intégralement ici, d'autant plus que tous les algorithmes ne sont pas cités, seulement ceux qu'il faut mettre en œuvre, car ils sont fiables, et ceux qu'il ne faut pas mettre en œuvre, car trop cassés, et qui doivent donc être documentés pour mettre les programmeurs en garde. Parmi les points à noter :
NULL
,
c'est-à-dire l'absence de chiffrement (RFC 2410 ; on
peut utiliser ESP pour l'authentification seule, d'où cet
algorithme). Il y a aussi plusieurs algorithmes notés MUST
NOT, comme DES-CBC
(RFC 2405). Ils ne doivent pas être programmés, afin
d'être sûr qu'on ne s'en serve pas.La section 4 donne des conseils sur l'utilisation d'ESP et
AH. AH ne fournit que l'authentification, alors qu'ESP peut fournir
également le chiffrement. Bien sûr, le chiffrement sans
l'authentification ne sert pas à grand'chose, puisqu'on risque
alors de parler à l'homme du milieu sans le savoir (voir l'article
de Bellovin, S. « Problem areas for the IP security
protocols » dans les Proceedings of the Sixth
Usenix Unix Security Symposium en 1996). Certaines
combinaisons d'algorithmes ne sont pas sûres, par exemple,
évidemment, ESP avec les algorithmes de chiffrement et
d'authentification tous les deux à NULL
(voir
par exemple l'article de Paterson, K. et J. Degabriele,
« On the (in)security of IPsec in MAC-then-encrypt
configurations » à l'ACM Conference on Computer
and Communications Security en 2010). Si on veut de
l'authentification/intégrité sans chiffrement, le RFC recommande
d'utiliser ESP avec le chiffrement NULL
,
plutôt que AH. En fait, AH est rarement utile, puisque ESP en est
presque un sur-ensemble, et il y a même eu des propositions de
le supprimer. AH avait été prévu pour une époque où le
chiffrement était interdit d'utilisation ou d'exportation dans
certains pays et un logiciel n'ayant que AH posait donc moins de
problèmes légaux. Aujourd'hui, la seule raison d'utiliser encore AH
est si on veut protéger certains champs de l'en-tête IP, qu'ESP ne défend pas.
Le chiffrement intègre/authentifié d'un algorithme comme AES-GCM (RFC 5116 et RFC 4106) est la solution recommandée dans la plupart des cas. L'intégration du chiffrement et de la vérification d'intégrité est probablement la meilleure façon d'obtenir une forte sécurité.
Triple DES et DES, eux, ont des défauts connus et ne doivent plus être utilisés. Triple DES a une taille de bloc trop faible et, au-delà d'un gigaoctet de données chiffrées avec la même clé, il laisse fuiter des informations à un écoutant, qui peuvent l'aider dans son travail de décryptage. Comme, en prime, il est plus lent qu'AES, il n'y a vraiment aucune raison de l'utiliser. (DES est encore pire, avec sa clé bien trop courte. Il a été cassé avec du matériel dont les plans sont publics.)
Triple DES étant sorti, la « solution de remplacement », si un gros problème est découvert dans AES, sera ChaCha20. Il n'y a aucune indication qu'une telle vulnérabilité existe mais il faut toujours garder un algorithme d'avance.
Pour l'authentification/intégrité, MD5, ayant des vulnérabilités connues (RFC 6151), question résistance aux collisions, est relégué en MUST NOT. (Notez que des vulnérabilités à des collisions ne gênent pas forcément l'utilisation dans HMAC.) SHA-1 a des vulnérabilités analogues mais qui ne concernent pas non plus son utilisation dans HMAC-SHA1, qui est donc toujours en MUST- (le moins indiquant son futur retrait). Les membres de la famille SHA-2 n'ont pas ces défauts, et sont désormais MUST pour SHA-256 et SHOULD pour SHA-512.
Voilà, c'est fini, la section 10 sur la sécurité rappelle juste quelques règles bien connues, notamment que la sécurité d'un système cryptographique dépend certes des algorithmes utilisés mais aussi de la qualité des clés, et de tout l'environnement (logiciels, humains).
Ce RFC se conclut en rappelant que, de même qu'il a remplacé ses prédécesseurs comme le RFC 7321, il sera probablement à son tour remplacé par d'autres RFC, au fur et à mesure des progrès de la recherche en cryptographie.
Si vous voulez comparer avec un autre document sur les algorithmes cryptographiques à choisir, vous pouvez par exemple regarder l'annexe B1 du RGS, disponible en ligne.
Merci à Florian Maury pour sa relecture acharnée. Naturellement, comme c'est moi qui tiens le clavier, les erreurs et horreurs restantes viennent de ma seule décision. De toute façon, vous n'alliez pas vous lancer dans la programmation IPsec sur la base de ce seul article, non ?
La section 8 du RFC résume les changements depuis le RFC 7321. Le RFC a été sérieusement réorganisé, avec une seule section regroupant les algorithmes avec AEAD et sans (j'ai gardé ici la présentation séparée de mon précédent article). Les sections d'analyse et de discussion ont donc été très modifiées. Par exemple, le RFC 7321 s'inquiétait de l'absence d'alternative à AES, problème qui a été résolu par ChaCha20. Parmi les principaux changements d'algorithmes :
En pratique, ce RFC est déjà largement mis en œuvre, la plupart des algorithmes cités sont présents (quand ils sont MUST ou SHOULD) ou absents (quand ils sont MUST NOT) dans les implémentations d'IPsec. Une exception est peut-être ChaCha20, qui est loin d'être présent partout. Malgré cela, ce RFC n'a pas suscité de controverses particulières, l'IETF avait un consensus sur ces nouveaux choix.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : N. Tomkinson, N. Borenstein (Mimecast)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF slim
Première rédaction de cet article le 10 octobre 2017
Dernière mise à jour le 20 mars 2018
La norme MIME permet d'étiqueter un
message en
indiquant la langue, avec l'en-tête
Content-Language:
(RFC 4021). Mais
comment faire si on veut envoyer le même message en plusieurs
langues, pour s'adapter à une audience variée, ou bien si on n'est
pas sûr des langues parlées par le destinataire ? C'est ce que
permet le nouveau type de
message multipart/multilingual
qui permet d'étiqueter les messages multilingues.
C'est le premier RFC du groupe de travail SLIM, chargé de créer des normes pour indiquer la langue utilisée dans le courrier et pour les communications synchrones (téléphonie sur IP, par exemple, couverte depuis par le RFC 8373), dans le cas où plusieurs langues sont en présence.
Le type de premier niveau multipart/
(RFC 2046, section 5.1, enregistré à
l'IANA) permet d'indiquer un message (ou une partie de
message, en cas de récursivité) composé de plusieurs
parties. Ainsi, multipart/mixed
(RFC 2046, section 5.1.3) indique un message dont
les dfférentes parties sont indépendantes (un texte et une image,
par exemple). Alors que multipart/alternative
(RFC 2046, section 5.1.4) est utilisé pour
le cas où les différentes parties veulent dire la même chose, mais
avec des formats différentes (une version texte
seul et une version HTML, par
exemple, ou bien une image en JPEG et la
même en PNG). Normalement, le lecteur de courrier ne va afficher qu'une
seule des parties d'une
multipart/alternative
, celle qui convient le
mieux, selon les capacités du logiciel de lecture et les
préférences de l'utilisateur. Ce sera la même chose avec ce
nouveau multipart/multilingual
: l'émetteur
enverra le message en plusieurs langues, le logiciel de lecture
n'affichera que celle qui colle le mieux aux choix de
l'utilisateur. Ce type est désormais enregistré
à l'IANA (section 9 du RFC).
Rappelez-vous que MIME est
récursif : un
multipart/
peut contenir un autre
multipart/
. Voici, par exemple, vu par le
MUA mutt, un
multipart/mixed
, dont la première partie est
un multipart/alternative
et la seconde un
fichier PDF :
I 1 <no description> [multipart/alternative, 7bit, 45K] I 2 ├─><no description> [text/plain, 8bit, utf-8, 1,8K] I 3 └─><no description> [text/html, 8bit, utf-8, 43K] A 4 35378031645-672029836-registration.pdf [application/pdf, base64, 84K]
Cette première partie du corps du message, de type
multipart/alternative
, a à son tour deux
parties, une en texte brut et l'autre en HTML. Comme ma
configuration mutt inclut la directive alternative_order
text/plain
, seule la version texte sera affichée, le
HTML étant ignoré.
Revenons aux messages multilingues. Avant ce RFC 8255, la solution la plus fréquente, quand on envoyait un message à quelqu'un dont on n'était pas sûr des préférences linguistiques, était de mettre toutes les versions dans un même message. Du genre :
(English translation at the end) Bonjour, ceci est un message. Hello, this is a message.
(La première ligne ayant pour but d'éviter que le lecteur anglophone se décourage en ne voyant au début que du français.) Cette solution n'est pas satisfaisante : elle passe mal à l'échelle dès qu'il y a plus de deux langues. Et elle ne permet pas la sélection automatique de la langue par le logiciel du destinataire.
Le type MIME d'un message est mis dans un en-tête
Content-Type:
. Par exemple, un message de
plusieurs parties représentant le même contenu serait :
Content-Type: multipart/alternative; boundary="------------E6043EF6F3B557567F3B18F4"
Où boundary
indique le texte qui sera le
séparateur entre les parties (RFC 2046,
section 5.1). La section 2 du RFC décrit ce qu'on peut désormais
mettre dans le Content-Type:
, avec le nouveau
type multipart/multilingual
.
Il ressemble beaucoup au
multipart/alternative
et, comme tous les
multipart/quelquechose
(RFC 2046, section 5.1.1), a une chaîne de
caractères qui indique le passage d'une partie à une autre :
Content-Type: multipart/multilingual; boundary=01189998819991197253
Chacune des parties d'un message en plusieurs langues doit
indiquer la langue de la partie, avec l'en-tête
Content-Language:
. Mais attention, comme il
faut tenir compte des vieux clients de messagerie qui ne
connaissent pas ce RFC, la première partie ne doit
pas avoir de
Content-Language:
, elle sert de solution de
repli, car c'est elle qui sera affichée en premier par les logiciels qui ne
connaissent pas multipart/multilingual
. Comme
elle sert de secours, il est recommandé qu'elle soit dans le
format le plus simple, donc text/plain
, en
UTF-8 (car il faut pouvoir représenter
diverses langues). Cette partie se nomme la préface. En la
lisant, l'utilisateur pourra donc comprendre qu'il doit mettre à
jour vers un logiciel plus récent.
Les parties suivantes, après la préface, vont être écrites dans des langues différentes. Le RFC recommande de les mettre dans l'ordre, avec celles qui ont le plus de chance d'être pertinentes en premier. (Ainsi, une société française ayant une activité européenne, avec une majorité de clients français, et envoyant un message en anglais, allemand et français, mettra sans doute le français en premier, suivi de l'anglais.)
Chacune de ces parties aura un en-tête
Content-Language:
, pour indiquer la langue,
et Content-Type:
pour indiquer le type
MIME. (Rappelez-vous toujours que MIME est récursif.) Il est
recommandé que chaque partie soit un message complet (avec
notamment le champ Subject:
, qui a besoin
d'être traduit, lui aussi, et le champ From:
,
dont une partie peut être utilement traduite). Le type conseillé est donc
message/rfc822
(RFC 2046, section 5.2.1), mais on peut aussi utiliser le
plus récent message/global
(RFC 6532).
Notez bien que ce RFC ne spécifie évidemment pas comment se fera la traduction : il ne s'occupe que d'étiqueter proprement le résultat.
Le message peut se terminer par une section « indépendante de
la langue » (par exemple une image ne comportant pas de
texte, si le message peut être porté par une image). Dans ce cas, son Content-Language:
doit indiquer zxx
, ce qui veut dire
« information non pertinente » (RFC 5646,
section 4.1). C'est elle qui sera sélectionnée si aucune partie ne
correspond aux préférences de l'utilisateur.
Maintenant, que va faire le client de
messagerie qui reçoit un tel message multilingue ? La
section 4 de notre RFC décrit les différents cas. D'abord, avec
les logiciels actuels, le client va afficher les différentes
parties de multipart/multilingual
dans
l'ordre où elles apparaissent (donc, en commençant par la
préface).
Mais le cas le plus intéressant est évidemment celui d'un client plus récent, qui connait les messages multilingues. Il va dans ce cas sauter la préface (qui n'a pas de langue indiquée, rappelez-vous) et sélectionner une des parties, celle qui convient le mieux à l'utilisateur.
Un moment. Arrêtons-nous un peu. C'est quoi, la « meilleure » version ? Imaginons un lecteur francophone, mais qui parle anglais couramment. Il reçoit un message multilingue, en français et en anglais. S'il choisissait manuellement, il prendrait forcément le français, non ? Eh bien non, car cela dépend de la qualité du texte. Comme peut le voir n'importe quel utilisateur du Web, les différentes versions linguistiques d'un site Web ne sont pas de qualité égale. Il y a le texte original, les traductions faites par un professionnel compétent, les traductions faites par le stagiaire, et celles faites par un programme (en général, les plus drôles). Sélectionner la meilleure version uniquement sur la langue n'est pas une bonne idée, comme le montre la mauvaise expérience de HTTP. Ici, pour reprendre notre exemple, si la version en anglais est la version originale, et que le français est le résultat d'une mauvaise traduction par un amateur, notre francophone qui comprend bien l'anglais va sans doute préférer la version en anglais.
Il est donc crucial d'indiquer le type de traduction effectuée,
ce que permet le Content-Translation-Type:
exposé plus loin, en section 6. (Les premières versions du projet
qui a mené à ce RFC, naïvement, ignoraient complètement ce
problème de la qualité de la traduction, et de la version
originale.)
Donc, le mécanisme de sélection par le MUA de la « meilleure » partie dans un message multilingue n'est pas complètement spécifié. Mais il va dépendre des préférences de l'utilisateur, et sans doute des règles du RFC 4647.
Si aucune partie ne correspond aux choix de l'utilisateur, le RFC recommande que le MUA affiche la partie indépendante de la langue, ou, si elle n'existe pas, la partie après la préface. Le MUA peut également proposer le choix à l'utilisateur (« Vous n'avez indiqué qu'une langue, le français. Ce message est disponible en anglais et en chinois. Vous préférez lequel ? »)
La section 5 du RFC présente en détail l'utilisation de l'en-tête
Content-Language:
. Il doit suivre la norme
existante de cet en-tête, le RFC 3282, et sa valeur doit
donc être une étiquette de langue du RFC 5646. Des exemples (en pratique, il ne faut
évidemment en mettre qu'un) :
Content-Language: fr Content-Language: sr-Cyrl Content-Language: ay Content-Language: aaq
Le premier exemple concerne le français, le second le serbe écrit en alphabet cyrillique, le troisième l'aymara et le quatrième l'abénaqui oriental.
La section décrit le nouvel en-tête
Content-Translation-Type:
qui indique le type
de traduction réalisé. Il peut prendre trois valeurs,
original
(la version originale),
human
(traduit par un humain) et
automated
(traduit par un programme). Notez
que les humains (les programmes aussi, d'ailleurs) varient
considérablement dans leurs compétences de traducteur. J'aurais
personnellement préféré qu'on distingue un traducteur
professionnel d'un amateur, mais la traduction fait partie des
métiers mal compris, où beaucoup de gens croient que si on parle
italien et allemand, on peut traduire de l'italien en allemand
correctement. C'est loin d'être le cas. (D'un autre côté, comme
pour tous les étiquetages, si on augmente le nombre de choix, on
rend l'étiquetage plus difficile et il risque d'être incorrect.)
Voici un exemple complet, tel qu'il apparait entre deux logiciels de messagerie. Il est fortement inspiré du premier exemple de la section 8 du RFC, qui comprend également des exemples plus complexes. Dans ce message, l'original est en anglais, mais une traduction française a été faite par un humain.
From: jeanne@example.com To: jack@example.com Subject: Example of a message in French and English Date: Thu, 7 Apr 2017 21:28:00 +0100 MIME-Version: 1.0 Content-Type: multipart/multilingual; boundary="01189998819991197253" --01189998819991197253 Content-Type: text/plain; charset="UTF-8" Content-Disposition: inline Content-Transfer-Encoding: 8bit This is a message in multiple languages. It says the same thing in each language. If you can read it in one language, you can ignore the other translations. The other translations may be presented as attachments or grouped together. Ce message est disponible en plusieurs langues, disant la même chose dans toutes les langues. Si vous le lisez dans une des langues, vous pouvez donc ignorer les autres. Dans votre logiciel de messagerie, ces autres traductions peuvent se présenter comme des pièces jointes, ou bien collées ensemble. --01189998819991197253 Content-Type: message/rfc822 Content-Language: en Content-Translation-Type: original Content-Disposition: inline From: Manager <jeanne@example.com> Subject: Example of a message in French and English Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Hello, this message content is provided in your language. --01189998819991197253 Content-Type: message/rfc822 Content-Language: fr Content-Translation-Type: human Content-Disposition: inline From: Directrice <jeanne@example.com> Subject: =?utf-8?q?Message_d=27exemple=2C_en_fran=C3=A7ais_et_en_anglais?= Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit MIME-Version: 1.0 Bonjour, ce message est disponible dans votre langue. --01189998819991197253--
On note qu'il y a une préface (la première partie, celle qui
commence par « This is a message in multiple languages »), que
chacun des deux parties étiquetées a un
From:
(pour pouvoir adapter le titre de
l'expéditrice) et un sujet (celui en français étant encodé selon
le RFC 2047). Les deux dernières parties
ont un Content-Translation-Type:
.
Actuellement, il ne semble pas qu'il existe de
MUA qui gère cette nouvelle norme. Si vous
voulez envoyer des messages à ce format, vous pouvez copier/coller
l'exemple ci-dessus, ou bien utiliser le programme Python send-multilingual.py
.
Voici le message d'exemple cité plus haut affiché par mutt (version NeoMutt 20170113 (1.7.2)) :
Thunderbird affiche à peu près la même chose.
Gmail a stupidement décidé de mettre ce message dans le dossier Spam. Une fois le message sorti de ce purgatoire, voici ce que ça donnait :
Notez l'existence d'une FAQ sur ce format.
Une mise en œuvre dans
mutt a été faite au hackathon
de la réunion IETF 101 par Alexandre Simon. Elle a été soumise en tant que pull
request à mutt. Outre ce travail sur un
MUA, le même auteur a écrit un réflecteur de
courrier permettant de tester ce RFC. Vous envoyez un
message ordinaire en français à
reflector+sl_fr+tl_zh@rfc8255.as-home.fr
(sl
= Source Language, tl = Target
Language, ici, on dit que le message est en français, et
on veut une réponse en chinois), et il répond avec
un message conforme au RFC 8255, traduit par Google Translate. Voici par exemple une demande :
To: reflector+sl_fr+tl_zh@rfc8255.as-home.fr MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit User-Agent: NeoMutt/20170113 (1.7.2) Subject: Test je parle la France From: Stephane Bortzmeyer <stephane@bortzmeyer.org> Alors, ça donne quoi ? Surtout s'il y a plusieurs lignes.
Et sa réponse :
Content-Type: multipart/multilingual; boundary="----------=_1521485603-32194-0" Content-Transfer-Encoding: binary MIME-Version: 1.0 To: Stephane Bortzmeyer <stephane@bortzmeyer.org> Sender: <reflector@idummy.as-home.fr> Subject: Test je parle la France From: Stephane Bortzmeyer <stephane@bortzmeyer.org> This is a multi-part message in MIME format... ------------=_1521485603-32194-0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: binary Alors, ça donne quoi ? Surtout s'il y a plusieurs lignes. ------------=_1521485603-32194-0 Content-Type: message/rfc822 Content-Disposition: inline Content-Transfer-Encoding: binary Content-Language: fr Content-Translation-Type: original Content-Type: text/plain; charset=utf-8 From: Stephane Bortzmeyer <stephane@bortzmeyer.org> Subject: Test je parle la France Alors, ça donne quoi ? Surtout s'il y a plusieurs lignes. ------------=_1521485603-32194-0 Content-Type: message/rfc822 Content-Disposition: inline Content-Transfer-Encoding: binary Content-Language: zh Content-Translation-Type: automated Content-Type: text/plain; charset=utf-8 From: Stephane Bortzmeyer <stephane@bortzmeyer.org> Subject: =?UTF-8?B?5rWL6K+V5oiR6K6y5rOV5Zu9?= 那么,这给了什么? 特别是如果有几条线。 ------------=_1521485603-32194-0 Content-Type: message/rfc822 Content-Disposition: inline Content-Transfer-Encoding: binary Content-Language: zxx Content-Translation-Type: human Content-Type: text/plain; charset=utf-8 /////| ///// | ///// | |~~~| M | |===| U | | | L | | R | T | | F | I | | C | L | | 8 | I | | 2 | N | | 5 | G | | 5 | U | | | A | | | L | | | | | | / | | / |===|/ '---' ------------=_1521485603-32194-0--
On notera la dernière partie, qui utilise la langue
zxx
, code qui veut dire « non spécifiée », et
qui sert à mettre du texte qui sera affiché dans tous les
cas. Notez aussi que le message originel est inclus.
Ce message est ainsi affiché par un mutt actuel :
Et par le mutt patché pendant le hackathon :
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Saint-Andre (Jabber.org), A. Melnikov (Isode)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF precis
Première rédaction de cet article le 6 octobre 2017
Ah, les plaisirs de l'internationalisation du logiciel... Quand l'informatique ne concernait que les États-Unis, tout était simple. Un utilisateur ne pouvait avoir un nom (un login) que s'il était composé uniquement des lettres de l'alphabet latin (et même chose pour son mot de passe). Mais de nos jours, il n'est pas acceptable de se limiter au RFC 20. Il faut que Пу́тин ou 艾未未 puissent écrire leur nom dans leur écriture et l'informatique doit suivre. Notre RFC décrit comment le faire (il remplace le RFC 7613, mais il y a peu de changements, rassurez-vous).
Mais pourquoi faut-il standardiser quelque chose ? Pourquoi ne
pas dire simplement « les noms d'utilisateur sont en
Unicode » et basta ? Parce que les
logiciels qui gèrent noms d'utilisateurs et mots de passe ont
besoin de les manipuler, notamment de les
comparer. Si ПУ́ТИН
essaie de se loguer, et
que la base de données contient un utilisateur пу́тин
, il faut bien déterminer si c'est
le même utilisateur (ici, oui, à part la casse). C'est en général assez simple dans l'alphabet
latin (ou dans le cyrillique utilisé pour
les exemples) mais il y a d'autres cas plus vicieux qui
nécessitent quelques règles supplémentaires.
Le cadre général de l'internationalisation des identificateurs est normalisé dans le RFC 8264. Notre nouveau RFC 8265 est l'adaptation de ce RFC 8264 au cas spécifique des noms d'utilisateur et des mots de passe.
Ces noms et ces mots de passe (aujourd'hui, il faudrait plutôt
dire phrases de passe) sont largement
utilisés pour l'authentification, soit
directement (SASL
PLAIN
du RFC 4616, authentification
de base de HTTP du RFC 7617), ou
bien comme entrée d'une fonction de condensation
cryptographique (SASL SCRAM
du
RFC 5802 ou bien authentification HTTP
digest du RFC 7616). L'idée est que
les opérations de base sur ces noms (et sur les mots de passe) ne
surprennent pas excessivement l'utilisateur, quel que soit son
pays, sa langue, sa culture. Un Français ou un Belge ne sera probablement pas trop
étonné que Dupont
soit accepté comme synonyme
de dupont
mais il le serait davantage si
dupond
l'était. Évidemment, la tâche est
impossible (les utilisateurs sont tous différents) mais l'idée est
de ne pas faire un système parfait mais un système qui marche la
plupart du temps.
C'est là qu'intervient le cadre PRECIS (PReparation,
Enforcement, and Comparison of Internationalized
Strings) du RFC 8264. Il évite que
chaque développeur d'un système d'authentification doive lire et
comprendre toutes les conséquences d'Unicode, et lui permet de
s'appuyer sur une analyse déjà faite. Un exemple de piège
d'Unicode (et qui montre pourquoi « on va juste dire que les noms
d'utilisateur peuvent être n'importe quel caractère Unicode » est
sans doute une mauvaise politique) est le chiffre 1 en exposant,
U+00B9 (comme ça : « ¹ » Si vous ne voyez rien, c'est parce
que votre logiciel ne sait pas afficher ce caractère. Vous pouvez
toujours regarder le source HTML pour comprendre l'exemple.). Doit-on
l'autoriser ? Le mettre en correspondance avec le 1 traditionnel
de façon à ce que user¹
et
user1
soient le même nom ? Imaginez un client
XMPP qui vous dise
« user¹@example.com
veut être votre
copain. Je l'autorise ? » et que vous acceptiez en croyant que
c'était user1@example.com
. Bien sûr, on ne
peut pas réellement parler d'« attaque » dans ce cas, une telle
erreur permettrait juste de faire quelques farces mais, pour le
logiciel qui vérifie une identité, des confusions seraient plus
gênantes. Si les « attaques » exploitant la similitude de
caractères Unicode sont surtout un fantasme d'informaticien
anglophone n'ayant jamais réellement accepté
l'internationalisation (plutôt qu'une réalité du terrain), il est
quand même plus prudent de supprimer quelques causes de
cafouillage le plus tôt possible.
(Ce RFC suggère également de séparer le nom d'utilisateur, identificateur formel et n'utilisant qu'un jeu de caractères restreint, et la description (cf. RFC 8266) qui pourrait utiliser davantage de caractères. Twitter ou Mastodon fonctionnent ainsi.)
Notre RFC compte deux sections importantes, décrivant le profil
PRECIS pour les noms d'utilisateur (section 3) et les mots de
passe (section 4). Commençons par les noms d'utilisateur. Un nom
est une chaîne de caractères Unicode
composée de parties séparées par des espaces. Chaque partie doit
être une instance de IdentifierClass
et est
normalisée en NFC. Pourquoi cette notion de « parties
séparées par des espaces » ? Parce que la classe
IdentifierClass
du RFC 8264 ne permet pas les espaces, ce qui est gênant pour
certains identificateurs (« Prénom Nom » par exemple, cf. section
3.5). D'où la grammaire de la section 3.1 :
username = userpart *(1*SP userpart)
qui dit « un nom d'utilisateur est composé d'au moins une partie,
les parties étant séparées par un nombre quelconque
d'espaces ». Une des conséquences de cette grammaire étant que le
nombre d'espaces n'est pas significatif : Jean
Dupont
et Jean Dupont
sont
le même identificateur.
Chaque partie étant une instance de
l'IdentifierClass
du RFC 8264, les caractères interdits par cette classe sont
donc interdits pour les noms d'utilisateurs. Une application
donnée peut restreindre (mais pas étendre) ce profil. Ces noms
d'utilisateurs sont-ils sensibles à la casse ? Certains protocoles ont
fait un choix et d'autres le choix opposé. Eh bien, il y a
deux sous-profils, un sensible et
un insensible (ce dernier étant recommandé). Les
protocoles et applications utilisant ce RFC 8265
devront annoncer clairement lequel ils utilisent. Et les
bibliothèques logicielles manipulant ces utilisateurs auront
probablement une option pour indiquer le sous-profil à
utiliser.
Le sous-profil UsernameCaseMapped
rajoute
donc une règle de préparation des chaînes de caractères : tout
passer en minuscules (avant les comparaisons, les condensations
cryptographiques, etc), en utilisant l'algorithme
toLowerCase d'Unicode (section 3.13 de la norme
Unicode ; et, oui, changer la casse est
bien plus compliqué en Unicode qu'en ASCII). Une
fois la préparation faite, on peut comparer octet par octet, si
l'application a bien pris soin de définir l'encodage.
L'autre sous-profil, UsernameCasePreserved
ne change pas la casse, comme son nom
l'indique. ПУ́ТИН
et
пу́тин
y sont donc deux identificateurs
différents. C'est la seule différence entre les deux
sous-profils. Notre RFC recommande le profil insensible à la
casse, UsernameCaseMapped
, pour éviter des
surprises comme celles décrites dans le RFC 6943 (cf. section 8.2 de notre RFC).
Bon, tout ça est bien nébuleux et vous préféreriez des exemples ? Le RFC nous en fournit. D'abord, des identificateurs peut-être surprenants mais légaux (légaux par rapport à PRECIS : certains protocoles peuvent mettre des restrictions supplémentaires). Attention, pour bien les voir, il vous faut un navigateur Unicode, avec toutes les polices nécessaires :
juliet@example.com
: le @ est
autorisé donc un JID (identificateur
XMPP) est légal.fussball
: un nom d'utilisateur
traditionnel, purement ASCII, qui passera
partout, même sur les systèmes les plus antédiluviens.fußball
: presque le même mais
avec un peu d'Unicode. Bien qu'en
allemand, on traite en général ces deux
identificateurs comme identiques, pour PRECIS, ils sont
différents. Si on veut éviter de la confusion aux germanophones,
on peut interdire la création d'un des deux identificateurs si
l'autre existe déjà : PRECIS ne donne que des règles miminales,
on a toujours droit à sa propre politique derrière.π
: entièrement en Unicode, une
lettre.Σ
: une lettre
majuscule.σ
: la même en
minuscule. Cet identificateur et le précédent seront identiques
si on utilise le profil UsernameCaseMapped
et différents si on utilise le profil UsernameCasePreserved
.ς
: la même, lorsqu'elle est en
fin de mot. Le cas de ce sigma final est
compliqué, PRECIS ne tente pas de le résoudre. Comme pour le
eszett plus haut, vous pouvez toujours
ajouter des règles locales.Par contre, ces noms d'utilisateurs ne sont pas valides :
HenriⅣ
: le chiffre romain 4 à
la fin est illégal (car il a ce qu'Unicode appelle « un
équivalent en compatibilité », la chaîne « IV »).♚
: seules les lettres sont
acceptées, pas les symboles (même règle que pour les noms de domaine).Continuons avec les mots de passe (section 4). Comme les noms,
le mot de passe est une chaîne de caractères
Unicode. Il doit
être une instance de FreeformClass
. Cette
classe autorise bien plus de caractères que pour les noms
d'utilisateur, ce qui est logique : un mot de passe doit avoir
beaucoup d'entropie. Peu de caractères sont donc interdits (parmi
eux, les caractères de
contrôle, voir l'exemple plus bas). Les
mots de passe sont sensibles à la casse.
Des exemples ? Légaux, d'abord :
correct horse battery staple
: vous
avez reconnu un fameux dessin de XKCD.Correct Horse Battery Staple
: les
mots de passe sont sensibles à la casse, donc c'est un mot de
passe différent du précédent.πßå
: un mot de passe en Unicode ne pose
pas de problème.Jack of ♦s
: et les symboles sont
acceptés, contrairement à ce qui se passe pour les noms d'utilisateur.foo bar
: le truc qui
ressemble à un trait est
l'espace de l'Ogham, qui doit normalement
être normalisé en un espace ordinaire, donc ce mot de passe est
équivalent à foo bar
.Par contre, ce mot de passe n'est pas valide :
my cat is a by
: les caractères
de contrôle, ici une tabulation, ne sont pas autorisés.Rappelez-vous que ces profils PRECIS ne spécifient que des règles minimales. Un protocole utilisant ce RFC peut ajouter d'autres restrictions (section 5). Par exemple, il peut imposer une longueur minimale à un mot de passe (la grammaire de la section 4.1 n'autorise pas les mots de passe vides mais elle autorise ceux d'un seul caractère, ce qui serait évidemment insuffisant pour la sécurité), une longueur maximale à un nom d'utilisateur, interdire certains caractères qui sont spéciaux pour ce protocole, etc.
Certains profils de PRECIS suggèrent d'être laxiste en acceptant certains caractères ou certaines variantes dans la façon d'écrire un mot (accepter strasse pour straße ? C'est ce qu'on nomme le principe de robustesse.) Mais notre RFC dit que c'est une mauvaise idée pour des mots utilisés dans la sécurité, comme ici (voir RFC 6943).
Les profils PRECIS ont été ajoutés au registre IANA (section 7 de notre RFC).
Un petit mot sur la sécurité et on a fini. La section 8 de notre RFC revient sur quelques points difficiles. Notamment, est-ce une bonne idée d'utiliser Unicode pour les mots de passe ? Ça se discute. D'un côté, cela augmente les possibilités (en simplifiant les hypothèses, avec un mot de passe de 8 caractères, on passe de 10^15 à 10^39 possibilités en permettant Unicode et plus seulement ASCII), donc l'entropie. D'un autre, cela rend plus compliquée la saisie du mot de passe, surtout sur un clavier avec lequel l'utilisateur n'est pas familier, surtout en tenant compte du fait que lors de la saisie d'un mot de passe, ce qu'on tape ne s'affiche pas. Le monde est imparfait et il faut choisir le moindre mal...
L'annexe A du RFC décrit les changements (peu nombreux) depuis
l'ancien RFC 7613. Le principal est le
passage de la conversion de casse de la fonction Unicode
CaseFold()
à
toLowerCase()
. Et
UTF-8 n'est plus obligatoire, c'est
désormais à l'application de décider (en pratique, cela ne
changera rien, UTF-8 est déjà l'encodage recommandé dans
l'écrasante majorité des applications). Sinon, il y a une petite
correction dans l'ordre des opérations du profil
UsernameCaseMapped
, et quelques nettoyages et
mises à jour.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Saint-Andre (Jabber.org), M. Blanchet (Viagenie)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF precis
Première rédaction de cet article le 6 octobre 2017
Dans la longue marche vers une plus grande internationalisation de l'Internet, la question des identificateurs (comme par exemple les noms de domaine) a toujours été délicate. Ce sont en effet à la fois des éléments techniques, traités automatiquement par les programmes, et des marqueurs d'identité, vus par les humains (par exemple sur des cartes de visite) et manipulés par eux. Plutôt que de laisser chaque protocole internationaliser ses identificateurs (plus ou moins bien), l'approche de ce RFC est unificatrice, en élaborant des règles qui peuvent servir à de larges classes d'identificateurs, pour de nombreux protocoles différents. Il remplace le premier RFC qui avait suivi cette voie, le RFC 7564, désormais dépassé (mais les changements sont peu importants, c'est juste de la maintenance).
Cette opposition entre « éléments techniques » et « textes
prévus pour l'utilisateur » est au cœur du RFC 2277 qui pose comme principe politique qu'on
internationalise les seconds, mais pas les premiers. Cet excellent
principe achoppe souvent sur la question des identificateurs, qui
sont les deux à la fois. D'un côté, les programmes doivent les
traiter (les identificateurs doivent donc être clairement définis,
sans ambiguïté), de l'autre les humains les voient, les
communiquent, les échangent (les identificateurs doivent donc
permettre, sinon toutes les constructions du langage humain, en
tout cas un sous-ensemble d'Unicode qui
parait raisonnable aux humains ordinaires : pas question d'imposer
stephane
comme nom de login
à un utilisateur nommé Stéphane, avec un accent sur le E). C'est
cette double nature des identificateurs (ainsi, il est vrai, que
l'énorme couche de bureaucratie qui gère les noms de domaine) qui explique la durée et
la vivacité des discussions sur les IDN.
Maintenant que ces IDN existent (depuis plus de quatorze ans, RFC 3490), que faire avec les autres identificateurs ? Une possibilité aurait été que chaque protocole se débrouille avec ses propres identificateurs, comme l'a fait le DNS avec les noms de domaine. Mais cela menait à une duplication du travail (et tous les auteurs de protocole ne sont pas des experts Unicode) et surtout à un risque de choix très différents : certains protocoles autoriseraient tel caractère Unicode et d'autres pas, sans que l'utilisateur ordinaire puisse comprendre clairement les raisons de cette différence. L'idée de base du groupe PRECIS était donc d'essayer de faire des règles qui s'appliqueraient à beaucoup de protocoles, épargnant aux concepteurs de ces protocoles de les concevoir eux-mêmes, et offrant à l'utilisateur une certaine homogénéité. Ce RFC 8264 est le cadre de définition des identificateurs internationalisés. Ce cadre permet la manipulation d'identificateurs internationalisés (par exemple leur comparaison, comme lorsqu'un utilisateur tape son nom et son mot de passe, et qu'il faut les vérifier, cf. RFC 6943.)
Certaines personnes, surtout en Anglosaxonnie, pourraient estimer que c'est bien compliqué tout cela, et qu'il vaudrait mieux limiter les identificateurs à ASCII. Certes, Unicode est compliqué, mais sa complexité ne fait que refléter celle des langues humaines (section 6.1 de notre RFC). On ne peut pas simplifier Unicode, sauf à éliminer une partie de la diversité humaine.
Le nom du groupe de travail PRECIS reflète les trois fonctions essentielles que font les programmes qui manipulent les identificateurs internationalisés :
A priori, les serveurs Internet seront responsables de l'application, les clients n'ayant à faire qu'une préparation. Ayant souvent moins de ressources, les clients pourraient en effet avoir du mal à faire certaines opérations Unicode complexes (section 3).
Les principes du travail de PRECIS sont :
Si tout va bien, ces principes permettront l'indépendance vis-à-vis des versions d'Unicode (ou, plus exactement, la possibilité de passer à une nouvelle version d'Unicode sans faire une nouvelle norme incompatible), le partage des tables et du logiciel entre applications (par exemple par le biais de bibliothèques communes, utilisables si toutes les applications reposent sur PRECIS), et moins de surprises pour les utilisateurs, qui auraient été bien embêtés si chaque protocole Internet avait eu une manière complètement différente de prévoir l'internationalisation.
Bien, quelles sont donc les classes prévues par PRECIS (section 4) ? L'idée est de faire un compromis entre expressivité et sécurité. Qu'est-ce que la sécurité vient faire là dedans ? Personne ne le sait trop (le RFC utilise plusieurs fois safe et safety sans expliquer face à quels risques) mais Unicode fait souvent peur aux informaticiens anglo-saxons et il est donc courant d'estimer qu'il existe des caractères dangereux.
Il y a donc deux classes en tout et pour tout dans PRECIS :
IdentifierClass
et
FreeformClass
. La première classe servira à
identifier utilisateurs, machines, pièces de discussions en
messagerie instantanée, fichiers, etc, et ne
permettra que les lettres, les nombres et quelques symboles (comme
le ! ou le +, car ils étaient dans ASCII). C'est contraignant mais l'idée est qu'on veut des
désignations simples et sans ambiguïté, pas écrire des romans. La
seconde classe servira à tout le reste (mots de passe, textes
affichés comme la description d'une pièce XMPP, nom complet d'une
machine, etc). Par exemple, une imprimante sera
imprimante-jean-et-thérèse
pour les protocoles
qui demandent un nom de la classe
IdentifierClass
et Imprimante de
Jean & Thérèse
lorsqu'on pourra utiliser un nom
FreeformClass
.
Les classes sont définies par les caractères inclus et exclus. Plus précisément, un caractère peut être, pour une classe donnée (voir aussi la section 8) :
La classe IdentifierClass
se définit donc par :
JoinControl
du RFC 5892, section 2.8.La classe Freeform Class
se définit, elle,
par :
JoinControl
plus quelques exceptions.Ces listes de caractères autorisés ou interdits ne sont pas suffisantes. Il y a d'autres points à régler (section 5), ce qui se fait typiquement dans les profils. Ainsi, un profil doit définir :
target0
et target0
soient considérés différents (le fameux POLA, principe de
la moindre surprise).toLowerCase()
(section 3.13 de la norme
Unicode). Attention, cette méthode Unicode ne gère pas des cas qui
dépendent de la langue, dont le plus fameux est le i sans point du
turc (U+0131 c'est-à-dire ı). Le
changement de casse est évidemment déconseillé pour les mots de
passe (puisqu'il diminue l'entropie). Notez aussi que ce cas
illustre le fait que les transformations PRECIS ne sont pas sans
perte : si on met tout en minuscules, Poussin
ne se distingue plus de poussin
.Un profil peut également interdire certains caractères normalement autorisés (mais pas l'inverse).
Au passage, pour la comparaison, le RFC (section 7) impose un ordre à ces opérations. Par exemple, la mise en correspondance de la version large sur la version normale doit se faire avant l'éventuel changement de casse. C'est important car ces opérations ne sont pas commutatives entre elles.
Les profils sont enregistrés à l'IANA. Le RFC met bien en garde contre leur multiplication : toute l'idée de PRECIS est d'éviter que chaque protocole ne gère l'internationalisation des identificateurs de manière différente, ce qui empêcherait la réutilisation de code, et perturberait les utilisateurs. Si on avait un cadre commun mais des dizaines de profils différents, on ne pourrait pas dire qu'on a atteint cet objectif. Par exemple, en matière d'interface utilisateur, PRECIS essaie de s'en tenir au POLA (Principle of Least Astonishment) et ce principe serait certainement violé si chaque application trouvait rigolo d'interdire un caractère ou l'autre, sans raison apparente. Le RFC estime d'ailleurs (section 5.1) qu'il ne devrait y avoir idéalement que deux ou trois profils. Mais ce n'est pas possible puisque les protocoles existent déjà, avec leurs propres règles, et qu'on ne peut pas faire table rase de l'existant (tous les protocoles qui ont déjà définis des caractères interdits, comme IRC, NFS, SMTP, XMPP, iSCSI, etc).
Un petit point en passant, avant de continuer avec les
applications : vous avez noté que la classe
IdentifierClass
interdit les espaces (tous les
espaces Unicode, pas seulement le U+0020 d'ASCII), alors que
certaines applications acceptent les espaces dans les
identificateurs (par exemple, Unix les
accepte sans problèmes dans les noms de fichier,
Apple permet depuis longtemps de les
utiliser pour nommer iTrucs et imprimantes, etc). La section
5.3 explique cette interdiction :
françoise durand
avec françoisedurand
.C'est embêtant (toute contrainte est embêtante) mais le compromis a semblé raisonnable au groupe PRECIS. Tant pis pour les espaces.
Passons maintenant aux questions des développeurs d'applications (section 6 du RFC). Que doivent-ils savoir pour utiliser PRECIS correctement ? Idéalement, il suffirait de lier son code aux bonnes bibliothèques bien internationalisées et tout se ferait automatiquement. En pratique, cela ne se passera pas comme ça. Sans être obligé de lire et de comprendre tout le RFC, le développeur d'applications devra quand même réflechir un peu à l'internationalisation de son programme :
UsernameCaseMapped
de la classe
IdentifierClass
» (cf. RFC 8265).Sur ce dernier point, il faut noter que la frontière est mince entre « interdire plusieurs caractères normalement autorisés par le profil » et « définir un nouveau profil ». La possibilité d'interdire des caractères supplémentaires est surtout là pour s'accomoder des protocoles existants (comme dans l'exemple du courrier ci-dessus), et pour éviter d'avoir un profil par application existante.
Votre application pourra avoir besoin de constructions au-dessus
des classes existantes. Par exemple, si un nom d'utilisateur, dans
votre programme, peut s'écrire « Prénom Nom », il ne peut pas être
une instance de la classe IdentifierClass
, qui
n'accepte pas les espaces pour la raison indiquée plus haut. Il
faudra alors définir un concept « nom d'utilisateur », par exemple
en le spécifiant comme composé d'une ou plusieurs instances de
IdentifierClass
, séparées par des espaces. En
ABNF :
username = userpart *(1*SP userpart) userpart = ... ; Instance d'IdentifierClass
La même technique peut être utilisée pour spécifier des identificateurs
qui ne seraient normalement pas autorisés par
IdentifierClass
comme
stéphane@maçonnerie-générale.fr
ou /politique/séries/Game-of-Thrones/saison05épisode08.mkv
.
On a vu plus haut qu'un des principes de PRECIS était de définir les caractères autorisés de manière algorithmique, à partir de leur propriétés Unicode, et non pas sous la forme d'une liste figée (qu'il faudrait réviser à chaque nouvelle version d'Unicode). Les catégories de caractères utilisées par cet algorithme sont décrites en section 9. Par exemple, on y trouve :
LettersDigits
qui rassemble les chiffres et
les lettres. (Rappelez-vous qu'on utilise Unicode : ce ne sont pas
uniquement les lettres et les chiffres d'ASCII.)ASCII7
, les caractères
d'ASCII, à l'exception des caractères de
contrôle,Spaces
, tous les espaces possibles (comme
le U+200A, HAIR SPACE, ainsi appelé en raison de sa
minceur),Symbols
, les symboles, comme U+20A3
(FRENCH FRANC SIGN, ₣) ou U+02DB
(OGONEK, ˛),Plusieurs registres IANA sont nécessaires pour stocker toutes les données nécessaires à PRECIS. La section 11 les recense tous. Le plus important est le PRECIS Derived Property Value, qui est recalculé à chaque version d'Unicode. Il indique pour chaque caractère s'il est autorisé ou interdit dans un identificateur PRECIS. Voici sa version pour Unicode 6.3 (on attend avec impatience une mise à jour…).
Les deux autres registres stockent les classes et les profils (pour l'instant, ils sont quatre). Les règles d'enregistrement (section 11) dans le premier sont strictes (un RFC est nécessaire) et celles dans le second plus ouvertes (un examen par un expert est nécessaire). La section 10 explique aux experts en question ce qu'ils devront bien regarder. Elle note que l'informaticien ordinaire est en général très ignorant des subtilités d'Unicode et des exigences de l'internationalisation, et que l'expert doit donc se montrer plus intrusif que d'habitude, en n'hésitant pas à mettre en cause les propositions qu'il reçoit. Dans beaucoup de RFC, les directives aux experts sont d'accepter, par défaut, les propositions, sauf s'il existe une bonne raison de les rejeter. Ici, c'est le contraire : le RFC recommande notamment de rejeter les profils proposés, sauf s'il y a une bonne raison de les accepter.
La section 12 est consacrée aux problèmes de sécurité qui, comme
toujours lorsqu'il s'agit
d'Unicode, sont plus imaginaires que réels. Un des problèmes
envisagés est celui du risque de confusion entre deux caractères
qui sont visuellement proches. Le problème existe déjà avec le seul
alphabet latin (vous voyez du premier coup
la différence entre google.com
et
goog1e.com
?) mais est souvent utilisé comme
prétexte pour retarder le déploiement d'Unicode. PRECIS se voulant
fidèle au principe POLA, le risque de confusion est
considéré comme important. Notez que le risque réel dépend de la
police
utilisée. Unicode normalisant des caractères et non pas des
glyphes, il n'y a pas de
solution générale à ce problème dans Unicode (les écritures
humaines sont compliquées : c'est ainsi). Si le texte
ᏚᎢᎵᎬᎢᎬᏒ ressemble
à STPETER, c'est que vous utilisez une police qui ne distingue pas
tellement l'alphabet
cherokee de l'alphabet latin. Est-ce que ça a des
conséquences pratiques ? Le RFC cite le risque accru de
hameçonnage, sans citer les nombreuses
études qui montrent le contraire (cf. le Unicode Technical Report
36, section 2, « the use of visually
confusable characters in spoofing is often overstated »,
et la FAQ de
sécurité d'Unicode).
Quelques conseils aux développeurs concluent cette partie : limiter le nombre de caractères ou d'écritures qu'on accepte, interdire le mélange des écritures (conseil inapplicable : dans la plupart des alphabets non-latins, on utilise des mots entiers en alphabet latin)... Le RFC conseille aussi de marquer visuellement les identificateurs qui utilisent plusieurs écritures (par exemple en utilisant des couleurs différentes), pour avertir l'utilisateur.
C'est au nom de ce principe POLA que la classe
IdentifierClass
est restreinte à un ensemble
« sûr » de caractères (notez que ce terme « sûr » n'est jamais
expliqué ou défini dans ce RFC). Comme son nom l'indique,
FreeformClass
est bien plus large et peut donc
susciter davantage de surprises.
PRECIS gère aussi le cas des mots de passe en Unicode. Un bon
mot de passe doit être difficile à deviner ou à trouver par force
brute (il doit avoir beaucoup d'entropie). Et
il faut minimiser les risques de faux positifs (un mot de passe
accepté alors qu'il ne devrait pas : par exemple, des mots de passe
insensibles à la
casse seraient agréables pour les utilisateurs mais
augmenteraient le risque de faux positifs). L'argument de
l'entropie fait que le RFC déconseille de créer des profils
restreints de FreeformClass
, par exemple en
excluant des catégories comme la ponctuation. Unicode permet des
mots de passe vraiment résistants à la force brute, comme
« 𝃍🐬ꢞĚΥਟዶᚦ⬧ »... D'un
autre côté, comme le montre l'exemple hypothétique de mots de passe
insensibles à la casse, il y a toujours une tension entre la
sécurité et l'utilisabilité. Laisser les utilisateurs choisir des
mots de passe comportant des caractères « exotiques » peut poser
bien des problèmes par la suite lorsqu'utilisateur tentera de les
taper sur un clavier peu familier. Il faut noter aussi que les mots
de passe passent parfois par des protocoles intermédiaires (comme
SASL, RFC 4422, ou comme RADIUS, RFC 2865) et
qu'il vaut donc mieux que tout le monde utilise les mêmes règles
pour éviter des surprises (comme un mot de passe refusé par un
protocole intermédiaire).
Enfin, la section 13 de notre RFC se penche sur l'interopérabilité. Elle rappele qu'UTF-8 est l'encodage recommandé (mais PRECIS est un cadre, pas un protocole complet, et un protocole conforme à PRECIS peut utiliser un autre encodage). Elle rappelle aussi qu'il faut être prudent si on doit faire circuler ses identificateurs vers d'autres protocoles : tous ne savent pas gérer Unicode, hélas.
Il existe une mise en œuvre de PRECIS en Go : https://godoc.org/golang.org/x/text/secure/precis
et une en
JavaScript, precis-js.
Les changements depuis le précédent RFC, le RFC 7564, sont résumés dans l'annexe A. Rien de crucial, le
principal étant le remplacement de
toCaseFold()
par
toLower()
pour les opérations insensibles à la
casse. Ces deux fonctions sont définies dans la norme
Unicode (section 4.1). La principale différence est que
toCaseFold()
ne convertit pas forcément en
minuscules (l'alphabet
cherokee est converti en majuscules). Grâce à Python, dont la version 3
fournit casefold
en plus de
lower()
et upper()
,
voici d'abord la différence sur l'eszett :
>>> "ß".upper() 'SS' >>> "ß".lower() 'ß' >>> "ß".casefold() 'ss'
Et sur l'alphabet cherokee (pour le cas, très improbable, où vous ayiez la police pour les minuscules, ajoutées récemment) :
>>> "ᏚᎢᎵᎬᎢᎬᏒ".upper() 'ᏚᎢᎵᎬᎢᎬᏒ' >>> "ᏚᎢᎵᎬᎢᎬᏒ".lower() 'ꮪꭲꮅꭼꭲꭼꮢ' >>> "ᏚᎢᎵᎬᎢᎬᏒ".casefold() 'ᏚᎢᎵᎬᎢᎬᏒ'
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Saint-Andre (Jabber.org)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF precis
Première rédaction de cet article le 6 octobre 2017
Bien des protocoles Internet manipulent des noms qui doivent être parlants pour les utilisateurs et donc, de nos jours, doivent pouvoir être en Unicode. Les noms purement ASCII appartiennent à un passé révolu. Le groupe de travail PRECIS de l'IETF établit des règles pour ces noms, de manière à éviter que chaque protocole, chaque application, ne soit obligé de définir ses propres règles. Ce RFC contient les règles pour un sous-ensemble de ces noms : les noms qui visent plutôt à communiquer avec un utilisateur humain (par opposition aux noms qui sont indispensables aux protocoles réseaux, traités dans le RFC 8265). Il remplace le RFC 7700 (mais il y a peu de changements).
Ces noms « humains » sont typiquement ceux qui sont présentés aux utilisateurs. Ils doivent donc avant tout être « parlants » et il faut donc qu'ils puissent utiliser la plus grande part du jeu de caractères Unicode, sans restrictions arbitraires (contrairement aux identificateurs formels du RFC 8265, pour lesquels on accepte des limites du genre « pas d'espaces » ou « pas d'emojis »).
Le terme utilisé par le RFC pour ces noms « humains » est nicknames, terme qui vient du monde de la messagerie instantanée. Il n'y a pas de terme standard pour les désigner, certains protocoles (comme le courrier) parlent de display names (par opposition au login name ou account name), d'autres utilisent encore d'autres termes (l'article « An Introduction to Petname Systems » peut vous intéresser). Par exemple, dans un message électronique, on pourrait voir :
From: Valérie Pécresse <vp@les-républicains.fr>
Et, dans cet exemple, vp
serait le nom formel
(mailbox name dans le courrier, login
name pour se connecter), alors que Valérie
Pécresse
est le nickname, le nom
montrable aux humains. (Le concept de display
name dans le courrier est normalisé dans la section 3.4.1
du RFC 5322, son exact équivalent XMPP,
le nickname, est dans XEP-0172.)
Autre exemple, le réseau social Mastodon
où mon nom formel est bortzmeyer@mastodon.gougere.fr
alors que la description, le
terme affiché est « S. Bortzmeyer 🗸 » (avec un symbole à la fin, le U+1F5F8).
Comme l'illustre l'exemple ci-dessus, on veut évidemment que le nom puisse être en Unicode, sauf pour la petite minorité des habitants de la planète qui utilisent une langue qui peut s'écrire uniquement en ASCII.
Ces noms « parlants », ces nicknames, ne servent pas qu'à désigner des humains, ils peuvent aussi être utilisés pour des machines, des sites Web (dans les signets), etc.
On pourrait penser qu'il n'y a rien à spécifier pour permettre leur internationalisation. On remplace juste ASCII par Unicode comme jeu de caractères autorisé et vas-y, poupoule. Mais Unicode recèle quelques surprises et, pour que les nicknames fonctionnent d'une manière qui paraitra raisonnable à la plupart des utilisateurs, il faut limiter légèrement leur syntaxe.
Ces limites sont exposées dans la section 2 de notre RFC, qui
définit un profil de PRECIS. PRECIS,
Preparation, Enforcement, and Comparison of
Internationalized Strings est le sigle qui désigne le
projet « Unicode dans tous les identificateurs » et le groupe de
travail IETF qui réalise ce projet. PRECIS définit (RFC 8264) plusieurs classes d'identificateurs et
les nicknames sont un cas particulier de la
classe FreeformClass
(RFC 8264, section 4.3), la moins restrictive
(celle qui permet le plus de caractères).
Outre les restrictions de FreeformClass
(qui n'est pas complètement laxiste : par exemple, cette classe ne
permet pas les caractères de contrôle), le profil
Nickname
:
Thérèse
" et "Thérèse
" sont le
même nom,À noter qu'un nickname doit avoir une taille non nulle, après l'application des ces règles (autrement, un nickname de trois espaces serait réduit à... zéro).
Une fois ce filtrage et cette canonicalisation faite, les nicknames peuvent être comparés par une simple égalité bit à bit (s'ils utilisent le même encodage, a priori UTF-8). Un test d'égalité est utile si, par exemple, un système veut empêcher que deux utilisateurs aient le même nickname.
La section 3 de notre RFC fournit quelques exemples amusants et instructifs de nicknames :
Foo
" et "foo
" sont
acceptables, mais sont le même nom (en application de la régle
d'insensibilité à la casse),Foo Bar
" est permis (les espaces sont
autorisés, avec les quelques restrictions indiquées plus
haut),Échec au roi ♚
" est permis,
rien n'interdit les symboles comme cette pièce du jeu
d'échecs, le caractère Unicode
U+265A,Henri Ⅳ
" est permis (ouvrez
l'œil : c'est le chiffre romain à la fin, U+2163) mais la
normalisation NFKC (précédée du passage en minuscules) va faire
que ce nom est équivalent à "henri iv
"
(avec, cette fois, deux caractères à la fin).Notez que ces règles ne sont pas idempotentes et le RFC demande qu'elles soient appliquées répétitivement jusqu'à la stabilité (ou, au maximum, jusqu'à ce que trois applications aient été faites).
Comme le rappelle la section 4 de notre RFC, les applications doivent maintenant définir l'utilisation de ces noms parlants, où peut-on les utiliser, etc. L'application peut donc avoir des règles supplémentaires, comme une longueur maximale pour ces nicknames, ou des caractères interdits car ils sont spéciaux pour l'application.
L'application ou le protocole applicatif a également toute
latitude pour gérer des cas comme les duplicatas : j'ai écrit plus
haut que "Foo Bar
" et "foo
bar
" étaient considérés comme le même
nickname mais cela ne veut pas dire que leur
coexistence soit interdite : certaines applications permettent à
deux utilisateurs distincts d'avoir le même
nickname. Même chose avec d'autres règles
« métier » comme la possibilité d'interdire certains noms (par
exemple parce qu'ils sont grossiers).
Le profil Nickname
est désormais ajouté au
registre IANA (section 5 du RFC).
L'annexe A décrit les changements depuis le RFC 7700. Le principal est le remplacement de l'opération
Unicode CaseFold()
par
toLower()
pour assurer l'insensibilité à la
casse. La différence est subtile, et ne change rien pour la
plupart des écritures. Sinon, l'erreur notée #4570
a été corrigée. Le reste n'est que de la maintenance.
Première rédaction de cet article le 4 octobre 2017
Un certain nombre de webmestres
configurent leur serveur Web pour être
accessible via un nom en .onion
. Le but
principal est de résister aux tentatives de censure. Un tel
service, si on est soucieux de qualité, doit être
supervisé comme n'importe quel autre
service. J'explique ici comment j'ai configuré cette supervision,
avec le logiciel Icinga, et quelques leçons
apprises à l'occasion.
Tout site Web peut être accédé via le réseau Tor si le visiteur veut garder son
légitime anonymat, ou bien s'il veut
échapper à certaines censures (comme un blocage de l'adresse IP du serveur). Il lui
suffit d'installer le Tor
Browser et hop. Mais cette technique ne protège que le
client, elle ne protège pas le serveur. Des attaques côté serveur
(saisie du nom de
domaine, ou bien saisie du serveur physique) peuvent
couper l'accès. D'où les noms de domaines en
.onion
(ce qu'on
appelle parfois, bien à tort, « service caché », alors que ces
services sont complètement publics et que même le
Googlebot y passe). Avec une telle
configuration (utilisée, par exemple, par mon blog), le risque de censure
est bien plus faible (mais pas nul, attention, vous courrez quand
même des risques). Les journalistes ont inventé le terme
sensationnaliste de
darknet (qui désignait à
l'origine tout à
fait autre chose) pour ces services, et les
politiciens français l'ont traduit par « Internet
clandestin », une traduction d'état
d'urgence… (Et vraiment grotesque puisque tout le
monde, vous, moi, la NSA, Google, peut
visiter ces sites.)
De tels services sont de plus en plus nécessaires, et pas
seulement si on est dissident en Iran, en Chine ou en
Arabie saoudite. Les pays censés être
démocratiques utilisent également la censure de l'Internet comme
récemment en Espagne (contre le
référendum catalan et donc
en partie contre le .cat
) ou en
France avec la pérennisation de l'état d'urgence.
Donc, il est tout à fait justifié de configurer des services en
.onion
. Mais si on veut vérifier que ces
services marchent bien ? Pour les services plus traditionnels
(mettons un site Web classique), on utilise un logiciel de supervision. Mettons qu'on utilise, comme moi,
Icinga (mais la technique expliquée ici
devrait marcher avec n'importe quel logiciel de supervision
compatible avec l'API de
Nagios). Le script de test des serveurs
HTTP est check_http,
qui peut se tester depuis la ligne de commande :
% /usr/lib/nagios/plugins/check_http -H www.elysee.fr HTTP OK: HTTP/1.1 200 OK - 684876 bytes in 0.791 second response time |time=0.791354s;;;0.000000;10.000000 size=684876B;;;0
Il est appelé régulièrement par Icinga pour tester le serveur, et
déclencher une alarme si quelque chose va mal. Ce script utilise
les bibliothèques « normales » du système, et ne connait pas le
TLD « spécial »
.onion
:
% /usr/lib/nagios/plugins/check_http -H sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion Name or service not known HTTP CRITICAL - Unable to open TCP socket
Si on fait tourner le client Tor sur sa machine, il exporte par
défaut une interface SOCKS (paramètres
SOCKSPort
et SOCKSPolicy
dans le fichier de configuration de Tor). Cela permet à certains
programmes (ici, curl) d'accéder aux
services en .onion
:
% curl -v sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion ... curl: (6) Could not resolve host: sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion
Et avec la bonne option (Tor écoute par défaut sur le port 9050) :
% curl -v --socks5-hostname localhost:9050 sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion ... * SOCKS5 communication to sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion:80 * SOCKS5 request granted. * Connected to localhost (127.0.0.1) port 9050 (#0) > GET / HTTP/1.1 > Host: sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 200 OK < Date: Fri, 22 Sep 2017 09:31:32 GMT < Server: Apache < Content-Type: text/html < { [3245 bytes data] 0 425k 0 3245 0 0 3245 0 0:02:14 0:00:01 0:02:13 2414<?xml version="1.0" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xml:lang="fr" lang="fr" xmlns="http://www.w3.org/1999/xhtml"> <head> ...
(Si vous avez un message d'erreur du genre « * connect to 127.0.0.1 port 9050 failed: Connection refused », c'est que le client Tor ne tourne pas sur votre machine - il ne s'agit pas du Tor Browser, mais d'un démon - ou bien qu'il écoute sur un autre port, vérifiez la configuration.)
Petit problème, le script check_http
ne
semble pas disposer d'une option SOCKS. Il faut donc utiliser une
autre méthode, le programme torsocks
qui redirige divers appels système pour que l'application parle à
SOCKS en croyant parler directement au serveur HTTP. Pour curl, on
aurait pu faire :
% torsocks curl -v sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion
On peut donc lancer le script de test des Monitoring Plugins :
% torsocks /usr/lib/nagios/plugins/check_http -H sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion HTTP OK: HTTP/1.1 200 OK - 436330 bytes in 3.649 second response time |time=3.648528s;;;0.000000;10.000000 size=436330B;;;0
Maintenant que le test marche en ligne de commande, il n'y a plus qu'à configurer Icinga pour l'utiliser. On écrit un petit script shell :
% cat /usr/local/lib/nagios/plugins/check_http_onion #!/bin/sh /usr/bin/torsocks /usr/lib/nagios/plugins/check_http $*
Et on dit à Icinga de l'utiliser. Dans
commands.conf
:
object CheckCommand "http-onion" { command = [ PluginContribDir + "/check_http_onion" ] arguments = { "-H" = { value = "$http_vhost$" description = "Host name argument for servers using host headers (virtual host)" } [On reprend verbatim les options de check_http, qui sont, sur une Debian, dans /usr/share/icinga2/include/command-plugins.conf] vars.http_address = "$check_address$" vars.http_ssl = false vars.http_sni = false vars.http_linespan = false vars.http_invertregex = false vars.check_ipv4 = "$http_ipv4$" vars.check_ipv6 = "$http_ipv6$" vars.http_link = false vars.http_verbose = false }
Et dans services.conf
:
apply Service for (http_vhost => config in host.vars.http_onion_vhosts) { import "generic-service" check_command = "http-onion" vars += config }
Avec ces définitions, je peux décider de superviser un service en
mettant une entrée dans le
vars.http_onion_hosts
de la machine :
object Host "serveur-secret" { import "generic-host" ... [Autres tests] vars.http_onion_vhosts["onion"] = { http_uri = "/" http_vhost = "sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion" http_ssl = false http_timeout = 15 } }
Ou bien en créant un host spécial :
object Host "internet-libre" { import "generic-host" check_command = "always_true" /* No ping in Tor */ vars.http_onion_vhosts["aeris"] = { http_uri = "/" http_vhost = "aerisryzdvrx7teq.onion" http_ssl = false http_timeout = 15 } vars.http_onion_vhosts["amaelle"] = { http_uri = "/" http_vhost = "ozawuyxtechnopol.onion" http_ssl = false http_timeout = 15 } }
Ainsi configuré, Icinga va régulièrement tester ces services via Tor, et prévenir de la manière habituelle :
Alors, à l'usage, est-ce que ça marche ? Oui, au sens où je suis bien prévenu lorsqu'il y a un problème et Icinga teste bien et enregistre l'historique des pannes. Moins bien au sens où cette supervision révèle que Tor n'est pas parfaitement fiable, en tout cas vu de mon ADSL : les pannes temporaires sont relativement fréquentes (ce qui est logique, la requête étant acheminée par plusieurs nœuds Tor parfois lointains, et pas toujours stables). Cela peut donc valoir la peine de configurer le logiciel de supervision pour être moins prompt à lever une alarme pour ces services. Disons en gros une ou deux pannes d'environ dix minutes par jour, ce qui ne serait pas acceptable pour un serveur Web « normal ».
Ce qui est amusant est que, parfois, pendant une panne, le Tor Browser depuis le même réseau, arrive à se connecter au service. C'est probablement parce que le Tor Browser et le démon Tor utilisé par Icinga ont établi des circuits Tor différents (passant par des nœuds différents), et qu'un seul des circuits déconne.
Mille mercis à Lunar pour m'avoir tout
expliqué patiemment (et, en plus, ça marche). Et merci aussi à
Amaelle et Aeris pour m'avoir permis d'utiliser leurs serveurs pour
les tests. Voici les trois serveurs en .onion
qui ont été utilisé (les liens ne marcheront que si vous regardez
cet article via Tor) :
Première rédaction de cet article le 3 octobre 2017
Dernière mise à jour le 4 octobre 2017
Le premier octobre 2017, le site d'information spécialisé dans la Corée du Nord, 38 North, a publié un excellent article sur la deuxième connexion Internet de la Corée du Nord. Où peut-on vérifier ce genre d'informations ?
L'article est un bon exemple d'OSINT (ou de ROSO ?), ne s'appuyant que sur des sources publiques, et sur l'analyse des experts de Dyn research (l'ancien Renesys, qui a publié son propre article, une lecture très recommandée). Mais, sur le sujet de la Corée du Nord, la propagande tourne à plein régime et énormément d'informations fausses sont diffusées, d'autant plus que la dictature délirante de Kim Jong-un ne permet pas de vérifications sur place. Si les fake news ne sont pas une nouveauté (le mensonge est aussi ancien que le langage), le caractère assez technique de l'information (« la Corée du Nord a désormais un deuxième lien Internet, via la Russie ») rend les vérifications plus difficiles, pour les gens non spécialisés.
Donc, l'article de 38 North dit qu'un opérateur Internet russe connecte désormais la RDPC, qui n'était auparavant connectée que par la Chine. Vu le caractère… spécial de la Corée du Nord, cette connexion n'est pas une pure affaire de business et a sans doute nécessité une approbation par Moscou. Comment la vérifier ?
Car c'est tout l'intérêt de l'OSINT : le but n'est pas uniquement de faire gagner du temps aux espions (au prix d'une certaine perte de glamour : lire le Wikipédia russophone est moins spectaculaire que de pénétrer dans une chambre forte au Kremlin, après avoir séduit une belle espionne russe). L'intérêt politique de l'OSINT est de permettre des vérifications indépendantes : si on n'est pas sûr de l'information, on peut refaire une partie du travail pour la recouper.
Donc, première chose, récolter quelques informations
techniques. Quelles sont les adresses IP de l'(unique) opérateur Internet
nord-coréen ? On peut le savoir en partant des serveurs de noms de leur
TLD, .kp
:
% dig NS kp ; <<>> DiG 9.10.3-P4-Debian <<>> NS kp ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44497 ;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 3 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;kp. IN NS ;; ANSWER SECTION: kp. 429731 IN NS ns3.kptc.kp. kp. 429731 IN NS ns1.kptc.kp. kp. 429731 IN NS ns2.kptc.kp. ;; ADDITIONAL SECTION: ns1.kptc.kp. 429731 IN A 175.45.176.15 ns2.kptc.kp. 429731 IN A 175.45.176.16 ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Tue Oct 03 11:53:28 CEST 2017 ;; MSG SIZE rcvd: 122
Un petit coup de whois pour vérifier :
% whois 175.45.176.15 % [whois.apnic.net] % Whois data copyright terms http://www.apnic.net/db/dbcopyright.html % Information related to '175.45.176.0 - 175.45.179.255' % Abuse contact for '175.45.176.0 - 175.45.179.255' is 'postmaster@star-co.net.kp' inetnum: 175.45.176.0 - 175.45.179.255 netname: STAR-KP descr: Ryugyong-dong descr: Potong-gang District country: KP org: ORG-SJVC1-AP admin-c: SJVC1-AP tech-c: SJVC1-AP status: ALLOCATED PORTABLE mnt-by: APNIC-HM mnt-lower: MAINT-STAR-KP mnt-routes: MAINT-STAR-KP ... organisation: ORG-SJVC1-AP org-name: Star Joint Venture Co. Ltd. country: KP address: Ryugyong-dong address: Potong-gang District phone: +850-2-3812321 fax-no: +850-2-3812100 e-mail: postmaster@star-co.net.kp ...
OK, on a bien trouvé Star JV, l'opérateur coréen. Quel est son numéro d'AS ? Il existe plein de moyens de le trouver, à partir de la base APNIC mais, ayant l'habitude du DNS, j'utilise le service DNS de RouteViews :
% dig +short TXT 0.176.45.175.aspath.routeviews.org "54728 6939 701 4837 131279" "175.45.176.0" "24"
Il nous apprend que le préfixe 175.45.176.0/24
est annoncé par l'AS 131279 (rappelez-vous
que les chemins d'AS se lisent de droite à gauche). Le second AS (rappelez-vous
que les chemins d'AS se lisent de droite à gauche), 4837, est celui
du fournisseur chinois habituel, China Unicom.
L'article de 38 North dit que cela a changé le dimanche 1
octobre. En effet, l'excellent service RIPE stat, quand on
l'interroge sur le routage de
175.45.176.0/24
, confirme une activité
importante :
(Vous pouvez aussi regarder sur le site original.) Cette activité indique qu'un changement a eu lieu, puis s'est propagé par BGP à tout l'Internet, déclenchant en cascade d'autres changements. (Les tables BGP sont publiques, l'Internet est très ouvert. Exercice : cherchez les autres préfixes annoncés par Star JV.)
Bon, en quoi consistait cette nouveauté BGP du dimanche matin ? On va se tourner vers le service de RouteViews qui archive les annonces BGP et les met ensuite à la disposition du public. Les fichiers sont au format MRT (RFC 6396), qui s'analyse avec le programme bgpdump. Et on connait l'heure approximative de l'annonce (dans l'article de 38 North et grâce à RIPE stat) donc on peut télécharger le bon fichier (il change tous les quarts d'heure, regardez le nom de fichier, au format YYYYMMDD) :
% wget http://archive.routeviews.org/bgpdata/2017.10/UPDATES/updates.20171001.0900.bz2 % bunzip2 updates.20171001.0900.bz2 % bgpdump updates.20171001.0900 > updates.20171001.0900.txt 2017-10-03 11:22:24 [info] logging to syslog %
On charge le updates.20171001.0900.txt
dans
un éditeur, on cherche "131279" (l'AS de Star JV) et on trouve
l'annonce cherchée :
TIME: 10/01/17 09:07:51 TYPE: BGP4MP/MESSAGE/Update FROM: 202.73.40.45 AS18106 TO: 128.223.51.102 AS6447 ORIGIN: INCOMPLETE ASPATH: 18106 20485 131279 NEXT_HOP: 202.73.40.45 COMMUNITY: 4635:2000 ANNOUNCE 175.45.176.0/24 175.45.178.0/24 175.45.179.0/24
Ici, l'annonce BGP émise par 131279 est transmise à l'AS 20485, le nouveau transitaire de Star JV. Qui est-il ?
% whois AS20485 % This is the RIPE Database query service. ... aut-num: AS20485 as-name: TRANSTELECOM org: ORG-CJSC19-RIPE descr: Moscow, Russia ... organisation: ORG-CJSC19-RIPE org-name: Closed Joint Stock Company TransTeleCom org-type: LIR address: Testovskayia str., 8 , enterance 3 address: 123317 address: Moscow address: RUSSIAN FEDERATION phone: +74957846670 fax-no: +74957846671 ...
Il s'agit donc bien d'un russe, TransTelecom.
Maintenant, est-ce que cet opérateur russe est vraiment
utilisé ? Certes, il reçoit les annonces BGP des Nord-Coréens et
les relaie (c'est ainsi qu'elles sont reçues par
RouteViews). Mais, comme disent les opérateurs réseaux,
« The data plane does not always follow the control
plane », ce qui veut dire que les paquets
ne passent pas forcément par le chemin des AS qui ont relayé
l'annonce. Testons depuis TransTelecom, grâce aux sondes RIPE Atlas, en
utilisant le
programme atlas-traceroute. On demande à trois sondes dans
l'AS de TransTelecom d'aller visiter 175.45.176.15
:
% atlas-traceroute --format --as 20485 --requested 3 175.45.176.15 Measurement #9412526 Traceroute 175.45.176.15 from AS #20485 uses 3 probes 3 probes reported Test #9412526 done at 2017-10-03T10:08:23Z From: 79.140.108.66 20485 TRANSTELECOM Moscow, Russia, RU Source address: 79.140.108.66 Probe ID: 1257 1 79.140.108.65 20485 TRANSTELECOM Moscow, Russia, RU [8.027, 1.63, 1.634] 2 217.150.57.50 20485 TRANSTELECOM Moscow, Russia, RU [5.27, 5.079, 5.072] 3 212.73.250.154 3356 LEVEL3 - Level 3 Communications, Inc., US [30.465, 30.464, 31.473] 4 ['*', '*', '*'] 5 ['*', '*', '*'] 6 204.255.173.53 701 UUNET - MCI Communications Services, Inc. d/b/a Verizon Business, US [132.757, 132.524, 132.329] 7 ['*', '*', '*'] 8 152.63.114.26 701 UUNET - MCI Communications Services, Inc. d/b/a Verizon Business, US [205.82, 205.77, 206.587] 9 ['*', '*', '*'] 10 219.158.101.197 4837 CHINA169-BACKBONE CHINA UNICOM China169 Backbone, CN [380.779, 380.674, 380.586] 11 219.158.3.130 4837 CHINA169-BACKBONE CHINA UNICOM China169 Backbone, CN [385.401, 385.712, 385.569] 12 ['*', '*', '*'] 13 219.158.39.42 4837 CHINA169-BACKBONE CHINA UNICOM China169 Backbone, CN [268.096, '*', '*'] ...
(Vous pouvez aussi regarder une représentation
graphique de cette mesure.)
Et on voit bien que, même depuis TransTelecom, fournisseur de
connectivité de Star JV, le trafic n'est pas envoyé via le
pont de l'Amitié, mais à un opérateur
états-unien, Level 3, qui transmettra
ensuite à China Unicom. (Exercice : faites pareil depuis d'autres
AS, comme 18106, pour voir qu'ils envoient bien à TransTelecom - ils
ont bien reçu l'annonce BGP - mais que celui-ci ne transmet pas
ensuite directement aux Coréens.) Le
lien tout neuf n'est donc pas utilisé pour l'instant. Ceci dit, la
situation change régulièrement. Le 4 octobre, il n'y avait plus
qu'un seul préfixe annoncé via TransTelecom, le
175.45.178.0/24
et lui est bien routé
directement vers Star JV :
% atlas-traceroute --format --as 20485 --requested 3 --proto icmp 175.45.178.1 Measurement #9675151 Traceroute 175.45.178.1 from AS #20485 uses 3 probes 3 probes reported Test #9675151 done at 2017-10-04T12:21:50Z From: 79.140.108.66 15774 TTK-RTL Retail, RU Source address: 79.140.108.66 Probe ID: 1257 1 79.140.108.65 15774 TTK-RTL Retail, RU [8.441, 1.936, 1.936] 2 217.150.57.50 20485 TRANSTELECOM Moscow, Russia, RU [5.432, 5.497, 5.441] 3 ['*', '*', '*'] 4 188.43.225.153 20485 TRANSTELECOM Moscow, Russia, RU [131.737, 132.065, 131.744] 5 ['*', '*', '*'] 6 175.45.178.1 131279 STAR-KP Ryugyong-dong, KP [130.062, 130.123, 129.827] ...
[Version graphique disponible
en ligne. Notez l'utilisation du protocole ICMP,
UDP étant bloqué quelque part.] Si votre
FAI reçoit l'annonce via TransTelecom, vous
pouvez aussi faire un traceroute depuis chez
vous (ici depuis Free)
(188.43.225.153
est TransTelecom) :
% traceroute -I 175.45.178.1 traceroute to 175.45.178.1 (175.45.178.1), 30 hops max, 60 byte packets ... 4 rke75-1-v900.intf.nra.proxad.net (78.254.255.42) 40.085 ms 40.079 ms 40.078 ms 5 cev75-1-v902.intf.nra.proxad.net (78.254.255.46) 40.070 ms 40.083 ms 40.084 ms 6 p16-6k-1-po12.intf.nra.proxad.net (78.254.255.50) 40.043 ms 25.274 ms 25.259 ms 7 bzn-crs16-1-be1024.intf.routers.proxad.net (212.27.56.149) 29.072 ms 23.159 ms 16.617 ms 8 194.149.165.206 (194.149.165.206) 31.358 ms 31.041 ms 31.054 ms 9 * * * 10 tenge10-1.br01.frf06.pccwbtn.net (63.218.232.41) 30.996 ms 30.990 ms 32.615 ms 11 63-218-233-6.static.pccwglobal.net (63.218.233.6) 32.616 ms 34.156 ms 34.162 ms 12 * * * 13 188.43.225.153 (188.43.225.153) 191.185 ms 191.210 ms 180.775 ms 14 * * * 15 175.45.178.1 (175.45.178.1) 185.670 ms 185.680 ms 186.084 ms
Peut-être le lien vers TransTelecom est-il un lien de secours, prévu pour faire face à d'éventuelles sanctions chinoises ? Ou peut-être tout simplement le nouveau lien est en cours de configuration et que la situation n'est pas tout à fait stable ?
Notez que, si les tables et les annonces BGP sont publiques (ainsi que l'utilisation des sondes RIPE Atlas), la capacité est bien plus difficile à connaitre et des affirmations comme quoi le nouveau lien représenterait « 60 % de la capacité Internet nord-coréenne » (dans cet article) sont probablement de la pure spéculation.
Merci à Dyn pour des explications supplémentaires.
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : R. Bush (Internet Initiative
Japan)
Réalisé dans le cadre du groupe de travail IETF sidr
Première rédaction de cet article le 28 septembre 2017
Le mécanisme de sécurisation de BGP nommé BGPsec, normalisé dans le RFC 8205 n'a pas encore de déploiement important. Mais le bon sens, l'expérience d'autres mécanismes de sécurité, et les expérimentations déjà faites avec BGPsec permettent de dégager quelques bonnes pratiques opérationnelles, que ce RFC décrit. Le RFC est court : on manque encore de pratique pour être sûr de toutes les bonnes pratiques.
Bien sûr, ces pratiques évolueront. À l'heure de la publication de ce RFC, il y a encore très peu d'expérience concrète avec BGPsec. Au fait, c'est quoi, BGPsec ? Le protocole BGP, la norme pour les annonces de routes sur l'Internet, souffre d'une faiblesse de sécurité : par défaut, n'importe qui peut annoncer n'importe quelle route, même vers un préfixe IP qu'il ne contrôle pas. Il existe plusieurs systèmes pour limiter les dégâts : les IRR, les systèmes d'alarme, et, plus récemment, les ROA (Route Origin Authorizations, cf. RFC 6482). BGPsec est la technique la plus efficace, mais aussi la plus difficile à déployer. Comme les ROA, il repose sur la RPKI. Contrairement aux ROA, il ne valide pas que l'AS d'origine mais tout le chemin d'AS, par une signature de chaque AS. Il introduit donc la cryptographie dans le routage, ce qui causera sans doute quelques perturbations.
Donc, avant de lire ce RFC 8207, c'est sans doute une bonne idée de lire au moins les RFC 4271 sur BGP et RFC 6480 sur la RPKI. Comme BGPsec dépend de la RPKI, il est également bon de lire le RFC 7115, qui parle des questions opérationnelles des ROA (qui reposent également sur la RPKI). Et enfin il faut lire le RFC sur le protocole BGPsec, le RFC 8205.
Avec BGPsec, chaque routeur de bordure de l'AS va avoir une paire {clé privée, clé publique}. On peut avoir une seule paire pour tous ses routeurs (et donc publier un seul certificat dans la RPKI) ou bien une paire par routeur (section 4 du RFC). La première solution est plus simple mais, si un routeur est piraté, il faudra changer la configuration de tous les autres routeurs. Le RFC suggère aussi de pré-publier des clés de secours dans la RPKI, pour qu'elles soient bien distribuées partout le jour où on en aura besoin.
Tous vos routeurs ne sont pas censés parler BGPsec, seulement ceux au bord de l'AS, face au grand monde inconnu du méchant Internet (section 5). Si vous n'avez pas le temps ou l'envie d'activer BGPsec sur tous vos routeurs, commencez par ceux qui font face aux « maillons faibles », les pairs ou les transitaires que vous suspectez d'être le plus capable d'envoyer ou de relayer des annonces erronées ou mensongères. Évidemment, ce sont aussi ceux qui ont le moins de chance de déployer BGPsec…
Si vous ne vous contentez pas de vérifier les signatures BGPsec, mais que vous agissez sur la base de cette vérification (en ignorant les annonces invalides), attendez-vous, comme avec toute technique de sécurité BGP, à ce que le trafic se déplace, par exemple vers d'autres pairs. Un pré-requis au déploiement de BGPsec est donc sans doute un bon système de métrologie.
Attention aux limites de BGPsec : la signature couvre le chemin d'AS, pas les communautés (RFC 1997). Ne vous fiez donc pas à elles.
Si BGPsec vous semble bien compliqué à déployer, et que vous hésitez devant le travail que cela représente, la section 6 vous rassurera : la majorité des AS sont des stubs, ils ne fournissent de connectivité à personne et n'ont donc pas forcément besoin de valider les annonces. Par contre, vous pouvez demander à vos transitaires s'ils utilisent BGPsec (début 2017, la réponse était forcément non).
Les lect·eur·rice·s subtil·e·s ont peut-être noté une
différence avec les ROA. Alors que l'état de la validation ROA
(RFC 6811)
donne un résultat ternaire (pas de ROA, un ROA et valide, un ROA
et invalide), BGPsec est binaire (annonce valide, ou
invalide). C'est parce que les chemins d'AS sécurisés BGPsec_path
ne
sont propagés qu'entre routeurs BGPsec, qui signent et, a priori,
valident. Si on reçoit une annonce BGPsec, c'est forcément que
tout le chemin d'AS gérait BGPsec. L'équivalent du « pas de ROA »
est donc une annonce BGP traditionnelle, avec
AS_PATH
mais sans
BGPsec_path
.
Quant à la décision qu'on prend lorsqu'une annonce est
invalide, c'est une décision locale. Ni
l'IETF, ni l'ICANN,
ni le CSA ne peuvent prendre cette décision
à votre place. Notre RFC recommande (mais c'est juste une
recommandation) de jeter les annonces BGPsec invalides. Certains
opérateurs peuvent se dire « je vais accepter les annonces
invalides, juste leur mettre une préférence plus basse » mais cela
peut ne pas suffire à empêcher leur usage même lorsqu'il existe
des alternatives. Imaginons un routeur qui reçoive une annonce
valide pour 10.0.0.0/16
et une invalide pour
10.0.666.0/24
(oui, je sais que ce n'est pas
une adresse IPv4 correcte, mais c'est le RFC qui a commencé, ce
n'est pas moi). Le routeur installe les deux routes, avec une
préférence basse pour 10.0.666.0/24
. Mais les
préférences ne jouent que s'il s'agit de préfixes identiques. Ici,
lorsqu'il faudra transmettre un paquet, c'est la route la plus
spécifique qui gagnera, donc la mauvaise (666 = chiffre
de la Bête). Notez que l'article « Are We There Yet?
On RPKI’s Deployment and Security » contient plusieurs autres exemples où le rejet d'une annonce
invalide a des conséquences surprenantes.
La même section 7 couvre aussi le cas des serveurs de routes, qui est un peu particulier car ces routeurs n'insèrent pas leur propre AS dans le chemin, ils sont censés être transparents.
Enfin, la section 8 traite de quelques points divers, comme le rappel que la RPKI (comme d'ailleurs le DNS) n'est pas cohérente en permanence, car pas transmise de manière synchrone partout. Par exemple, si vous venez d'obtenir un certificat pour un AS, ne l'utilisez pas tout de suite : les annonces BGP se propagent plus vite que les changements dans la RPKI et vos annonces signées risquent donc d'être considérées comme invalides par certains routeurs.
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : R. Bush (Internet Initiative
Japan), R. Austein (Dragon Research
Labs)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sidr
Première rédaction de cet article le 28 septembre 2017
Le protocole décrit dans ce RFC fait partie de la grande famille des RFC sur la RPKI, un ensemble de mécanismes permettant de sécuriser le routage sur l'Internet. Il traite un problème bien particulier : comme la validation des annonces de route, potentiellement complexe en cryptographie, se fait typiquement sur une machine spécialisée, le cache validateur, comment communiquer le résultat de ces validations au routeur, qui va devoir accepter ou rejeter des routes ? C'est le rôle du protocole RTR (RPKI/Router Protocol), un protocole très simple. Ce RFC décrit une nouvelle version de RTR, la version 1, la première (version 0) était dans le RFC 6810.
L'opérateur Internet a donc deux acteurs qui font du RTR entre eux, le cache validateur qui récupère les ROA (Route Origin Authorization, cf. RFC 6482) par rsync, les valide cryptographiquement et garde en mémoire le résultat de cette validation (pour des exemples d'utilisation de deux programmes mettant en œuvre cette fonction, voir mon article sur les logiciels de la RPKI), et le routeur (Cisco, Juniper, Unix avec Quagga, etc). Ce dernier joue un rôle critique et on ne souhaite pas trop charger la barque en lui imposant les opérations assez lourdes du validateur. Dans la plupart des déploiements de la RPKI, le routeur ne fait donc que récupérer des résultats simples (« ce préfixe IP peut être annoncé par cet AS »). Le jeu de données que connait le cache validateur a un numéro de série, qui est incrémenté dès qu'il y a une modification (RFC 1982). Le cache peut donc facilement savoir si un de ses clients routeur est à jour en comparant son numéro de série avec celui du routeur. Le numéro de série est spécifique à un cache donné, il n'a pas de signification globale. Le routeur ne validant pas, le cache qu'il utilise doit donc être une machine de confiance.
Un autre acteur, externe à l'opérateur, est la RPKI, l'ensemble des informations de routage publiées (RFC 6481), et qui sont transportées en général en rsync et validées par les signatures cryptographiques qu'elles portent.
Le routeur va ouvrir une connexion avec un ou plusieurs caches et transmettre le numéro de série de ses données. Le cache va lui envoyer les données plus récentes que ce numéro (ou bien rien, si le routeur est à jour). Le cache dispose aussi d'un mécanisme de notification pour dire à tous les routeurs connectés « j'ai du nouveau ». La section 10 décrit plus en détail le processus avec plusieurs caches.
La section 5 décrit le format des paquets : chacun, outre le classique champ version (1 dans ce RFC, c'était 0 dans le RFC précédent), a un type, indiqué par un entier de 8 bits (requête, réponse, etc), le numéro de série de l'envoyeur et un identifiant de session, qui identifie le serveur du cache (pour détecter une remise à zéro d'un cache, par exemple), la longueur du préfixe annoncé et bien sûr le préfixe lui-même.
Les types de paquets les plus utiles (la liste complète est dans un registre IANA) :
Le dialogue typique entre un routeur et un cache validateur est décrit en section 8 (le RFC détaille aussi le dialogue de démarrage, ici, je ne montre que celui de fonctionnement régulier) :
À noter que puisqu'il y a désormais deux versions de RTR dans la nature, il a fallu mettre en place un système de négociation, décrit dans la section 7. Lorsqu'un cache/validateur connait la version 1 (celle de notre nouveau RFC), et reçoit une requête de version 0 du routeur, il doit se rabattre sur la version 0 (s'il la connait, et veut bien la parler), ou bien envoyer un message d'erreur (code 4 « Unsupported Protocol Version »).
Si c'est l'inverse (routeur récent parlant à un vieux cache/validateur), deux cas, qui dépendent de la politique du validateur. S'il répond avec une erreur, cela peut être intéressant de réessayer avec le protocole 0, si le routeur le connait. Si, par contre, le validateur répond avec des messages de version 0, le routeur peut décider de continuer en version 0.
Les codes d'erreur possibles sont décrits dans la section 12 et font l'objet d'un registre IANA. On y trouve par exemple 0 (données corrompues), 1 (erreur interne dans le client ou le serveur), 2 (pas de nouvelles données ; ce n'est pas à proprement parler une erreur).
La question du transport des données entre le cache et le routeur est traitée dans la section 9. Le principal problème est le suivant : comme le but de l'opération RPKI+ROA est d'augmenter la sécurité du routage, il faut que chaque maillon de la chaîne soit sécurisé. Il ne servirait pas à grand'chose de déployer une telle usine à gaz de validation si la liaison entre le routeur et le cache permettait à un intermédiaire de changer les données en cours de route. Il était donc tentant de normaliser une technique de sécurité particulière pour le transport, comme par exemple TLS. Malheureusement, il n'existe pas à l'heure actuelle de technique accessible sur tous les routeurs. L'IETF ayant toujours soin de normaliser des protocoles réalistes, qui puissent être mis en œuvre et déployés, le choix a été fait de ne pas imposer une technique de sécurisation particulière. L'interopérabilité en souffre (un cache et un routeur peuvent ne pas avoir de protocole de sécurité en commun) mais c'était la seule option réaliste. (J'ajoute que, si le but de la séparation du routeur et du validateur était de dispenser ce dernier des calculs cryptographiques, il ne serait pas logique de lui imposer ensuite une session protégée cryptographiquement avec le validateur.)
Le RFC se contente donc de donner une préférence à AO (TCP Authentication Option, cf. RFC 5925) en signalant qu'elle sera la technique préférée, dès lors qu'elle sera disponible sur tous les routeurs (ce qui est très loin d'être le cas en 2017, les progrès sont faibles depuis le précédent RFC sur RTR). Les algorithmes du RFC 5926 doivent être acceptés.
En attendant AO, et pour s'assurer qu'il y aura au moins un protocole commun, le RFC spécifie que routeurs et caches doivent pouvoir se parler en utilisant du TCP nu, sans sécurité, vers le port 323. Dans ce cas, le RFC spécifie que routeur et cache validateur doivent être sur le même réseau (idéalement, sur le même segment, cf. section 13) et que des mécanismes, par exemple de contrôle d'accès physiques, empêchent les méchants de jouer avec ce réseau.
Au cas où AO ne soit pas disponible (la quasi-totalité des routeurs
aujourd'hui) et où le transport non sécurisé soit jugé nettement trop
faible, le RFC présente plusieurs transports sécurisés possibles. Par
exemple, SSH (RFC 4252), avec une sécurisation des clés
obtenue par un moyen externe (entrée manuelle des clés, par
exemple). Le sous-système SSH à utiliser est nommé
rpki-rtr
. Notez que, si SSH n'a pas été rendu
obligatoire, alors que quasiment tous les routeurs ont un programme
SSH, c'est parce que ce programme n'est pas toujours disponible sous
forme d'une bibliothèque, accessible aux
applications comme RTR.
Autre alternative de sécurisation évidente,
TLS. Le RFC impose une authentification par un
certificat client, comportant l'extension
subjectAltName
du RFC 5280,
avec une adresse IP (celle du routeur qui se connecte, et que
le serveur RTR, la cache validateur, doit vérifier). Le client RTR,
lui (le routeur), utilisera le nom de
domaine du serveur RTR (le cache) comme entrée pour l'authentification (cf. RFC 6125). N'importe quelle
AC peut être utilisée mais notre RFC estime
qu'en pratique, les opérateurs créeront leur propre AC, et mettront
son certificat dans tous leurs routeurs.
Notez que ce problème de sécurité ne concerne que le transport entre le routeur et le cache validateur. Entre les caches, ou bien entre les caches et les serveurs publics de la RPKI, il n'y a pas besoin d'une forte sécurité, l'intégrité des données est assurée par la validation cryptographique des signatures sur les objets.
Ce RFC spécifie un protocole, pas une politique. Il pourra y avoir plusieurs façons de déployer RTR. La section 11 donne un exemple partiel de la variété des scénarios de déploiement, notamment :
Pour limiter la charge sur les serveurs rsync de la RPKI (voir RFC 6480), il est recommandé d'organiser les caches de manière hiérarchique, avec peu de caches tapant sur les dépôts rsync publics, et les autres caches se nourrissant (et pas tous en même temps) à partir de ces caches ayant un accès public.
Bien que ce protocole RTR ait été conçu dans le cadre de la RPKI, il peut parfaitement être utilisé avec d'autres mécanismes de sécurisation du routage, puisque RTR se contente de distribuer des autorisations, indépendemment de la manière dont elles ont été obtenues.
Et puis un petit point opérationnel : sur le validateur/cache, n'oubliez pas de bien synchroniser l'horloge, puisqu'il faudra vérifier des certificats, qui ont une date d'expiration…
Quels sont les changements par rapport au RFC original, le RFC 6810, qui normalisait la version 0 de RTR ? Ces changements sont peu nombreux, mais, du fait du changement de version, les versions 0 et 1 sont incompatibles. La section 1.2 résume ce qui a été modifié entre les deux RFC :
Aujourd'hui, qui met en œuvre RTR ? Il existe en logiciel libre une bibliothèque en C, RTRlib. Elle a fait l'objet d'un bon article de présentation (avec schéma pour expliquer RTR). Elle peut tourner sur TCP nu mais aussi sur SSH. RTRlib permet d'écrire, très simplement, des clients RTR en C, pour déboguer un cache/validateur, ou bien pour extraire des statistiques. Un client simple en ligne de commande est fourni avec, rtrclient. Il peut servir d'exemple (son code est court et très compréhensible) mais aussi d'outil de test simple. rtrclient se connecte et affiche simplement les préfixes reçus. Voici (avec une version légèrement modifiée pour afficher la date) son comportement (en face, le RPKI Validator du RIPE-NCC, dont il faut noter qu'il ne marche pas avec Java 9, il faut reculer vers une version plus vieille, et qu'il ne gère pas encore la version 1 de RTR) :
% rtrclient tcp localhost 8282 ... 2012-05-14T19:27:42Z + 195.159.0.0 16-16 5381 2012-05-14T19:27:42Z + 193.247.205.0 24-24 15623 2012-05-14T19:27:42Z + 37.130.128.0 20-24 51906 2012-05-14T19:27:42Z + 2001:1388:: 32-32 6147 2012-05-14T19:27:42Z + 2001:67c:2544:: 48-48 44574 2012-05-14T19:27:42Z + 178.252.36.0 22-22 6714 2012-05-14T19:27:42Z + 217.67.224.0 19-24 16131 2012-05-14T19:27:42Z + 77.233.224.0 19-19 31027 2012-05-14T19:27:42Z + 46.226.56.0 21-21 5524 2012-05-14T19:27:42Z + 193.135.240.0 21-24 559 2012-05-14T19:27:42Z + 193.247.95.0 24-24 31592 ...
Autre bibliothèque pour développer des clients RTR, cette fois en Go, ma GoRTR. Le programmeur doit fournir une fonction de traitement des données, qui sera appelée chaque fois qu'il y aura du nouveau. GoRTR est fournie avec deux programmes d'exemple, un qui affiche simplement les préfixes (comme rtrclient plus haut), l'autre qui les stocke dans une base de données, pour étude ultérieure. Par exemple, on voit ici dans cette base de données que la majorité des préfixes annoncés autorisent une longueur de préfixe égale à la leur (pas de tolérance) :
essais=> SELECT count(id) FROM Prefixes WHERE serialno=(SELECT max(serialno) FROM Prefixes) AND maxlength=masklen(prefix); count ------- 2586 (1 row) essais=> SELECT count(id) FROM Prefixes WHERE serialno=(SELECT max(serialno) FROM Prefixes) AND maxlength>masklen(prefix); count ------- 1110 (1 row)
Et voici une partie de évenements d'une session RTR, stockée dans cette base:
tests=> select * from events; id | time | server | event | serialno ----+----------------------------+---------------------------------+---------------------------------+---------- ... 12 | 2017-06-24 13:31:01.445709 | rpki-validator.realmv6.org:8282 | (Temporary) End of Data | 33099 13 | 2017-06-24 13:31:01.447959 | rpki-validator.realmv6.org:8282 | Cache Response, session is 4911 | 33099 14 | 2017-06-24 13:31:25.932909 | rpki-validator.realmv6.org:8282 | (Temporary) End of Data | 33099 15 | 2017-06-24 13:31:38.64284 | rpki-validator.realmv6.org:8282 | Serial Notify #33099 -> #33100 | 33099 16 | 2017-06-24 13:31:38.741937 | rpki-validator.realmv6.org:8282 | Cache reset | 33099 17 | 2017-06-24 13:31:38.899752 | rpki-validator.realmv6.org:8282 | Cache Response, session is 4911 | 33099 18 | 2017-06-24 13:32:03.072801 | rpki-validator.realmv6.org:8282 | (Temporary) End of Data | 33100 ...
Notez que GoRTR gère désormais les deux versions du protocole mais ne connait pas la négociation de version de la section 7 du RFC : il faut indiquer explicitement quelle version on utilise.
Autrement, Cisco et Juniper ont tous les deux annoncé que leurs routeurs avaient un client RTR, et qu'il a passé des tests d'interopérabilité avec trois mises en œuvre du serveur (celles de BBN, du RIPE et de l'ISC). Ces tests d'interopérabilité ont été documentés dans le RFC 7128. Quant aux serveurs, un exemple est fourni par le logiciel RPKI du RIPE-NCC qui fournit un serveur RTR sur le port 8282 par défaut.
En 2013, un client RTR récupèrait 3696 préfixes (ce qui est plus que le nombre de ROA puisqu'un ROA peut comporter plusieurs préfixes). C'est encore très loin de la taille de la table de routage globale et il reste donc à voir si l'infrastructure suivra. (Sur ma station de travail, le validateur, écrit en Java, rame sérieusement.)
Pour les curieux, pourquoi est-ce que l'IETF n'a pas utilisé Netconf (RFC 6241) plutôt que de créer un nouveau protocole ? Les explications sont dans une discussion en décembre 2011. Le fond de la discussion était « les informations de la RPKI sont des données ou de la configuration » ? La validation est relativement statique mais reste quand même plus proche (fréquence de mise à jour, notamment) des données que de la configuration.
Si vous voulez tester un client RTR, il existe un serveur public
rpki-validator.realmv6.org:8282
. Voici le genre de choses
qu'affiche rtrclient (test fait avec un vieux serveur, qui n'est plus disponible) :
% rtrclient tcp rtr-test.bbn.com 12712 ... 2012-05-14T19:36:27Z + 236.198.160.184 18-24 4292108787 2012-05-14T19:36:27Z + 9144:8d7d:89b6:e3b8:dff1:dc2b:d864:d49d 105-124 4292268291 2012-05-14T19:36:27Z + 204.108.12.160 8-29 4292339151 2012-05-14T19:36:27Z + 165.13.118.106 27-28 4293698907 2012-05-14T19:36:27Z + 646:938e:20d7:4db3:dafb:6844:f58c:82d5 8-82 4294213839 2012-05-14T19:36:27Z + fd47:efa8:e209:6c35:5e96:50f7:4359:35ba 20-31 4294900047 ...
Question mise en œuvre, il y a apparemment plusieurs logiciels de validateurs/cache qui mettent en œuvre cette version 1, mais je n'ai pas trouvé lesquels. (Le RPKI Validator du RIPE-NCC ne le fait pas, par exemple, ni le serveur public indiqué plus haut, tous les deux ne connaissent que la version 0.)
Wireshark ne semble pas décoder ce
protocole. Si vous voulez vous y mettre, un
pcap est disponible en rtr.pcap
.
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : M. Lepinski (NCF), K. Sriram
(NIST)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sidr
Première rédaction de cet article le 28 septembre 2017
Ce RFC s'inscrit dans la longue liste des efforts de normalisation d'une solution de sécurité pour BGP. Actuellement, il est trop facile de détourner le routage Internet via BGP (cf. mon article « La longue marche de la sécurité du routage Internet »). Le RFC 6482 introduisait une solution partielle, les ROA (Route Origin Authorizations). S'appuyant sur une infrastructure de clés publiques et de certificats prouvant que vous êtes détenteur légitime d'une ressource Internet (comme un numéro d'AS ou bien un préfixe IP), infrastructure nommée la RPKI (Resource Public Key Infrastructure), ces ROA permettent de valider l'origine d'une annonce BGP, c'est-à-dire le premier AS du chemin. C'est insuffisant pour régler tous les cas. Ainsi, lors de la fuite de Telekom Malaysia en juin 2015, les annonces avaient une origine normale. Avec les ROA, on ne détectait donc pas le problème. D'où cette nouvelle étape, BGPsec. Il s'agit cette fois de valider le chemin d'AS complet de l'annonce BGP, chaque routeur intermédiaire signant cryptographiquement l'annonce avant de la transmettre. BGPsec permet donc de détecter bien plus d'attaques, mais il est aussi bien plus lourd à déployer.
Donc, pour résumer BGPsec en une phrase : un nouvel attribut BGP
est créé, BGPsec_Path
, il est transporté dans les
annonces de route BGP, il offre la possibilité de vérifier, via les
signatures, que chaque AS listé dans ce
chemin a bien autorisé l'annonce. BGPsec ne remplace pas les ROA, il
les complète (et, comme elles, il s'appuie sur la
RPKI). Deux bonnes lectures : le modèle de
menace de BGPsec, dans le RFC 7132, et le
cahier des charges de BGPsec, dans le RFC 7353.
Le reste, ce n'est que du détail technique. Contrairement aux ROA, qui sont transportés en dehors de BGP, BGPsec est une modification du protocole BGP. Deux routeurs qui font du BGPsec doivent donc le négocier lors de l'établissement de la session (section 2 de notre RFC). Cela se fait par le mécanisme des capacités du RFC 5492. La capacité annonçant qu'on sait faire du BGPsec est BGPsec Capability, code n° 7. Notez que cette capacité dépend de celle de gérer des AS de quatre octets (normalisée dans le RFC 6793).
Le nouvel attribut est décrit en section 3. (Le concept
d'attributs BGP est dans la section 5 du RFC 4271.) Les annonces BGP avaient
traditionnellement un attribut nommé AS_PATH
, qui
indiquait le chemin d'AS suivi par l'annonce. (Deux choses
importantes à se rappeler : lorsqu'on écrit un chemin d'AS, il se lit
de droite à gauche, et vous n'avez aucune garantie que les
paquets IP suivront effectivement ce chemin « the data plane does not always follow
the control plane ».)
Le nouvel attribut remplace
AS_PATH
. Il se nomme
BGPsec_path (code 33) et est optionnel et non-transitif,
ce qui veut dire qu'il ne sera pas passé tel quel aux pairs. Au
contraire, le routeur est supposé créer un nouvel attribut
BGPsec_path
, ajoutant son AS
(signé) et celui de l'AS à qui il transmet l'annonce. On
n'envoie une annonce portant cet attribut à un pair que si celui-ci a
signalé sa capacité à gérer BGPsec. (Sinon, on lui envoie un
AS_PATH
ordinaire, non sécurisé, formé en
extrayant les AS du BGPsec_path
, cf. section 4.4.)
La signature contient entre autres un identifiant de l'algorithme utilisé. La liste des algorithmes possibles est dans un registre IANA (RFC 8208).
Pour pouvoir mettre sa signature dans le
BGPsec_path
, le routeur doit évidemment disposer
d'une clé
privée. La clé publique correspondante doit être dans un
certificat de la RPKI, certificat valable pour
l'AS qui signe.
Un routeur qui transmet une annonce à un pair BGPsec ne garantit pas forcément qu'il a validé le chemin sécurisé. (Même s'il le garantissait, pourquoi lui faire confiance ?) C'est à chaque routeur de vérifier l'intégrité et l'authenticité du chemin d'AS (section 5 du RFC).
C'est dans cette section 5 qu'est le cœur du RFC, les détails de la validation d'un chemin d'AS. Cette validation peut être faite par le routeur, ou bien par une machine spécialisée dans la validation, avec laquelle le routeur communique via le protocole RTR (RPKI To Router protocol, cf. RFC 8210). Bien sûr, si on valide, c'est sans doute pour que la validation ait des conséquences sur la sélection de routes, mais notre RFC n'impose pas de politique particulière : ce que l'on fait avec les annonces BGPsec mal signées est une décision locale, par chaque routeur (et peut-être, sur un routeur donné, différente pour chaque pair BGP).
L'algorithme de validation est simple :
BGPsec_path
.À ce stade, on peut déjà rejeter les annonces qui ont échoué à l'un de ces tests, et les traiter comme signifiant un retrait de la route citée (RFC 7606). Ensuite, on passe aux vérifications cryptographiques :
Notez qu'on ne négocie pas les algorithmes cryptographiques au moment de l'établissement de la session BGP, comme cela se fait, par exemple, en TLS. En effet, il ne suffit pas de connaitre l'algorithme de ses pairs, il faut valider tout le chemin, y compris des AS lointains. C'est pour cela que le RFC 8208 liste des algorithmes obligatoires, que tout le monde est censé connaître (actuellement ECDSA avec la courbe P-256 et SHA-256).
On note que l'attribut BGPsec_path
avec ses
signatures n'est « transmis » qu'aux routeurs qui comprennent
BGPsec (pour les autres, on envoie un AS_PATH
classique). Au début du déploiement, ces attributs ne survivront donc pas
longtemps, et les îlots BGPsec seront donc rares (section 7 du
RFC).
Notez que ce protocole est en développement depuis plus de six ans, et qu'il s'est parfois nommé PathSec dans le passé.
BGPsec est apparemment mis en œuvre au moins dans BIRD et dans Quagga (mais pas dans les versions officielles).
Quelques bonnes lectures sur BGPsec :
Et merci à Guillaume Lucas pour m'avoir rappelé que BGPsec est compliqué et que je suis loin de le maitriser.
Première rédaction de cet article le 26 septembre 2017
Le 20 juin (oui, je suis en retard), la nouvelle version d'Unicode est sortie, la 10.0. Une description officielle des principaux changements est disponible mais voici ceux qui m'ont intéressé particulièrement. (Il n'y a pas de changement radical.)
Pour explorer plus facilement la grande base Unicode, j'utilise un programme qui la convertit en SQL et permet ensuite de faire des analyses variées. Faisons quelques requêtes SQL :
ucd=> SELECT count(*) AS Total FROM Characters; total -------- 136755
Combien caractères sont arrivés avec la version 10 ?
ucd=> SELECT version,count(version) FROM Characters GROUP BY version ORDER BY version::float; ... 8.0 | 7716 9.0 | 7500 10.0 | 8518
8 518 nouveaux. Lesquels ?
ucd=> SELECT To_U(codepoint) AS Codepoint, name FROM Characters WHERE version='10.0'; codepoint | name -----------+---------------------------------------------------------------------------- ... U+20BF | BITCOIN SIGN ... U+11A00 | ZANABAZAR SQUARE LETTER A ... U+11D01 | MASARAM GONDI LETTER AA ... U+1F6F8 | FLYING SAUCER ... U+1F96A | SANDWICH
Le premier cité va évidemment faire plaisir aux possesseurs de bitcoins. Mais on trouve aussi quatre écritures entièrement nouvelles comme le zanabazar ou le masaram gondi cités en exemple plus haut. Et il y a bien sûr l'habituel lot d'emojis pour faire rire les réseaux sociaux. Tiens, d'ailleurs, combien de caractères sont des symboles (il n'y a pas que les emojis parmi eux, mais Unicode n'a pas de catégorie « emoji ») :
ucd=> SELECT count(*) FROM Characters WHERE category IN ('Sm', 'Sc', 'Sk', 'So'); count ------- 6978
Ou, en plus détaillé, et avec les noms longs des catégories :
ucd=> SELECT description,count(category) FROM Characters,Categories WHERE Categories.name = Characters.category AND category IN ('Sm', 'Sc', 'Sk', 'So') GROUP BY category, description; description | count -----------------+------- Currency_Symbol | 54 Other_Symbol | 5855 Modifier_Symbol | 121 Math_Symbol | 948 (4 rows)
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : N. Cam-Winget (Cisco), L. Lorenzin (Pulse Secure)
Pour information
Réalisé dans le cadre du groupe de travail IETF sacm
Première rédaction de cet article le 25 septembre 2017
Le groupe de travail SACM de l'IETF bosse sur une architecture, un modèle de données, et des protocoles pour suivre l'état de la sécurité de ses machines. Ceci est son deuxième RFC, qui vise à définir le cahier des charges de la future solution.
Le cœur de cette idée est l'évaluation de la sécurité sur chaque machine du réseau. Il faut la déterminer, et acheminer le résultat jusqu'aux responsables de la sécurité. Cela nécessite donc un modèle de données (comment on décrit « la sécurité d'une machine » ?) puis un protocole permettant de gérer un très grand nombre de machines. Parmi les informations à récolter sur chaque machine, on trouvera évidemment la liste des logiciels installés, les logiciels serveurs en activité qui écoutent sur le réseau, l'état de la mise à jour (« pas pu joindre le dépôt officiel de logiciels depuis N jours »), etc. Le cahier des charges décrit dans ce RFC part des scénarios d'usage du RFC 7632. Ce sont souvent de grandes généralités. On est loin de mécanismes concrets.
La section 2 forme l'essentiel de ce RFC, avec la liste des exigences du cahier des charges. Je ne vais en citer que quelques unes. La première, nommée G-001 (« G » préfixe les exigences générales sur l'ensemble du système SACM), demande que toute la solution SACM soit extensible, pour les futurs besoins. G-003 est la possibilité de passage à l'échelle. Par exemple, les messages échangés peuvent aller de quelques lignes à, peut-être plusieurs gigaoctets si on fait une analyse détaillée d'une machine. Et il peut y avoir beaucoup de machine avec des échanges fréquents (le RFC n'est pas plus précis sur les chiffres).
Les données traitées sont évidemment sensibles, et G-006 demande que la solution permette évidemment de préserver la confidentialité des données.
Les exigences sur l'architecture de la future solution sont préfixées de « ARCH ». Ainsi, ARCH-009 insiste sur l'importance d'une bonne synchronisation temporelle de toutes les machines. Un journal des connexions/déconnexions des utilisateurs, par exemple, n'aurait guère d'intérêt si l'horloge qui sert à l'estampiller n'est pas exacte.
D'autres sections décrivent les exigences pour le modèle de données et pour les protocoles, je vous laisse les découvrir.
Date de publication du RFC : Septembre 2015
Auteur(s) du RFC : D. Waltermire, D. Harrington
Pour information
Première rédaction de cet article le 25 septembre 2017
La sécurité est évidemment une préoccupation courante quand on gère un réseau (« enterprise network » dit ce document, mais cela s'applique à n'importe quel réseau, capitaliste ou pas). Parmi les problèmes, déterminer l'état de la sécurité de chaque machine du réseau (son logiciel est à jour ? Pas de RAT installé ?) est un des plus importants. Ce RFC décrit des scénarios où on cherche à analyser cet état de la sécurité.
Disons-le tout de suite, une grande partie du RFC est du jargon « process » (quand on remplace l'intelligence par la paperasserie). Si vous n'êtes pas manager chargé d'obtenir une certification ISO 27001, cela ne vous intéressera guère. La seule partie utile est, dans section 2.2 du RFC, quelques scénarios intéressants.
J'ai bien aimé le scénario de la section 2.2.5, où un groupe de chercheurs (ah, justement, ce n'est plus du « enterprise network ») travaille dans la station polaire Zebra (oui, c'est une référence au film) et doit maintenir à jour ses ordinateurs, malgré une liaison Internet intermittente, chère (et la recherche publique n'a jamais d'argent) à faible capacité et énorme latence. Non seulement les conditions sont très dures, mais ils doivent en outre se plier à la « conformité » (compliance), le buzz word à la mode. Une partie du temps de recherche sera donc consommé à suivre les process.
Première rédaction de cet article le 24 septembre 2017
Dernière mise à jour le 9 octobre 2020
Petit service juste pour s'amuser : mettre l'ensemble des codes postaux français dans le DNS avec pour chaque code, la ou les communes concernées, leur longitude et leur latitude.
À quoi ça sert ? Pas à grand'chose, je voulais juste
illustrer l'utilisation de deux types d'enregistrements DNS
peu connus, LOC
(RFC 1876) et URI
(RFC 7553). (Au passage, si vous lisez que le DNS sert à
traduire des noms en adresses IP, allez lire autre chose :
c'est faux, ou, en tout cas, très réducteur.) Et traiter un peu de
JSON en prévision de la prochaine
sortie du nouveau RFC sur JSON, le RFC 8259.. Voyons
d'abord le résultat avec le traditionnel client DNS
dig :
% dig +short +nodnssec TXT 34000.cp.bortzmeyer.fr "MONTPELLIER"
L'enregistrement TXT
permet d'indiquer
le nom de la ville concernée. Parfois, il y en a plusieurs :
% dig +short +nodnssec TXT 52100.cp.bortzmeyer.fr "HALLIGNICOURT" "VALCOURT" "CHANCENAY" "SAPIGNICOURT" "ST DIZIER" "PERTHES" "ST EULIEN" "BETTANCOURT LA FERREE" "LANEUVILLE AU PONT" "VILLIERS EN LIEU" "MOESLAINS"
Le même domaine cp.bortzmeyer.fr
contient
également la position de la ville (ou plus probablement celle du
bureau de poste principal), avec le type LOC
,
normalisé dans le RFC 1876) :
% dig +short +nodnssec LOC 34000.cp.bortzmeyer.fr 43 36 47.108 N 3 52 9.113 E 0.00m 1m 10000m 10m
Nous voyons ici que Montpellier est à 43°
36' 47'' de latitude Nord et 3° 52' 9'' de
longitude Est.
Si vous voulez voir tout de suite où ça se trouve, vous avez
également un URI
(type d'enregistrement DNS
normalisé dans le RFC 7553) qui pointe vers
OpenStreetMap (merci à OpenStreetMap de
permettre de fabriquer des URL simples) :
% dig +short +nodnssec URI 52100.cp.bortzmeyer.fr 10 1 "http://www.openstreetmap.org/?mlat=48.646559&mlon=4.912187&zoom=12"
(L'enregistrement URI étant relativement récent, il ne faut pas utiliser un dig trop vieux. Par exemple, avec la version 9.8.3, on n'a pas de résultat avec cette commande.) Enfin, vous pouvez aussi retrouver les codes postaux à partir du nom de la ville :
% dig +short +nodnssec TXT ROUEN.cp.bortzmeyer.fr "76100" "76000"
Mais il y a quelques pièges : la base utilisée n'a que des caractères ASCII donc il faudra écrire « epernay » et pas le nom correct. Et pour Paris, il faudra ajouter un tiret et l'arrondissement, par exemple « paris-16 ».
Ce n'est pas très joli avec dig ? Pas convivial ? Vous pouvez aussi essayer un DNS Looking Glass. Par exemple, toutes les données pour 73370, la localisation de 73700, les les noms associés à 97434 ou bien le(s) code(s) postal(aux) de Beaugency.
Comment est-ce que ce service a été réalisé ? Je suis parti d'un fichier JSON contenant les informations nécessaires, un petit programme Python le transforme en fichier de zone DNS (section 5 du RFC 1035). Que du très simple. Le plus dur était certainement de trouver les données. En France, il y a une tradition régalienne de ne pas donner les informations au citoyen. L'État préfère les garder pour lui, ce qui obligeait à des contorsions compliquées. Le mouvement « données ouvertes » a heureusement changé les choses, même si c'est encore loin d'être parfait.
La source que j'ai utilisée est la « Base officielle des codes postaux » (trouvée via data.gouv.fr). Je ne dis pas que cette base est meilleure que les autres, mais son côté « officiel » me permet de répondre « c'est pas ma faute » si quelqu'un y trouve une erreur ou une approximation. J'ai utilisé la version GeoJSON du fichier. Elle n'est pas parfaite. Ainsi il y a un certain nombre de fautes d'orthographe comme Marolles-sous-Lignières écrit MAROLLES SOUS LIGNIERES, sans l'accent (les tirets et les apostrophes manquent également). D'autre part, certaines communes, notamment en Polynésie française, n'ont pas d'information de localisation. Ici, à Fakarava :
% dig +short +nodnssec LOC 98764.cp.bortzmeyer.fr %
Dernier problème, la liste n'est pas à jour. La commune de Meaulne-Vitray (code postal 03360), créée le 1er janvier 2017, n'apparait pas encore dans la liste, téléchargée le 24 septembre 2017. (Et elle n'est pas la seule.)
D'autres sources possibles de données avaient été considérées (et merci à Sabine Blanc, Olivier Macchioni, Pascal Corpet, Laurent Penou, Adrien Clerc, Ainiton et tous les autre qui m'ont fait d'excellentes suggestions) :
http://www.aito.fr/maps_ville.sql
est une
excellente base MySQL, où les noms sont
bien orthographiés, mais qui est vieille de nombreuses
années. D'autre part, elle semble incomplète
(Lille n'y a qu'un seul code postal alors
qu'il en existe plusieurs en pratique).http://www.galichon.com/codesgeo/
permet de
trouver codes postaux et positions géographiques, et sa base (à
jour ? correcte ?) est téléchargeable (au format
Microsoft Excel).Si vous voulez proposer d'autres bases, n'oubliez pas de regarder ces points :
Ce service utilise les codes postaux, mais il existe aussi les COG, dits « codes INSEE », qui sont également disponibles. Ce sera pour un autre service.
Un peu de technique pour finir. Le script lancé par cron tous les mois fait :
wget -O laposte_hexasmal.geojson 'https://datanova.legroupe.laposte.fr/explore/dataset/laposte_hexasmal/download/?format=geojson' ./laposte2dns.py laposte_hexasmal.geojson > cp.zone ods-signer sign bortzmeyer.fr
La première commande télécharge le fichier au format JSON. La
seconde (le script laposte2dns.py
) convertit le JSON
en fichier de zone DNS qui ressemble donc à :
10130.cp IN TXT "MAROLLES SOUS LIGNIERES" IN LOC 48 3 7.201 N 3 55 56.876 E 0 IN URI 10 1 "http://www.openstreetmap.org/?mlat=48.052000&mlon=3.932466&zoom=12" 10170.cp IN TXT "LES GRANDES CHAPELLES" IN LOC 48 29 29.856 N 3 56 0.604 E 0 IN URI 10 1 "http://www.openstreetmap.org/?mlat=48.491627&mlon=3.933501&zoom=12" 10110.cp IN TXT "MERREY SUR ARCE" IN LOC 48 6 57.679 N 4 25 51.357 E 0 IN URI 10 1 "http://www.openstreetmap.org/?mlat=48.116022&mlon=4.430933&zoom=12" ...
Le fichier ainsi produit (dans les onze mégaoctets) est chargé depuis le fichier de zone
principal de bortzmeyer.fr
, via une directive
d'inclusion :
; Codes Postaux $INCLUDE /etc/nsd/primary/cp.incl
Enfin, la troisième commande utilise OpenDNSSEC pour signer la zone, et recharger le serveur de noms.
Un tel service dans le DNS existe déjà pour le
Royaume-Uni, via le domaine
find.me.uk
:
% dig +short DY76JP.find.me.uk LOC 52 26 46.924 N 2 13 5.686 W 0.00m 0.00m 0.00m 0.00m
(Ou bien, avec le
Looking Glass.) Un travail similaire a été fait pour la
Suisse avec
zipdns.ch
. Par exemple :
% dig +short LOC 1204.zipdns.ch 46 12 14.214 N 6 8 47.064 E 1.00m 1m 10000m 10m
Ce qui est plus joli avec le Looking
Glass. Pour l'Allemagne, c'est en
cours (il n'y a pas encore la position physique)) sur zipde.jpmens.net
. Essayons avec
Ratisbonne :
% dig +short TXT 93047.zipde.jpmens.net "Regensburg"
Il y a un œuf de Pâques dans
ce service, faisant référence à une célèbre blague allemande.
Et il y a enfin un service similaire pour les codes IATA à trois lettres des
aéroports (souvent utilisés pour nommer les routeurs dans l'Internet, ce qui aide à
interpréter les traceroutes)
en air.jpmens.net
:
% dig +short ORY.air.jpmens.net LOC 48 43 24.000 N 2 22 46.000 E 89.00m 1m 10000m 10m
Une requête TXT vous en dira plus :
% dig +short ORY.air.jpmens.net TXT "u:https://en.wikipedia.org/wiki/Orly_Airport_(Paris)" "cc:FR; m:Paris; t:large, n:Paris-Orly Airport"
Là aussi, vous pouvez préferer le
Looking Glass. Ce service est documenté
dans un intéressant article. (Un service analogue utilisant
les UN/LOCODE existe en
locode.sha256.net
mais semble expérimental.)
Et pour rire, une quetsion difficile à la fin : faut-il indiquer la latitude ou la longitude en premier ? (ISO 6709 dit qu'on met la latitude avant).
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : A. Sullivan (Dyn)
Pour information
Réalisé dans le cadre du groupe de travail IETF dnssd
Première rédaction de cet article le 22 septembre 2017
DNS-SD (DNS-based Service Discovery, normalisé dans le RFC 6763) permet de découvrir des services dans le réseau, via le DNS, mais aussi via d'autres systèmes de résolution de noms, comme mDNS (RFC 6762). Ces différents systèmes de résolution ayant des propriétés différentes, il peut se poser des problèmes d'interopérabilité. Ce RFC documente ces problèmes et est donc une lecture recommandée pour les développeurs DNS-SD.
Rappelons un peu de contexte (section 1 du RFC) : les applications qui utilisent le DNS imposaient fréquemment une syntaxe réduite aux noms manipulés, la syntaxe « LDH » ([ASCII] Letters, Digits and Hyphen), décrite dans le RFC 952. (Contrairement à ce qu'on lit souvent, le DNS n'impose pas une telle limite, elle est purement dans les applications.) Du fait de ces règles des applications, il a fallu, pour utiliser des lettres Unicode, un système spécial, IDN (RFC 5890). IDN ajoute ses propres contraintes, comme le fait que les « non-lettres » (par exemple les emojis) ne sont pas acceptés.
Le DNS accepte tout à fait du binaire quelconque dans les noms (donc, par exemple, des caractères non-ASCII avec des encodages comme Latin-1). mDNS (RFC 6762) autorise également les caractères non-ASCII, mais impose l'encodage UTF-8. mDNS autorise des caractères qui ne sont pas permis en IDN, du banal espace aux symboles les plus rigolos. DNS-SD recommande même leur usage (RFC 6763, section 4.1.3). La seule contrainte est de se limiter au format Unicode du réseau, décrit dans le RFC 5198.
Bien des développeurs d'application ne sont pas au courant de ces subtilités. Ils manipulent des noms de services comme des chaînes de caractères, et ne se soucient pas du système de résolution sous-jacent et de ses particularités. Or, potentiellement, un même nom peut être accepté par certains de ces systèmes de résolution et refusés par d'autres.
Si le développeur est au courant, et s'il prend soin de faire en sorte que les noms « avancés » ne soient pas envoyés aux systèmes de résolution les plus anciens, ça peut marcher. Mais il ne faut pas se faire d'illusion, les fuites se produisent et tout nom utilisé sur une machine connectée à l'Internet finira tôt ou tard par arriver au DNS, comme le montre le trafic que reçoivent les serveurs de noms de la racine.
Notez enfin que les utilisateurs de DNS-SD, encore plus que ceux des noms classiques, ne tapent que très rarement les noms : presque toujours, ils les choisissent dans une liste.
Maintenant, pourquoi y a-t-il un problème ? (Section 2 du RFC.) Comme je l'ai indiqué plus haut, le DNS accepte n'importe quoi dans un nom. Pourquoi ne pas juste dire « envoyons le nom "Joe's printer, first floor" directement dans le DNS, et ça marchera » ? Il y a deux problèmes avec cette approche. D'abord, l'application qui ne ferait pas attention et enverrait un nom non-ASCII en disant « le DNS s'en tirera de toute façon », cette application oublie qu'il peut y avoir sur le trajet de sa requête une couche logicielle qui fait de l'IDN et va donc encoder en Punycode (transformation du U-label en A-label). Le « vrai » système de résolution ne verra donc pas le nom original. Un problème du même genre arrive avec certains logiciels qui se mêlent de politique, par exemple les navigateurs Web comme Firefox qui se permettent d'afficher certains noms en Unicode et d'autres en ASCII, selon la politique du TLD. Bref, le trajet d'un nom dans les différents logiciels est parsemé d'embûches si on est en Unicode.
En outre, l'approche « j'envoie de l'UTF-8 sans me poser de questions » (suggérée par la section 4.1.3 du RFC 6763) se heurte au fait que la racine du DNS et la plupart des TLD ne permettent de toute façon par d'enregistrer directement de l'UTF-8. (Au passage, le RFC oublie un autre problème de l'UTF-8 envoyé directement : les serveurs DNS ne font pas de normalisation Unicode, et l'insensibilité à la casse du DNS n'est pas évidente à traduire en Unicode.)
La section 3 de notre RFC propose donc de travailler à un profil permettant une meilleure interopérabilité. Un profil est une restriction de ce qui est normalement permis (un dénominateur commun), afin de passer à peu près partout. Un exemple de restriction serait l'interdiction des majuscules. En effet, le DNS est insensible à la casse mais IDN interdit les majuscules (RFC 5894, sections 3.1.3 et 4.2) pour éviter le problème de conversion majuscules<->minuscules, qui est bien plus difficile en Unicode qu'en ASCII.
Notez que notre RFC ne décrit pas un tel profil, il propose sa création, et donne quelques idées. Il y a donc encore du travail.
Pour rendre le problème plus amusant, les noms utilisés par DNS-SD sont composés de trois parties, avec des règles différentes (section 4 de notre RFC, et RFC 6763, section 4.1) :
L'instance risque d'être « interceptée » par le traitement IDN et, à tort, transformée en Punycode. Limiter le nom de l'instance aux caractères acceptés par IDN serait horriblement restrictif. La seule solution est probablement de mettre en place du code spécifique qui reconnait les noms utilisés par DNS-SD, pour ne jamais les passer au DNS. Quant à la partie « Domaine », l'idée de la traiter comme un nom de domaine normal, et donc sujet aux règles des RFC 5890 et suivants, est restrictive :
Première rédaction de cet article le 18 septembre 2017
On lit souvent dans les articles sur la sécurité du routage Internet, ou dans les articles qui décrivent un détournement BGP, volontaire ou non, une phrase du genre « la route pour le préfixe de l'attaquant, un /24, a été acceptée car elle est plus spécifique que le préfixe normal /22, et BGP préfère les routes plus spécifiques ». Ce n'est pas vraiment exact.
La question est importante, car ces détournements BGP arrivent (cf. le rapport de l'ANSSI à ce sujet), le cas le plus célèbre étant Pakistan Telecom contre YouTube, et le plus récent la détournement accidentel par Google, notamment au Japon. Si une organisation annonce un /22, et que l'attaquant publie un /24 mensonger, est-ce que cela aura davantage d'effet que s'il l'attaquant avait publié un /22 ? Et, pour le titulaire légitime du préfixe, est-ce que annoncer des /24 en plus est une bonne défense ? On lit souvent des Oui aux deux questions, avec comme justification que BGP préférerait le préfixe le plus spécifique, et qu'il ne faut donc pas le laisser à l'attaquant. La technique est bonne, mais cette justification n'est pas complètement correcte. (Bien qu'on la trouve même dans des articles sérieux.)
BGP (RFC 4271, notamment la section 9.1), en effet, n'a pas d'opinion sur la longueur des routes. S'il voit un /22 et un /24 plus spécifique, il les considère comme deux préfixes différents, et les garde en mémoire et les passe à ses voisins. (Les experts BGP noteront que j'ai un peu simplifié, BGP ayant plusieurs listes de routes, les RIB - Routing Information Base. La Loc-RIB ne garde que le préfixe spécifique mais l'Adj-RIBs-Out, ce qui est transmis aux voisins, a bien les deux routes.)
C'est IP, pas BGP, qui a une règle « longest prefix » (préfixe le plus spécifique) et qui, lui, choisira le /24. Comme le dit la maxime des opérateurs, « The data plane [IP] does not always follow the control plane [BGP]. »
Vous pouvez facilement voir cela sur un
looking glass. Prenons
par exemple celui de Level 3, en
. Le préfixe http://lg.level3.net/
2405:fd80::/32
a un plus-spécifique,
2405:fd80:100::/40
(il en a même plusieurs,
mais je simplifie). Les deux sont transportés par BGP. Le
looking glass affiche (j'ai fait deux requêtes
séparées, car l'option Longer prefixes de ce
looking glass ne marche pas pour moi) :
BGP Routing table entry for 2405:fd80::/32 3491 135391 ... BGP Routing table entry for 2405:fd80:100::/40 2914 135391
Le routeur connait donc bien les deux préfixes, le générique et le spécifique. (Il s'agit du comportement par défaut de BGP ; il existe des configurations de routeurs qui ne font pas cela, par exemple parce qu'elles agrègent les préfixes les plus spécifiques en un préfixe moins spécifique. Voir par exemple celle de Juniper.)
Mais, si je tape une adresse IP (pas un préfixe) dans un looking glass ou un routeur, je n'obtiens bien que le préfixe plus spécifique ? Oui, mais ce n'est pas un choix de BGP, c'est le choix du routeur sous-jacent de chercher le préfixe immédiatement englobant cette adresse. Tapez un préfixe au lieu d'une adresse IP et vous aurez bien le préfixe demandé.
[Le reste est plus technique, avec du code qui tourne.]
Si vous voulez vous-même analyser la table de routage mondiale, sans recourir à un service cloud comme RIPE Stat, voici deux façons de faire, l'une avec PostgreSQL, et l'autre avec un programme en C. Les deux utilisent la même source de données, RouteViews. RouteViews distribue des mises à jour et des tables complètes (RIB = Routing Information Base). On télécharge la RIB et on la décomprime :
% wget http://archive.routeviews.org/bgpdata/2017.09/RIBS/rib.20170905.1200.bz2 % bunzip2 rib.20170905.1200.bz2
Elle est au format MRT (RFC 6396). On utilise bgpdump pour la traduire en texte :
% bgpdump rib.20170905.1200>rib.20170905.1200.txt
De manière grossière et sauvage, on ne garde que les lignes indiquant un préfixe, et on les dédoublonne (attention, ça va manger de la RAM) :
% grep PREFIX: rib.20170905.1200.txt | sort | uniq > rib.20170905.1200.sorted
À partir de là, on va utiliser soit PostgreSQL, soit le programme en C.
D'abord, avec PostgreSQL, qui a depuis longtemps d'excellents types de données pour les adresses IP, avec plein d'opérations possibles. On crée la table avec :
bgp=> CREATE TABLE Prefixes (id SERIAL, pfx cidr); CREATE TABLE
Pour importer le fichier texte dans PostgreSQL, on utilise un tout petit script Python :
% ./dump2psql.py rib.20170905.1200.sorted
Et tout est maintenant dans la base de données, qu'on peut
explorer. Par exemple, trouver tous les préfixes plus générique que 192.134.0.0/24
:
% psql bgp bgp=> SELECT * FROM Prefixes WHERE pfx >> '192.134.0.0/24'; id | pfx ---------+---------------- 1135437 | 192.134.0.0/16 1135438 | 192.134.0.0/22 (2 rows)
Et si on veut la liste de toutes les annonces plus spécifiques qu'une annonce existante, ici en IPv6 :
bgp=> SELECT gen.pfx AS Generic, spec.pfx AS Specific FROM Prefixes gen, Prefixes spec WHERE family(spec.pfx) = 6 AND masklen(spec.pfx) < 48 AND spec.pfx << gen.pfx; generic | specific ---------------------+--------------------- 2001:1280::/32 | 2001:1280:8000::/36 2001:128c::/32 | 2001:128c:6000::/36 2001:128c::/32 | 2001:128c:7000::/40 2001:12f0::/36 | 2001:12f0:100::/42 ...
Et si on n'aime pas les SGBD et qu'on veut le faire en C ? On va se servir de la bibliothèque libcidr. Installons :
% wget http://www.over-yonder.net/~fullermd/projects/libcidr/libcidr-1.2.3.tar.xz % unxz libcidr-1.2.3.tar.xz % tar xvf libcidr-1.2.3.tar % cd libcidr-1.2.3 % ./configure % make % sudo make install
Puis écrivons un petit programme C qui va lire le fichier texte avec
la liste des préfixes et afficher les préfixes plus spécifiques (ou égaux) qu'un préfixe
donné (option -s
) ou les plus génériques (option
-g
) :
% ./read-mrt rib.20170905.1200.sorted -s 72.249.184.0/21 72.249.184.0/21 (line 700376) 72.249.184.0/24 (line 700377) % ./read-mrt rib.20170905.1200.sorted -g 72.249.184.0/21 72.249.128.0/18 (line 700369) 72.249.184.0/21 (line 700376)
Merci à Bruno Spiquel pour ses conseils.
Date de publication du RFC : Août 2017
Auteur(s) du RFC : T. Anderson (Redpill Linpro)
Chemin des normes
Première rédaction de cet article le 18 septembre 2017
Ce très court RFC documente la
réservation du préfixe IPv6
64:ff9b:1::/48
, pour les divers mécanismes de
traduction entre IPv6 et IPv4.
L'adressage de ces mécanismes de traduction est décrit dans le
RFC 6052. Il réservait le préfixe
64:ff9b::/96
, qui s'est avéré insuffisant et
est désormais complété par notre RFC 8215.
Ouvrez bien l'œil : ces deux préfixes sont différents, même si leurs quatre premiers octets sont identiques (c'est un problème fréquent en IPv6 : il peut être difficile de distinguer deux adresses du premier coup.)
La section 3 de notre RFC expose le problème : depuis la
publication du RFC 6052, plusieurs
mécanismes de traduction IPv4<->IPv6 ont été proposés (par
exemple dans les RFC 6146 et RFC 7915). Ils ne sont pas inconciliables (ils couvrent
parfois des scénarios différents ) et un opérateur peut donc
vouloir en déployer plusieurs à la fois. Le seul préfixe
64:ff9b::/96
ne va alors plus suffire. Il
faut donc un préfixe plus grand, et dont l'usage n'est pas
restreint à une seule technologie de traduction.
Pourquoi 64:ff9b:1::/48
, d'ailleurs ? La
section 4 répond à la question. La longueur de 48 bits a été
choisie pour permettre plusieurs mécanismes de traduction, chacun
utilisant un préfixe plus spécifique. Par exemple, si chaque
mécanisme utilise un /64, le préfixe réservé pour les englober
tous devait être un /48, ou plus général (on se limite aux
multiples de 16 bits, car ils permettent que les préfixes se
terminent sur un deux-points, facilitant la
vie de l'administrateur réseaux qui devra les manipuler).
Ensuite, pourquoi cette valeur
64:ff9b:1::
? Parce qu'elle est proche
(presque adjacente) du
64:ff9b::/96
, minimisant la consommation
d'adresses IPv6. (64:ff9a:ffff::/48
est
complètement adjacent, de l'autre côté, mais l'agrégation avec
64:ff9b::/96
en un seul préfixe est beaucoup
moins efficace. Et puis 64:ff9b:1::
est plus
court.)
À part son usage, 64:ff9b:1::/48
est un
préfixe IPv6 normal et les routeurs, ou autres machines qui
manipulent des adresses IPv6, ne doivent pas le traiter
différemment des autres.
Le nouveau préfixe est désormais enregistré dans le registre des adresses IPv6 spéciales (section 7 de notre RFC).
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : P. McManus (Mozilla)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 16 septembre 2017
Lorsqu'un serveur HTTP renvoie une
réponse à un client, il peut indiquer une durée de vie maximale de
la ressource transmise, avec l'en-tête
Cache-Control:
(RFC 9111). Mais cela ne donne qu'une durée
maximale. La ressource peut quand même être
modifiée avant la fin de cette période. Un client HTTP prudent va
donc revalider la fraîcheur de cette
ressource de temps en temps. L'extension décrite dans ce
RFC permet de se dispenser de cette
revalidation, en indiquant une durée
minimale, pendant laquelle on est sûrs et
certains que la ressource ne sera pas modifiée.
Le RFC prend l'exemple (section 1) d'un journal dont la page d'accueil indique une durée de vie maximale d'une heure :
Cache-Control: max-age=3600
Le client HTTP qui reçoit cet en-tête dans la réponse sait qu'il doit recontacter le serveur au bout d'une heure. Mais la page sera peut-être modifiée (par exemple en raison d'un évènement soudain et imprévu) avant qu'une heure soit écoulée. Le client est donc partagé entre la revalidation (qui risque de consommer des ressources pour rien, même si elle est moins coûteuse qu'un téléchargement complet, grâce aux trucs du RFC 7232) et le risque de servir à son utilisateur une ressource qui n'est désormais plus à jour. Le problème est d'autant plus ennuyeux que la revalidation d'une bête page Web qui comprend des photos peut générer beaucoup de requêtes, dont la plupart produiront sans doute un 304 (ressource non modifiée, la revalidation était inutile).
Notez que dans certains cas, le contenu pointé par un URL peut changer, mais dans d'autres cas, il est réellement immuable. Par exemple, si on publie chaque version de sa feuille de style sous un URL différent, la CSS n'a pas besoin d'être revalidée. (La mention d'immuabilité est donc intéressante pour les URL faits à partir du RFC 6920.)
Bref, il y avait un besoin de pouvoir indiquer l'immuabilité
d'une ressource. C'est désormais fait (section 2 de ce RFC) avec
une extension à Cache-Control:
:
Cache-Control: max-age=3600, immutable
Avec le mot-clé immutable
, le serveur indique
que la ressource ne sera pas modifiée pendant la durée de vie
indiquée, le client peut donc se dispenser de
vérifier. (« Client » ici désignant aussi bien le client final,
par exemple le navigateur Web, qu'un
intermédiaire.)
Par exemple, comme les RFC sont immuables (on ne les change
jamais même d'une virgule, même en cas d'erreur), la ressource qui désigne ce RFC,
https://www.rfc-editor.org/rfc/rfc8246.txt
,
pourrait parfaitement renvoyer cet en-tête (elle ne le fait
pas). Cela pourrait être :
Last-Modified: Thu, 14 Sep 2017 23:11:35 GMT Cache-Control: max-age=315576000, immutable
(Oui, dix ans…)
Voilà, c'est tout, la directive a été ajoutée au registre
IANA. Mais la section 3 du RFC se penche encore
sur quelques questions de sécurité. Indiquer qu'une ressource est
immuable revient à la fixer pour un temps potentiellement très
long, et cela peut donc servir à pérenniser une attaque. Si un
méchant pirate un site Web, et sert son contenu piraté avec un
en-tête d'immuabilité, il restera bien plus longtemps dans les
caches. Pire, sans
HTTPS, l'en-tête avec
immutable
pourrait être ajouté par un
intermédiaire (le RFC déconseille donc de tenir compte de cette
option si on n'a pas utilisé HTTPS).
Les navigateurs Web ont souvent deux options pour recharger une
page, « douce » et « dure » (F5 et Contrôle-F5 dans
Firefox sur Unix). Le RFC conseille que, dans le second cas
(rechargement forcé), le immutable
soit ignoré (afin de
pouvoir recharger une page invalide).
Enfin, toujours question sécurité, le RFC recommande aux
clients HTTP de ne tenir compte de l'en-tête d'immuabilité que
s'ils sont sûrs que la ressource a été transmise proprement
(taille correspondant à Content-Length:
par
exemple).
Si vous voulez comparer deux ressources avec et sans
immutable
, regardez
(sans http://www.bortzmeyer.org/files/maybemodified.txt
immutable
) et
(avec). Si le client HTTP gère l'extension d'immuabilité, un
rechargement « doux » ne fera pas de requête HTTP. Sinon, il y aura
revalidation et le serveur HTTP renverra un 304 :
http://www.bortzmeyer.org/files/forever.txt
[2001:db8:abcd:1234:acd8:9bd0:27c9:3a7f]:45452 - - [16/Sep/2017:17:53:29 +0200] "GET /files/forever.txt HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux i686; rv:52.0) Gecko/20100101 Firefox/52.0" www.bortzmeyer.org
Apparemment, Firefox gère cette extension mais, ici, le Firefox était sans doute trop vieux (normalement, cela aurait dû arriver avec la version 49, puisque Mozilla était premier intéressé). Les auteurs de Squid ont annoncé qu'ils géreraient cette extension. Côté serveur, notons que Facebook envoie déjà cette extension pour, par exemple, le code JavaScript (qui est versionné et donc jamais changé) :
% curl -v https://www.facebook.com/rsrc.php/v3iCKe4/y2/l/fr_FR/KzpL-Ycd5sN.js * Connected to www.facebook.com (2a03:2880:f112:83:face:b00c:0:25de) port 443 (#0) ... < HTTP/2 200 < content-type: application/x-javascript; charset=utf-8 < cache-control: public,max-age=31536000,immutable ...
Idem pour une mise en œuvre d'IPFS.
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : J. Reschke (greenbytes)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 14 septembre 2017
Dernière mise à jour le 16 septembre 2017
Les requêtes et réponses du protocole HTTP incluent des
en-têtes (comme
User-Agent:
ou
Content-Disposition:
) avec des
valeurs, qui, il y a longtemps, ne pouvaient se représenter
directement qu'avec les caractères du jeu ISO
8859-1, voire seulement avec
ASCII (c'était compliqué). Comme MIME, dans le RFC 2231,
prévoyait un mécanisme très riche pour encoder les en-têtes du
courrier électronique, ce RFC 8187
réutilise ce mécanisme pour HTTP (il remplace le RFC 5987, qui avait été le premier à le faire). Pour le corps du
message (voir par exemple le RFC 7578), rien
ne change.
Cette ancienne restriction à Latin-1 (qui n'est plus d'actualité) vient de la norme HTTP, le RFC 2616, dans sa section 2.2, qui imposait l'usage du RFC 2047 pour les caractères en dehors de ISO 8859-1. Le RFC 7230 a changé cette règle depuis (sa section 3.2) mais pas dans le sens d'une plus grande internationalisation (ISO 8859-1 ne convient qu'aux langues européennes), plutôt en supprimant le privilège d'ISO 8859 et en restreignant à ASCII. Et il ne précise pas vraiment comment faire avec d'autres jeux de caractère comme Unicode. Il ne reste donc que la solution du RFC 2231.
Notre nouveau RFC peut être résumé en disant qu'il spécifie un profil du RFC 2231. Ce profil est décrit en section 3, qui liste les points précisés par rapport au RFC 2231. Tout ce RFC n'est pas utilisé, ainsi le mécanisme en section 3 du RFC 2231, qui permettait des en-têtes de plus grande taille, n'est pas importé (section 3.1 de notre RFC).
En revanche, la section 4 du RFC 2231, qui spécifiait
comment indiquer la langue dans laquelle était
écrite la valeur d'un en-tête est repris pour les paramètres dans les
en-têtes. Ainsi, (section 3.2), voici un en-tête (imaginaire :
Information:
n'a pas été enregistré), avec un paramètre
title
traditionnel en pur ASCII :
Information: news; title=Economy
et en voici un avec les possibilités de notre RFC pour permettre les caractères £ et € (« Sterling and euro rates ») :
Information: news; title*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates
Par rapport au RFC 2231 (qui était silencieux sur ce point), un encodage de caractères est décrété obligatoire (c'est bien sûr UTF-8), et il doit donc être géré par tous les logiciels. La mention de l'encodage utilisé est également désormais obligatoire (section 3.2 de notre RFC). La langue elle-même est indiquée par une étiquette, selon la syntaxe du RFC 5646. Du fait de ces possibilités plus riches que celles prévues autrefois pour HTTP, les paramètres qui s'en servent doivent se distinguer, ce qui est fait avec un astérisque avant le signe égal (voir l'exemple ci-dessus). Notez que l'usage de l'astérisque n'est qu'une convention : si on trouve un paramètre inconnu dont le nom se termine par un astérisque, on ne peut pas forcément en déduire qu'il est internationalisé.
La valeur du paramètre inclut donc le jeu de caractères et l'encodage (obligatoire), la langue (facultative, elle n'est pas indiquée dans l'exemple ci-dessus) et la valeur proprement dite.
Voici un exemple incluant la langue, ici
l'allemand (code de
, la
phrase est « Mit der Dummheit kämpfen Götter
selbst vergebens », ou « contre la bêtise, les dieux
eux-mêmes luttent en vain », tirée de la pièce « La pucelle
d'Orléans ») :
Quote: theater; sentence*=UTF-8'de'Mit%20der%20Dummheit%20k%C3%A4mpfen%20G%C3%B6tter%20selbst%20vergebens.
La section 4 couvre ensuite les détails pratiques pour les normes qui décrivent un en-tête qui veut utiliser cette possibilité. Par exemple, la section 4.2 traite des erreurs qu'on peut rencontrer en décodant et suggère que, si deux paramètres identiques sont présents, celui dans le nouveau format prenne le dessus. Par exemple, si on a :
Information: something; title="EURO exchange rates"; title*=utf-8''%e2%82%ac%20exchange%20rates
le titre est à la fois en ASCII pur et en UTF-8, et c'est cette
dernière version qu'il faut utiliser, même si normalement il n'y a
qu'un seul paramètre title
.
Ces paramètres étendus sont mis en œuvre dans Firefox et Opera ainsi que, dans une certaine mesure, dans Internet Explorer.
Plusieurs en-têtes HTTP se réfèrent formellement à cette façon d'encoder les caractères non-ASCII :
Authentication-Control:
, dans le RFC 8053 (« For example, a parameter "username" with
the value "Renee of France" SHOULD be sent as username="Renee of
France". If the value is "Renée of France", it SHOULD be sent as
username*=UTF-8''Ren%C3%89e%20of%20France instead »),Authorization:
(pour l'authentification
HTTP, RFC 7616, avec également un paramètre
username
pour l'ASCII et
username*
pour l'encodage défini dans ce RFC),Content-Disposition:
, RFC 6266,
qui indique sous quel nom enregistrer un fichier et dont le
paramètre filename*
permet tous les caractères Unicode,Link:
, normalisé dans le RFC 5988, où le paramètre title*
permet des
caractères non-ASCII (title
étant pour l'ASCII pur).Les changements depuis le RFC 5987, sont expliqués dans l'annexe A. Le plus spectaculaire est le retrait d'ISO 8859-1 (Latin-1) de la liste des encodages qui doivent être gérés obligatoirement par le logiciel. Cela fera plaisir aux utilisateurs d'Internet Explorer 9, qui avait déjà abandonné Latin-1. Autrement, rien de crucial dans ces changements. Le texte d'introduction a été refait pour mieux expliquer la situation très complexe concernant la légalité (ou pas) des caractères non-ASCII dans les valeurs d'en-tête.
Si vous voulez voir un exemple, essayez de télécharger le fichier
http://www.bortzmeyer.org/files/foobar.txt
. Si
votre client HTTP gère l'en-tête
Content-Disposition:
et le paramètre
internationalisé filename*
, le fichier devrait
être enregistré sous le nom föbàr.txt
.La
configuration d'Apache pour envoyer le
Content-Disposition:
est :
<Files "foobar.txt"> Header set Content-Disposition "attachment; filename=foobar.txt; filename*=utf-8''f%%C3%%B6b%%C3%%A0r.txt" </Files>
Par exemple, Safari ou Firefox enregistrent bien ce fichier sous son nom international.
Ah, et puisque ce RFC parle d'internationalisation, on notera que c'est le premier RFC (à part quelques essais ratés au début) à ne pas comporter que des caractères ASCII. En effet, suivant les principes du RFC 7997, il comporte cinq caractères Unicode : dans les exemples (« Extended notation, using the Unicode character U+00A3 ("£", POUND SIGN) » et « Extended notation, using the Unicode characters U+00A3 ("£", POUND SIGN) and U+20AC ("€", EURO SIGN) »), dans l'adresse (« Münster, NW 48155 ») et dans les noms des contributeurs (« Thanks to Martin Dürst and Frank Ellermann »).
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : H. Tschofenig (ARM Limited), S. Farrell (Trinity College Dublin)
Pour information
Première rédaction de cet article le 12 septembre 2017
La mode des objets connectés a mené à l'existence de nombreux « objets », qui sont en fait des ordinateurs (avec leurs problèmes, comme le fait que leur logiciel ait des failles de sécurité), mais qui ne sont pas considérés comme des ordinateurs par leurs propriétaires, et ne sont donc pas gérés (pas d'administrateur système, pas de mises à jour des logiciels). L'attaque contre Dyn du 21 octobre 2016, apparemment menée par ces objets, a bien illustré le risque que cette profusion irresponsable crée. Ce nouveau RFC est le compte-rendu d'un atelier sur la mise à jour des objets connectés, atelier de réflexion qui s'est tenu à Dublin les 13 et 14 juin 2016. Il fait le point sur la (difficile) question.
La question de base était « que peut-on faire pour que ces foutus objets soient mis à jour, au moins pour patcher leurs failles de sécurité ? » L'atelier réuni à Trinity College n'a évidemment pas fourni une réponse parfaite, mais a au moins permis de clarifier le problème. Il me semble toutefois, d'après le compte-rendu, qu'il y avait un gros manque : les droits du propriétaire de l'objet. La plupart des solutions discutées tournaient autour de l'idée de mises à jour systématiques et automatiques du logiciel via les serveurs du vendeur, et les éventuelles conséquences néfastes pour le propriétaire (comme l'arrivée de nouvelles fonctions de surveillance, par exemple) ne sont guère mentionnées.
Aujourd'hui, un grand nombre des machines ayant accès, même indirect, à l'Internet, est composé de trucs qualifiés d'objets, qui ont en commun qu'on ne les appelle pas « ordinateurs » (alors que c'est bien cela qu'ils sont). Ces objets sont très divers, allant d'engins qui ont les capacités matérielles d'un ordinateur (une télévision connectée, par exemple) à des petits machins très contraints (CPU et mémoire limités, batterie à la capacité finie, etc). Le risque que font peser ces objets « irresponsables » (pas gérés, pas supervisés) sur l'Internet est connu depuis longtemps. Le RFC cite l'article de Schneier, « The Internet of Things Is Wildly Insecure And Often Unpatchable », qui tirait la sonnette d'alarme en 2014. Il notait que le logiciel de ces objets n'était déjà plus à jour quand l'objet était sorti de sa boite la première fois (les fabricants d'objets connectés aiment bien utiliser des versions antédiluviennes des bibliothèques). Or, des objets peuvent rester branchés et actifs pendant des années, avec leur logiciel dépassé et jamais mis à jour, plein de failles de sécurité que les craqueurs ne vont pas manquer d'exploiter (cf. le logiciel Mirai).
Vers la même époque, un rapport de la FTC, « FTC Report on Internet of Things Urges Companies to Adopt Best Practices to Address Consumer Privacy and Security Risks », et un autre du groupe Article 29, « Opinion 8/2014 on the on Recent Developments on the Internet of Things » faisaient les mêmes remarques.
Donc, le problème est connu depuis des années. Mais il n'est pas facile à résoudre :
Le risque n'est donc pas seulement de l'absence de système de mise à jour. Il peut être aussi d'un système de mise à jour bogué, vulnérable, ou non documenté, donc non maintenable.
Le problème de la mise à jour automatique des logiciels est évidemment ancien, et plusieurs systèmes d'exploitation ont des solutions opérationnelles depuis un certain temps (comme pacman ou aptitude). Le RFC se focalise donc sur les objets les plus contraints, ceux limités dans leurs capacités matérielles, et donc dans les logiciels qu'ils peuvent faire tourner.
Après cette introduction, le RFC fait un peu de terminologie, car le choix des mots a suscité une discussion à l'atelier. D'abord, la notion de classe. Les « objets connectés » vont de systèmes qui ont les capacités matérielles et l'alimentation électrique d'un ordinateur de bureau (une télévision connectée, par exemple) et qui peuvent donc utiliser les mêmes techniques, jusqu'à des objets bien plus contraints dans leurs capacités. Pour prendre un exemple chez les systèmes populaires auprès des geeks, un Raspberry Pi fait tourner un système d'exploitation « normal » et se met à jour comme un ordinateur de bureau, un Arduino ne le peut typiquement pas. Il faudra donc sans doute développer des solutions différentes selon la classe. Ou, si on veut classer selon le type de processeur, on peut séparer les objets ayant plus ou moins l'équivalent d'un Cortex-A (comme le Pi) de ceux ayant plutôt les ressources du Cortex-M (comme l'Arduino, mais notez que cette catégorie est elle-même très variée).
Le RFC définit également dans cette section 2 la différence entre mise à jour du logiciel et mise à jour du firmware. La distinction importante est que les engins les plus contraints en ressources n'ont typiquement qu'une mise à jour du firmware, qui change tout, système et applications, alors que les engins de classe « supérieure » ont des mises à jour modulaires (on peut ne mettre à jour qu'une seule application).
Dernier terme à retenir, hitless (« sans impact »). C'est une propriété des mises à jour qui ne gênent pas le fonctionnement normal. Par exemple, s'il faut arrêter l'engin pour une mise à jour, elle ne sera pas hitless. Évidemment, la mise à jour du logiciel d'une voiture ne sera probablement pas hitless et nécessitera donc des précautions particulières.
Maintenant, le gros morceau du RFC, la section 3, qui regroupe notamment les exigences issues de l'atelier. C'est assez dans le désordre, et il y a davantage de questions que de réponses. Pour commencer, que devraient faire les fabricants d'objets connectés (en admettant qu'ils lisent les RFC et aient le sens des responsabilités, deux paris hasardeux) ? Par exemple, le RFC note que les mises à jour globales (on remplace tout) sont dangereuses et recommande que des mises à jour partielles soient possibles, notamment pour limiter le débit utilisé sur le réseau (comme avec bsdiff ou courgette). Idéalement, on devrait pouvoir mettre à jour une seule bibliothèque, mais cela nécessiterait du liage dynamique et de code indépendant de la position, ce qui peut être trop demander pour les plus petits objets. Mais au moins un système d'exploitation plutôt conçu pour les « classe M », les objets les plus contraints (équivalents au Cortex-M), le système Contiki, a bien un lieur dynamique.
Certains dispositifs industriels ont plusieurs processeurs, dont un seul gère la connexion avec le reste du monde. Il peut être préférable d'avoir une architecture où ce processeur s'occupe de la mise à jour du logiciel de ses collègues qui ne sont pas directement connectés.
Un problème plus gênant, car non exclusivement technique, est celui des engins qui ont plusieurs responsables potentiels. Si une machine sert à plusieurs fonctions, on risque d'avoir des cas où il n'est pas évident de savoir quel département de l'entreprise a « le dernier mot » en matière de mise à jour. Pour les ordinateurs, cette question sociale a été réglée depuis longtemps dans la plupart des organisations (par exemple en laissant le service informatique seul maître) mais les objets connectés redistribuent les cartes. Si le service Communication a une caméra connectée, qui peut décider de la mettre à jour (ou pas) ?
Un cas proche est celui où un appareil industriel inclut un ordinateur acheté à un tiers (avec le système d'exploitation). Qui doit faire la mise à jour ? Qui est responsable ? Pensons par exemple au problème d'Android où les mises à jour doivent circuler depuis Google vers le constructeur du téléphone, puis (souvent) vers l'opérateur qui l'a vendu. En pratique, on constate qu'Android est mal mis à jour. Et ce sera pire pour un réfrigérateur connecté puisque sa partie informatique sera fabriquée indépendamment, et avant le frigo complet. Lorsque celui-ci démarrera pour la première fois, son logiciel aura sans doute déjà plusieurs mois de retard.
Et la sécurité ? Imaginons un objet connecté dont le fabricant soit assez incompétent et assez irresponsable pour que les mises à jour se fassent en récupérant du code en HTTP, sans aucune authentification d'aucune sorte. Un tel objet serait très vulnérable à des attaques visant à remplacer le code authentique par un code malveillant. D'où l'exigence de DAO (Data Origin Authentication). Sans DAO, tout serait fichu. Deux solutions évidentes utilisent la cryptographie, authentifier le serveur qui distribue les mises à jour (par exemple avec HTTPS) ou bien authentifier le code téléchargé, via une signature numérique (notez que l'IETF a un format CMS pour cela, décrit dans le RFC 4108, mais qui semble peu utilisé.) Mais signer et vérifier est plus facile à dire qu'à faire ! D'abord, si la cryptographie n'est pas un problème pour les engins « classe A », comme une télévision connectée ou bien une voiture connectée, elle peut être bien coûteuse pour des objets plus limités. La cryptographie symétrique est typiquement moins coûteuse, mais bien moins pratique. Le problème de fond est que la cryptographie symétrique n'authentifie pas un envoyeur mais une classe d'envoyeurs (tous ceux qui ont la clé). Il faudrait donc une clé différente par objet, ce qui semble ingérable. Faut-il lister comme exigence de base d'utiliser la cryptographie asymétrique ?
Son coût en opérations de calcul n'est pas le seul problème. Par exemple, la clé privée qui signe les mises à jour peut être volée, ou bien tout simplement devenir trop faible avec les progrès de la cryptanalyse (certains objets peuvent rester en production pendans dix ans ou plus). Il faut donc un moyen de mettre la clé publique à jour. Comment le faire sans introduire de nouvelles vulnérabilités (imaginez un attaquant qui trouverait le moyen de subvertir ce mécanisme de remplacement de clé, et changerait la clé de signature pour la sienne). Bref, les méthodes « il n'y a qu'à… » du genre « il n'y a qu'à utiliser des signatures cryptographiques » ne sont pas des solutions magiques.
Et si l'objet utilise du code venant de plusieurs sources ? Faut-il plusieurs clés, et des règles compliquées du genre « l'organisation A est autorisée à mettre à jour les composants 1, 3 et 4 du système, l'organisation B peut mettre à jour 2 et 3 » ? (Les systèmes fermés sont certainement mauvais pour l'utilisateur, mais présentent des avantages en matière de sécurité : une seule source. Pensez à tous les débats autour de l'utilisation du magasin libre F-Droid, par exemple l'opinion de Whisper.)
Un problème plus fondamental est celui de la confiance : à qui un objet connecté doit-il faire confiance, et donc quelle clé publique utiliser pour vérifier les mises à jour ? Faire confiance à son propriétaire ? À son fabricant ? Au sous-traitant du fabricant ?
On n'en a pas fini avec les problèmes, loin de là. Les objets sont conçus pour être fabriqués et distribués en grande quantité. Cela va donc être un difficile problème de s'assurer que tous les objets dont on est responsable soient à jour. Imaginez une usine qui a des centaines, voire des milliers d'objets identiques, et qui veut savoir s'ils sont tous à la même version du logiciel.
On a parlé plus haut de confiance. On peut décider de faire confiance au fabricant d'un objet, parce qu'on juge l'organisation en question honnête et compétente. Mais cette évaluation ne s'étend pas forcément à la totalité de ses employés. Comment s'assurer qu'un employé méchant ne va pas compromettre le processus de mise à jour du logiciel, par exemple en se gardant une copie de la clé privée, ou bien en ajoutant aux objets produits une clé publique supplémentaire (il aurait alors une porte dérobée dans chaque objet, ce qui illustre bien le fait que la mise à jour est elle-même source de vulnérabilités).
On a parlé des risques pour la sécurité de l'objet connecté. Les précautions prises sont actuellement proches de zéro. Espérons que cela va s'améliorer. Mais, même en prenant beaucoup de précautions, des piratages se produiront parfois. Ce qui ouvre la question de la récupération : comment récupérer un objet piraté ? Si on a dix grille-pains compromis, va t-il falloir les reflasher tous les dix, manuellement ? (Avant cela, il y a aussi l'intéressante question de savoir comment détecter qu'il y a eu piratage, sachant que la quasi-totalité des organisations ne lisent pas les messages leur signalant des comportements suspects de leurs machines.)
La cryptographie apporte clairement des solutions intéressantes à bien des problèmes de sécurité. Mais elle nécessite en général une horloge bien à l'heure (par exemple pour vérifier la date d'expiration d'un certificat). Avoir une horloge maintenue par une batterie pendant l'arrêt de la machine et pendant les coupures de courant n'est pas trivial :
Il n'est donc pas évident qu'il y ait une bonne solution à ce problème. (Le RFC cite Roughtime comme une approche prometteuse.)
Et si on a une bonne solution pour distribuer les mises à jour à des milliers ou des millions d'objets connectés, comment ceux-ci vont-ils être informés de l'existence d'une mise à jour. Push ou pull ? Envoyer les mises à jour depuis le serveur est la solution la plus rapide mais elle peut être délicate si l'objet est derrière un pare-feu qui interdit les connexions entrantes (il faudrait que l'objet contacte le serveur de mise à jour, et reste connecté). Et le cas d'objets qui ne sont pas joignables en permanence complique les choses. De l'autre côté, le cas où l'objet contacte le serveur de temps en temps pour savoir s'il y a une mise à jour pose d'autres problèmes. Par exemple, cela consomme de l'électricité « pour rien ».
Vous trouvez qu'il y a assez de problèmes ? Mais ce n'est pas fini. Imaginez le cas d'une mise à jour qui contienne une bogue, ce qui arrivera inévitablement. Comment revenir en arrière ? Refaire une mise à jour (la bogue risque d'empêcher ce processus) ? L'idéal serait que l'objet stocke deux ou trois versions antérieures de son logiciel localement, pour pouvoir basculer vers ces vieilles versions en cas de problème. Mais cela consomme de l'espace de stockage, qui est très limité pour la plupart des objets connectés.
Le consensus de l'atelier a été que les signatures des logiciels (avec vérification par l'objet), et la possibilité de mises à jour partielles, étaient importants. De même, les participants estimaient essentiels la disponibilité d'une infrastructure de mise à jour, qui puisse fonctionner pour des objets qui n'ont pas un accès complet à l'Internet. Par exemple, il faut qu'une organisation puisse avoir une copie locale du serveur de mise à jour, à la fois pour des raisons de performances, de protection de la vie privée, et pour éviter de donner un accès Internet à ses objets.
En parlant de vie privée (RFC 6973), il faut noter que des objets se connectant à un serveur de mise à jour exposent un certain nombre de choses au serveur, ce qui peut ne pas être souhaitable. Et certains objets sont étroitement associés à un utilisateur (tous les gadgets à la maison), aggravant le problème. Le RFC note qu'au minimum, il ne faut pas envoyer un identificateur unique trop facilement, surtout au dessus d'un lien non chiffré. Mais le RFC note à juste titre qu'il y a un conflit entre la vie privée et le désir des vendeurs de se faire de l'argent avec les données des utilisateurs.
On a surtout parlé jusqu'à présent des risques pour l'objet si le serveur des mises à jour est méchant ou piraté. Certains acteurs du domaine vont ajouter des risques pour le serveur : par exemple, si le logiciel est non-libre, certains peuvent souhaiter authentifier l'objet, pour ne pas distribuer leur précieux logiciel privateur à « n'importe qui ».
On en a déjà parlé mais cela vaut la peine d'y revenir : un gros problème de fond est celui de l'autorisation (section 4 du RFC). Qui peut accepter ou refuser une mise à jour ? Oui, il y a de bonnes raisons de refuser : des mises à jour obligatoires sont une forme de porte dérobée, elles permettent d'ajouter des fonctions qui n'étaient pas là avant et qui peuvent être malveillantes. Le cas avait été cité lors de l'affaire de l'iPhone de San Bernardino (des gens proposaient une mise à jour que le téléphone appliquerait, introduisant une porte dérobée). Et le RFC rappelle le cas d'une mise à jour forcée d'imprimantes HP qui avait supprimé des fonctions utiles mais que HP ne voulait plus (la possibilité d'accepter des cartouches d'encre fournies par les concurrents). Un cas analogue révélé la veille de la publication du RFC est celui de Tesla activant une nouvelle fonction à distance (ici, pour améliorer la voiture, mais il est facile de voir que cela pourrait être en sens inverse). Et il y a bien sûr le cas fameux d'Amazon détruisant des livres à distance.
Prenez un téléphone Android programmé par Google, fabriqué par LG, vendu par Orange à la société Michu qui le confie à son employé M. Dupuis-Morizeau. Qui va décider des mises à jour ?
On peut séparer les mises à jour qui corrigent une faille de sécurité et les autres : seules les premières seraient systématiquement appliquées. (Tous les fournisseurs de logiciel ne font pas cette séparation, qui les oblige à gérer deux lignes de mises à jour.) Ceci dit, en pratique, la distinction n'est pas toujours facile, et la correction d'une faille peut en entrainer d'autres.
D'autre part, certains objets connectés sont utilisés dans un environnement très régulé, où tout, matériel et logiciel, doit subir un processus de validation formel avant d'être déployé (c'est le cas des objets utilisés dans les hôpitaux, par exemple). Il y aura forcément une tension entre « faire la mise à jour en urgence car elle corrige une vulnérabilité » et « ne pas faire la mise à jour avant d'avoir tout revalidé, car cet objet est utilisé pour des fonctions vitales ».
Autre problème rigolo, et qui a de quoi inquiéter, le risque élevé d'une cessation du service de mise à jour logicielle (section 5 du RFC). Les vendeurs de logiciel ne sont pas un service public : ils peuvent décider d'arrêter de fournir un service pour des produits qui ne les intéressent plus (ou qui fait concurrence à un produit plus récent, qui offre une meilleure marge). Ou bien ils peuvent tout simplement faire faillite. Que faire face à cet échec du capitalisme à gérer les produits qu'il met sur le marché ? Le problème est d'autant plus fréquent que des objets connectés peuvent rester en service des années, une éternité pour les directions commerciales. Outre le cas du Nabaztag cité plus haut, le RFC mentionne l'exemple d'Eyefi. Un objet un peu complexe peut incorporer des parties faites par des organisations très différentes, dont certaines seront plus stables que d'autres.
Faut-il envisager un service de reprise de la maintenance, par des organisations spécialisées dans ce ramassage des orphelins ? Si ces organisations sont des entreprises à but lucratif, quel sera leur modèle d'affaires ? (Le RFC ne mentionne pas la possibilité qu'il puisse s'agir d'un service public.) Et est-ce que l'entreprise qui abandonne un produit va accepter ce transfert de responsabilité (aucune loi ne la force à gérer ses déchets logiciels) ?
Pour le logiciel, une grosse différence apparait selon qu'il s'agisse de logiciel libre ou pas. Un projet de logiciel libre peut se casser la figure (par exemple parce que l'unique développeur en a eu marre), mais il peut, techniquement et juridiquement, être repris par quelqu'un d'autre. Ce n'est qu'une possibilité, pas une certitude, et des tas de projets utiles ont été abandonnés car personne ne les a repris.
Néanmoins, si le logiciel libre n'est pas une condition suffisante pour assurer une maintenance sur le long terme, c'est à mon avis une condition nécessaire. Parfois, un logiciel est libéré lorsqu'une société abandonne un produit (ce fut le cas avec Little Printer, qui fut un succès, la maintenance étant repris par des développeurs individuels), parfois la société ne fait rien, et le logiciel est perdu. Bref, un système de séquestre du code, pour pouvoir être transmis en cas de défaillance de l'entreprise originale, serait une bonne chose.
Comme souvent en sécurité, domaine qui ne supporte pas le « ya ka fo kon », certaines exigences sont contradictoires. Par exemple, on veut sécuriser les mises à jour (pour éviter qu'un pirate ne glisse de fausses mises à jour dans le processus), ce qui impose des contrôles, par exemple par des signatures numériques. Mais on veut aussi assurer la maintenance après la fin de la société originelle. Si un prestataire reprend la maintenance du code, mais qu'il n'a pas la clé privée permettant de signer les mises à jour, les objets refuseront celles-ci… Le séquestre obligatoire de ces clés peut être une solution, mais elle introduit d'autres risques (vol des clés chez le notaire).
Une solution possible serait que les appareils connectés soient programmés pour cesser tout service s'ils n'arrivent pas à faire de mise à jour pendant N mois, ou bien si le serveur des mises à jour leur dit que le contrat de support est terminé. De telles bombes temporelles seraient bonnes pour la sécurité, et pour le business des fabricants. Mais elles changeraient le modèle d'achat d'équipement : on ne serait plus propriétaire de sa voiture ou de son grille-pain, mais locataire temporaire. De la vraie obsolescence programmée ! De telles bombes dormantes peuvent aussi interférer avec d'autres fonctions de sécurité. Par exemple, un téléphone doit pouvoir passer des appels d'urgence, même si l'abonnement a expiré. Il serait logique qu'il soit en mesure d'appeler le 112, même si son logiciel n'a pas pu être mis à jour depuis longtemps (cf. RFC 7406 pour une discussion sur un compromis similaire.)
Dans une logique libertarienne, ce RFC parle peu de la possibilité d'obligations légales (comme il y a un contrôle technique pour les voitures, il pourrait y avoir obligation de mesures de sécurité quand on vend des objets connectés). Comme il n'y a aucune chance que des entreprises capitalistes fassent spontanément des efforts pour améliorer la sécurité de tous, si on n'utilise pas de contraintes légales, il reste la possibilité d'incitations, discutée dans la section 6. Certaines sont utopiques (« il est dans l'intérêt de tous d'améliorer la sécurité, donc il faut expliquer aux gentils capitalistes l'intérêt de la sécurité, et ils le feront gratuitement »). Une possibilité est celle d'un abonnement : ayant acheté un objet connecté, on devrait payer régulièrement pour le maintenir à jour. Du point de vue de la sécurité, c'est absurde : il est dans l'intérêt de tous que toutes les machines soient à jour, question sécurité. Avec un abonnement, certains propriétaires choisiront de ne pas payer, et leurs engins seront un danger pour tous. (Comparons avec la santé publiqué : il est dans l'intérêt des riches que les pauvres se soignent, sinon, les maladies contagieuses se répandront et frapperont tout le monde. Les vaccinations gratuites ne sont donc pas de la pure générosité.)
Un des problèmes du soi-disant « Internet des objets » est qu'on ne sait pas grand'chose de ce qui s'y passe. Combien de constructeurs d'objets connectés peuvent dire combien ils ont d'objets encore actifs dans la nature, et quel pourcentage est à jour, question logiciel ? La section 7 du RFC se penche sur les questions de mesures. Un des articles présentés à l'atelier notait que, treize ans après la fin de la distribution d'un objet connecté, on en détectait encore des exemplaires actifs.
Garder trace de ces machines n'est pas trivial, puisque certaines ne seront allumées que de manière très intermittente, et que d'autres seront déployées dans des réseaux fermés. Mais ce serait pourtant utile, par exemple pour savoir combien de temps encore il faut gérer des mises à jour pour tel modèle, savoir quels problèmes ont été rencontrés sur le terrain, savoir quelles machines ont été compromises, etc.
La solution simple serait que l'objet ait un identifiant unique et contacte son fabricant de temps en temps, en donnant des informations sur lui-même. Évidemment, cela soulève de gros problèmes de préservation de la vie privée, si le fabricant de brosses à dents connait vos habitudes d'hygiène (« sa brosse à dents n'a pas été allumée depuis six mois, envoyons-lui des pubs pour un dentiste »). Il existe des techniques statistiques qui permettent de récolter des données sans tout révéler, comme le système RAPPOR ou EPID.
Quelle que soit l'efficacité des mises à jour, il ne fait pas de doute que, parfois, des machines seront piratées. Comment les gérer, à partir de là (section 9) ? Car ce ne sera pas seulement un ou deux objets qu'il faudra reformater avec une copie neuve du système d'exploitation. En cas de faille exploitée, ce seront au moins des centaines ou des milliers d'objets qui auront été piratés par un logiciel comme Mirai. (Notez qu'un travail - qui a échoué - avait été fait à l'IETF sur la gestion des machines terminales et leur sécurité, NEA.)
Rien que signaler à l'utilisateur, sans ouvrir une nouvelle voie d'attaque pour le hameçonnage, n'est pas évident (le RFC 6561 propose une solution, mais peu déployée). Bref, pour l'instant, pas de solution à ce problème. Quand on sait que les organisations à but lucratif ne réagissent jamais quand on leur signale un bot dans leur réseau, on imagine ce que ce sera quand on préviendra M. Michu que son grille-pain est piraté et participe à des DoS (comme celles avec SNMP ou bien celle contre Brian Krebs).
Avant d'arriver à une conclusion très partielle, notre RFC, dans sa section 10, traite quelques points divers :
La section 11 rassemble les conclusions de l'atelier. On notera qu'il y a peu de consensus.
L'annexe B du RFC liste tous les papiers présentés à l'atelier qui sont, évidemment en ligne. Sinon, vous pouvez regarder le site officiel de l'atelier, et un bon résumé de présentation. Je recommande aussi cet article de l'IETF Journal, et un très bon article critique sur les objets connectés.
Première rédaction de cet article le 4 septembre 2017
L'article de Geoff Huston « BGP More Specifics: Routing Vandalism or Useful? » aborde une question qui a toujours déclenché de chaudes discussions dans les forums d'opérateurs réseaux : est-ce que les opérateurs qui, au lieu d'annoncer un et un seul préfixe IP très général, annoncent plusieurs sous-préfixes, sont des salauds ou pas ? Est-ce l'équivalent de polluer et de taguer ? Après une étude soignée et pas mal de graphiques, il apporte une réponse originale.
Comme d'habitude, je vais commencer par un petit rappel. Le
protocole BGP, normalisé dans le RFC 4271, est le protocole standard de
distribution des routes dans
l'Internet. Une route est annoncée sous forme
d'un préfixe IP, par exemple 2001:db8::/32
,
et les routeurs avec qui on communique relaieront cette annnonce,
jusqu'à ce que tout l'Internet soit au courant. Le préfixe annoncé
inclut une longueur (32 bits dans l'exemple ci-dessus). Comme des
expressions comme « grand préfixe » sont ambiguës (parle-t-on de
la longueur du préfixe, ou bien du nombre d'adresses IP qu'il contient ?), on
parle de préfixes plus spécifiques (longueur plus
grande, moins d'adresses IP) ou moins
spécifiques (longueur plus réduite, davantage
d'adresses IP). Ainsi, 2001:db8:42:abcd::/64
est
plus spécifique que 2001:db8:42::/48
.
Si un opérateur a le préfixe
2001:db8::/32
, et qu'il annonce en
BGP non seulement ce préfixe mais également
2001:db8:42::/48
et
2001:db8:cafe::/48
, on dit qu'il annonce des
préfixes plus spécifiques. Comme toute route va devoir être
stockée dans tous les routeurs de la
DFZ, cet opérateur qui annonce
trois routes au lieu d'une seule est vu par
certains comme un gougnafier qui abuse du système (et, là, on
cite en général la fameuse « Tragedy of the
Commons »). Cet opérateur force tous
les routeurs de la DFZ à consacrer des ressources
(CPU, mémoire) à ses
trois préfixes, et cela gratuitement, alors qu'il pourrait se
contenter d'un seul préfixe, qui englobe les trois en
question.
Le problème est d'autant plus complexe que l'Internet est un réseau décentralisé, sans chef et sans police, et que personne ne peut faire respecter des règles mondiales. Donc, même si on pense que les plus spécifiques sont une mauvaise chose, comment les interdire en pratique ?
En général, les opérateurs estiment que les annonces plus
spécifiques sont une mauvaise chose. Néanmoins, pas mal de gens en
font. Huston regarde de plus près pourquoi, et quelles sont les
conséquences. Les plus spécifiques peuvent être utiles pour leur
émetteur, car la transmission de paquets
IP est fondée sur le principe du
« préfixe le plus spécifique » : si un
routeur IP a deux routes pour la destination
2001:db8:cafe::1:443
, la première pour le préfixe
2001:db8::/32
, et la seconde pour le
préfixe
2001:db8:cafe::/48
, il utilisera toujours la
seconde, la plus spécifique. (Si, par contre, il doit envoyer un
paquet à 2001:db8:1337::bad:dcaf
, il
utilisera la première route, puisque la seconde ne couvre pas
cette destination.) L'utilisation de préfixes plus spécifiques peut
donc être un moyen de déterminer plus finement le trajet
qu'emprunteront les paquets qui viennent vers vous.
Quel est le pourcentage de ces « plus spécifiques », d'ailleurs ? Huston mesure environ 50 % des routes IPv4 et 40 % en IPv6. C'est donc une proportion non négligeable. La question est « peut-on / doit-on en réduire le nombre ? Est-ce possible ? Quels sont les coûts et les bénéfices ? »
Pour répondre à cette question, Huston développe une taxonomie
des annonces plus spécifiques, avec des exemples réels. (Les exemples ci-dessous ont été
vérifiés mais attention, le looking
glass typique n'indique que le préfixe le
plus spécifique ; si on n'a pas soi-même une copie de la DFZ et les
outils pour l'analyser, il faut utiliser un service qui affiche
également les préfixes englobants - je me sers de RIPE
Stat et un looking
glass - j'utilise celui
de Hurricane Electric.) Huston distingue trois cas. Le premier
est celui de « percement d'un trou » : l'annonce plus générale
n'envoie pas au bon AS et on fait donc une
annonce plus spécifique avec un AS d'origine différent. On voit
ainsi, pour 72.249.184.0/24
, sur RIPE Stat :
Originated by: AS394094 (valid route objects in LEVEL3 and RADB) Covering less-specific prefixes: 72.249.128.0/18 (announced by AS30496) 72.249.184.0/21 (announced by AS36024) Showing results for 72.249.184.0/24 as of 2017-09-03 08:00:00 UTC
Le /21 est annoncé par l'AS 36024 alors qu'il y a une annonce plus spécifique pour le /24, avec un autre AS, 394094. Huston note que cela pourrait être fait en annonçant un préfixe par /24 mais cela créerait davantage d'entrées dans la table de routage que ce percement de trou avec un plus spécifique.
Le second cas d'annonce plus spécifique est l'ingénierie de trafic. L'AS d'origine est le même, mais l'annonce est différente sur un autre point, par exemple par le prepending (la répétition du même AS dans l'annonce, pour rendre le chemin plus long et donc décourager l'usage de cette route), ou bien en envoyant les deux préfixes, le plus spécifique et le moins spécifique, à des opérateurs différents :
Originated by: AS4775 (valid route objects in RADB and NTTCOM) Covering less-specific prefix: 1.37.0.0/16 (announced by AS4775) Showing results for 1.37.27.0/24 as of 2017-09-03 08:00:00 UTC
Cette fois, l'AS d'origine est le même. Mais on note sur le looking glass que les annonces sont différentes :
Prefix: 1.37.0.0/16 AS_PATH: 4766 4775 Prefix: 1.37.27.0/24 AS_PATH: 4775
Le préfixe plus spécifique n'est pas annoncé aux mêmes opérateurs.
Enfin, le troisième et dernier cas est celui du recouvrement complet : l'annonce plus spécifique est apparemment inutile puisque il existe une annonce moins spécifique qui a exactement les mêmes caractéristiques. Pourquoi ferait-on cela ? Il peut s'agir de négligence ou d'incompétence mais il existe aussi une raison rationnelle : limiter les conséquences d'un détournement BGP.
Dans un tel détournement, l'attaquant annonce un préfixe qui n'est pas le sien, pour perturber l'accès à la victime, ou pour détourner son trafic vers l'attaquant (les cas les plus connus sont accidentels, comme le fameux détournement de YouTube par le Pakistan, mais cela peut aussi se faire délibérement). Si l'annonce normale est un /22, et que l'attaquant annonce aussi un /22, une partie de l'Internet verra toujours l'annonce légitime (les routeurs BGP ne propagent, pour un préfixe donné, que la meilleure route) et ne sera donc pas affectée. Pour qu'une attaque réussisse bien, dans ces conditions, il faut que l'attaquant soit très bien connecté, avec de nombreux partenaires BGP. Par contre, avec cette annonce légitime en /22, un attaquant qui enverrait une annonce pour un /24 verrait son annonce propagée partout (puisqu'il s'agirait d'un préfixe différent). Et, au moment de la transmission des paquets, les routeurs utiliseront la route la plus spécifique, donc le /24. Ainsi, un attaquant mal connecté pourra toujours voir son annonce acceptée et utilisée dans tout l'Internet.
Le fait d'annoncer un recouvrement complet (quatre /24 en plus du /22) protège donc partiellement contre cette technique. Huston rejette cet argument en disant qu'il ne s'agit que d'une protection partielle, mais je ne suis pas d'accord : une protection partielle vaut mieux que pas de protection du tout et, en sécurité, il est courant que les solutions soient partielles.
Un exemple est 1.0.4.0/22. On voit les quatre préfixes plus spécifiques, avec exactement le meme contenu :
Prefix: 1.0.4.0/22 AS_PATH: 4826 38803 56203 Prefix: 1.0.4.0/24 AS_PATH: 4826 38803 56203 Prefix: 1.0.5.0/24 AS_PATH: 4826 38803 56203 Prefix: 1.0.6.0/24 AS_PATH: 4826 38803 56203 Prefix: 1.0.7.0/24 AS_PATH: 4826 38803 56203
Les annonces plus spécifiques forment donc la moitié (en IPv4) des annonces dans la DFZ. Mais ce pourcentage augmente-t-il ou diminue-t-il ? Est-ce que la situation s'aggrave ? Huston utilise les données BGP accumulées depuis dix ans (si vous voulez faire pareil, les annonces BGP sont archivées et disponibles, entre autres à RouteViews). Eh bien, il n'y a pas de changement en IPv4 : le pourcentage de 50 % des annonces étant des annonces plus spécifiques qu'une annonce existante n'a pas changé depuis dix ans (mais la proportion des trois cas a changé : lisez l'article). En revanche, il est passé de 20 à 40 % en IPv6 dans le même temps, mais je ne suis pas sûr qu'on puisse en tirer des conclusions solides : il y a dix ans, IPv6 était peu présent dans la DFZ.
Ça, c'était le pourcentage des préfixes. Le nombre de préfixes à gérer a un effet négatif sur le routeur, car davantage de préfixes veut dire davantage de mémoire consommée par le routeur. La DFZ a actuellement plus de 700 000 préfixes (IPv4 et IPv6 mêlés). Une table de seulement 700 000 entrées ? Cela peut sembler peu à l'ère de la vidéo de chats en haute définition et du big data. Certes, un routeur du cœur de l'Internet n'a pas la même architecture qu'un PC de bureau, et la mémoire n'y est pas disponible dans les mêmes quantités mais, quand même, est-ce si grave ?
En fait, le plus gênant pour le routeur typique n'est pas la quantité de préfixes mais le rythme de changement (le churn). Davantage de préfixes veut surtout dire davantage de changements à traiter. Huston regarde donc dans la suite de son article l'effet des changements. Il constate que les préfixes plus spécifiques sont un peu plus « bruyants » (davantage de changements), au moins en IPv4. Les préfixes plus spécifiques du deuxième cas (ingénierie de trafic) sont les plus bruyants, ce qui est assez logique. Attention avec ces statistiques, toutefois : Huston rappelle que le rythme de changement varie énormément selon les préfixes. Certains changent tout le temps, d'autres sont très stables.
Maintenant, venons-en aux actions possibles. Est-ce que ces préfixes plus spécifiques, émis par des opérateurs égoïstes et payés par tous, doivent être activement combattus, pour en réduire le nombre ? C'est l'idée qui est derrière des actions comme le CIDR report. En analysant automatiquement les préfixes de la DFZ, et en publiant la liste des opérateurs qui abusent le plus, on espère leur faire honte (cette technique du name and shame est très courante sur Internet, puisqu'il n'existe pas d'État central qui puisse faire respecter des règles et que les opérateurs n'ont, heureusement, pas de police ou d'armée privée), et diminuer avec le temps le pourcentage de préfixes « inutiles ». Par exemple, Huston calcule que la suppression de tous les plus spécifiques du cas 3 (recouvrement complet) diminuerait la table de routage globale de 20 % et le rythme des changements de 10 à 15 %.
En conclusion, Huston estime qu'on ne peut pas parler de tous les préfixes moins spécifiques de la même façon, certains sont réellement utiles. Certes, d'autres ne le sont pas, mais les supprimer serait une tâche longue et difficile, pour un bénéfice limité. Huston résume en disant que les préfixes plus spécifiques sont un problème agaçant, mais pas grave.
First publication of this article on 1 September 2017
On 30 August, this year, a technical attack was performed against WikiLeaks, leading some visitors of WikiLeaks' Web site to see instead a claim by "OurMine" that they seized control of WikiLeaks' servers. A lot of stupid things, by ignorant people (both WikiLeaks fans and enemies), have been said on the networks, about this attack. Most of the time, they did not bother to check any facts, and they did not demonstrate any knowledge of the technical infrastructure. Here, I want to describe the bare facts, as seen from technical observations. Spoiler: I have no sensational revelations to make.
First, the basic fact: some people saw something which was obviously not WikiLeaks' Web site: screenshots of the page are here or here. Some people deduced from that that WikiLeaks' Web server was cracked and the crackers modified its content (you can find this in The Verge for instance). That was a bold deduction: the complete process from the user's browser to the display of the Web page is quite complicated, with a lot of actors, and many things can go wrong.
In the case of WikiLeaks, it appeared rapidly that the Web
server was not cracked but that the attack targeted successfully
the wikileaks.org
domain name. Observations
(more on that later) show that the name
wikileaks.org
was not resolved into the usual
IP address but in another one, located in a
different hoster. How is it possible? What are the
consequences?
You should remember that investigation of digital incidents on the Internet is difficult. The external analyst does not have all the data. Sometimes, when the analysis starts, it is too late, the data already changed. And the internal analyst almost never publishes everything, and sometimes even lies. There are some organisations that are more open in their communication (see this Cloudflare report or this Gandi report) but they are the exceptions rather than the rule. Here, WikiLeaks reacted like the typical corporation, denying the problem, then trying to downplay it, and not publishing anything of value for the users. So, most of the claims that you can read about network incidents are not backed by facts, specially not publicly-verifiable facts. The problem is obviously worse in that case, because WikiLeaks is at the center of many hot controversies. For instance, some WikiLeaks fans claimed from the beginning "WikiLeaks' servers have not been compromised" while they had zero actual information, and, anyway, not enough time to analyze it.
So, the issue was with the domain name
wikileaks.org
. To explain what happened, we
need to go back to the DNS, both a critical
infrastructure of the Internet, and a widely unknown (or underknown) technology. The DNS is a database indexed by
domain names (like
wikileaks.org
or
ssi.gouv.fr
). When you query the DNS for a
given domain name, you get various technical informations such as
IP addresses of servers, cryptographic keys, name of
servers, etc. When the typical Web browser goes to
http://www.okstate.com/
, the
software on the user's machine performs a DNS query for the name
www.okstate.com
, and gets back the IP address
of the HTTP server. It then connects to the
server.
From this very short description, you can see that s·he who controls the DNS controls where the user will eventually go and what s·he will see. And the entire DNS resolution process (from a name to the data) is itself quite complicated, offering many opportunities for an attacker. Summary so far: DNS is critical, and most organisations underestimate it (or, worse, claim it is not their responsability).
And where do the data in the DNS come from? That's the biggest
source of vulnerabilities: unlike what many people said, most
so-called "DNS attacks" are not DNS attacks at all, meaning they
don't exploit a weakness in the DNS protocol. Most of the time,
they are attacks against the provisioning
infrastructure, the set of actors and servers that domain name
holders (such as WikiLeaks for wikileaks.org
)
use to provision the data. Let's say you are Pat Smith,
responsible for the online activity of an organisation named the
Foobar Society. You have the domain name
foobar.example
. The Web site is hosted at
Wonderful Hosting, Inc. After you've choosen a
TLD (and I recommend you read the excellent EFF survey before you do so), you'll
typically need to choose a registrar which
will act as a proxy between you and the actual
registry of the TLD (here, the fictitious
.example
). Most of the time, you, Pat Smith,
will connect to the Web site of the registrar, create an account,
and configure the data which will ultimately appear in the
DNS. For instance, when the Web site is created at Wonderful Hosting, Pat will enter
its IP address in the control panel provided by the registrar. You
can see immediately that this required Pat to log in the said
control panel. If Pat used a weak password,
or wrote it down under h·is·er desk or if Pat is gullible and
believes a phone call asking h·im·er to give the password, the account may be
compromised, and the attacker may log in instead of Pat and put
the IP address of h·er·is choosing. This kind of attacks is very
common, and illustrate the fact that not all attacks are
technically complicated.
So, what happened in the WikiLeaks case? (Warning, it will now become more technical.) We'll first use a "passive DNS" base, DNSDB. This sort of databases observes the DNS traffic (which is most of the time in clear, see RFC 7626) and record it, allowing its users to time-travel. DNSDB is not public, I'm sorry, so for this one, you'll have to trust me. (That's why real-time reaction is important: when you arrive too late, the only tools to observe an attack are specialized tools like this one.) What's in DNSDB?
;; bailiwick: org. ;; count: 9 ;; first seen: 2017-08-30 04:28:40 -0000 ;; last seen: 2017-08-30 04:30:28 -0000 wikileaks.org. IN NS ns1.rivalhost.com. wikileaks.org. IN NS ns2.rivalhost.com. wikileaks.org. IN NS ns3.rivalhost.com. ;; bailiwick: org. ;; count: 474 ;; first seen: 2017-08-30 04:20:15 -0000 ;; last seen: 2017-08-30 04:28:41 -0000 wikileaks.org. IN NS ns1.rival-dns.com. wikileaks.org. IN NS ns2.rival-dns.com. wikileaks.org. IN NS ns3.rival-dns.com.
What does it mean? That during the attack (around 04:30
UTC), the
.org
registry was
replying with the illegitimate set of servers. The usual servers
are (we use the dig tool, the best tool to
debug DNS issues):
% dig @a0.org.afilias-nst.info. NS wikileaks.org ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21194 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 3 ... ;; AUTHORITY SECTION: wikileaks.org. 86400 IN NS ns1.wikileaks.org. wikileaks.org. 86400 IN NS ns2.wikileaks.org. ;; ADDITIONAL SECTION: ns1.wikileaks.org. 86400 IN A 46.28.206.81 ns2.wikileaks.org. 86400 IN A 46.28.206.82 ... ;; SERVER: 2001:500:e::1#53(2001:500:e::1) ;; WHEN: Fri Sep 01 09:18:14 CEST 2017 ...
(And, yes, there is a discrepancy between what is served by the
registry and what's inside nsX.wikileaks.org
name servers: whoever manages WikiLeaks DNS does a sloppy
job. That's why it is often useful to query the parent's name
servers, like I did here.)
So, the name servers were changed, for rogue ones. Note there was also a discrepancy during the attack. These rogue servers gave a different set of NS (Name Servers), according to DNSDB:
;; bailiwick: wikileaks.org. ;; count: 1 ;; first seen: 2017-08-31 02:02:38 -0000 ;; last seen: 2017-08-31 02:02:38 -0000 wikileaks.org. IN NS ns1.rivalhost-global-dns.com. wikileaks.org. IN NS ns2.rivalhost-global-dns.com.
Note that it does not mean that the DNS hoster of the attacker, Rival, is an accomplice. They may simply have a rogue customer. Any big service provider will have some rotten apples among its clients.
You can see the date of the last change in whois output, when everything was put back in place:
% whois wikileaks.org ... Updated Date: 2017-08-31T15:01:04Z
Surely enough, the rogue name servers were serving IP addresses pointing to the "false" Web site. Again, in DNSDB:
;; bailiwick: wikileaks.org. ;; count: 44 ;; first seen: 2017-08-30 04:29:07 -0000 ;; last seen: 2017-08-31 07:22:05 -0000 wikileaks.org. IN A 181.215.237.148
The normal IP addresses of WikiLeaks are in the prefixes
95.211.113.XXX
,
141.105.XXX
and
195.35.109.XXX
(dig A
wikileaks.org
if you want to see
them, or use a
DNS Looking Glass). 181.215.237.148
is the address of the
rogue Web site, hosted by Rival again, as can be seen with the
whois tool:
% whois 181.215.237.148 inetnum: 181.215.236/23 status: reallocated owner: Digital Energy Technologies Chile SpA ownerid: US-DETC5-LACNIC responsible: RivalHost, LLC. address: Waterwood Parkway, 1015, Suite G, C-1 address: 73034 - Edmond - OK country: US owner-c: VIG28 tech-c: VIG28 abuse-c: VIG28 ... nic-hdl: VIG28 person: AS61440 Network Operating Center e-mail: noc@AS61440.NET address: Moneda, 970, Piso 5 address: 8320313 - Santiago - RM country: CL
(It also shows that this prefix was allocated in Chile, the world is a complicated place, and the Internet even more so.)
So, this was the modus operandi of the
cracker. S·he managed to change the set of name servers serving
wikileaks.org
, and that gave h·im·er the
ability to send visitors to a server s·he controlled. (Note that
this HTTP server, 181.215.237.148
, no longer serves
the cracker's page: it was probably removed by the provider.)
Many people on the social networks claimed that the attack was done by "DNS poisoning". First, a word of warning by a DNS professional: when someone types "DNS poisoning", you can be pretty sure s·he knows next to nothing about DNS. DNS poisoning is a very specific attack, for which we have solutions (DNSSEC, mentioned later), but it does not seem to be very common (read again my warning at the beginning: most attacks are never properly analyzed and documented, so it is hard to be more precise). What is very common are attacks against the domain name provisioning system. This is, for instance, what happened to the New York Times in 2013, from an attack by the infamous SEA (see NYT paper and a technical analysis). More recently, there was the attack against St. Louis Federal Reserve and many others. These attacks don't use the DNS protocol and it is quite a stretch to label them as "DNS attacks" or, even worse, "DNS poisoning".
What are the consequences of such an attack? As explained earlier, once you control the DNS, you control everything. You can redirect users to a Web site (not only the external visitors, but also the employees of the targeted organisation, when they connect to internal services, potentially stealing passwords and other informations), hijack the emails, etc. So, claiming that "the servers were not compromised" (technically true) is almost useless. With an attack on domain names, the cracker does not need to compromise the servers.
Who was cracked in the WikiLeaks case? From the outside, we can
say with confidence that the name servers were changed. The
weakness could have been at the holder (WikiLeaks), at the
registrar (Dynadot, an
information you also get with whois), or
at the registry (.org
, administratively
managed by PIR and technically by
Afilias). From the information available,
one cannot say where the problem was (so, people who publicly
shouted that "WikiLeaks is not responsible" were showing their
blind faith, not their analytic abilities). Of course, most of the
times, the weakest link is the user (weak password to the registrar
portal, and not activating 2FA), but some registrars or
registries displayed in the past serious security weaknesses. The
only thing we can say is that no other domain name appeared to have
been hijacked. (When someone takes control of a registrar or
registry, s·he can change many domain names.)
I said before that, when you control a domain name, you can send both external and internal visitors to the server you want. That was not entirely true, since good security relies on defence in depth and some measures can be taken to limit the risk, even if your domain name is compromised. One of them is of course having HTTPS (it is the case of WikiLeaks), with redirection from the plain HTTP site, and HSTS (standardized in RFC 6797), to avoid that regular visitors go through the insecure HTTP. Again, WikiLeaks use it:
% wget --server-response --output-document /dev/null https://wikileaks.org/ ... Strict-Transport-Security: max-age=25920000; includeSubDomains; preload
These techniques will at least raise an alarm, telling the visitor that something is fishy. (There is also HPKP - RFC 7649 - but it does not seem deployed by Wikileaks; it should be noticed it is more risky.)
In the same way, using Tor to go to a
.onion
URL would also help. But I have not been
able to find a .onion
for WikiLeaks (the
http://suw74isz7wqzpmgu.onion/
indicated on
the
wiki does not work, the
http://wlupld3ptjvsgwqw.onion
seems to be just
for uploading).
One can also limit the risk coming from an account compromise by
enabling registry lock, a technique offered by most TLD (including
.org
) to prevent
unauthorized changes. When activated, it requires extra steps and
checking for any change. I cannot say, from the outside, if
WikiLeaks enabled it but sensitive domain names
must do it.
Funny enough, with so many people claiming it was "DNS
poisoning", the best protection against this specific attack,
DNSSEC, is not enabled
by WikiLeaks (there is a DNSSEC key in
wikileaks.org
but no signatures and no
DS record in the parent). If
wikileaks.org
was signed, and if you
use a validating DNS resolver (everybody should), you cannot fall
for a DNS poisoning attack against WikiLeaks. Of course, if the
attack is, instead, a compromise of holder account, registrar or
registry, DNSSEC would not help a lot.
A bit of technical fun at the end. WikiLeaks uses
glue records for its name servers. They are
nameserver names which are under the domain they serve, thus
creating a chicken-and-egg problem. To
allow the DNS client to query them, the parent has to know the IP
address of this name server. This is what is called a glue
record. DNSDB shows us that the glue for
ns1.wikileaks.org
was apparently modified
(note that it was several hours after the main attack):
;; bailiwick: org. ;; count: 546 ;; first seen: 2017-08-31 00:23:13 -0000 ;; last seen: 2017-08-31 06:22:42 -0000 ns1.wikileaks.org. IN A 191.101.26.67
This machine is still up and serves a funny value for
wikileaks.org
(again, you can use a
DNS Looking Glass):
% dig @191.101.26.67 A wikileaks.org ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53887 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ... ;; ANSWER SECTION: wikileaks.org. 400 IN A 127.0.0.1
This IP address, meaning
localhost
, was
indeed seen by some DNSDB sensors:
;; bailiwick: wikileaks.org. ;; count: 1 ;; first seen: 2017-08-31 09:17:29 -0000 ;; last seen: 2017-08-31 09:17:29 -0000 wikileaks.org. IN A 127.0.0.1
Since the DNS heavily relies on caching, the information was still seen even after the configuration was fixed. Here, we use the RIPE Atlas probes with the atlas-resolve tool to see how many probes still saw the wrong value (pay attention to the date and time, all in UTC, which is the rule when analyzing Internet problems):
% atlas-resolve -r 1000 -t A wikileaks.org [141.105.65.113 141.105.69.239 195.35.109.44 195.35.109.53 95.211.113.131 95.211.113.154] : 850 occurrences [195.175.254.2] : 2 occurrences [127.0.0.1] : 126 occurrences Test #9261634 done at 2017-08-31T10:03:24Z
Date de publication du RFC : Août 2017
Auteur(s) du RFC : R. Pantos (Apple), W. May (Major
League Baseball Advanced Media)
Pour information
Première rédaction de cet article le 1 septembre 2017
Ce RFC décrit la version 7 du protocole HTTP Live Streaming, qui permet de distribuer des flux multimédia, typiquement de la vidéo.
Utiliser HTTP, c'est bien, ça passe
quasiment tous les pare-feux, et ça permet
de réutiliser l'infrastructure existante, comme les serveurs, et les caches. (Au contraire, un protocole
comme RTP, RFC 3550, a souvent du mal à passer les
différentes
middleboxes.) La manière triviale de distribuer une vidéo avec
HTTP est d'envoyer un fichier vidéo au bon
format, avec des méthodes HTTP standard, typiquement en réponse à
un GET
. Mais cette méthode a des tas de
limites. Par exemple, elle ne permet pas de
s'adapter à une capacité variable du
réseau. Par contre, le protocole décrit dans ce RFC, quoique plus
compliqué qu'un simple GET
, permet cet
ajustement (et plein d'autres).
HTTP Live Streaming (ou simplement Live Streaming, ou encore HLS) existe depuis longtemps, la première spécification a été publiée (par Apple) en 2009, mais c'est la première fois qu'il se retrouve dans un RFC, ce qui devrait permettre d'améliorer encore l'interopérabilité.
Donc, brève description de HTTP Live Streaming : une ressource multimédia qu'on veut diffuser est représentée par un URI. Ceui-ci désigne une playlist, qui est un fichier texte contenant les URI des données multimedia et diverses informations. Les données multimédia figurent dans plusieurs segments ; en jouant tous les segments successivement, on obtient la vidéo souhaitée. Le client n'a pas ainsi à tout charger, il peut récupérer les segments au fur et à mesure de ses besoins. (Au lieu de « segments », mmu_man suggère de parler d'un « émincé de fichiers ».) Voici l'exemple de playlist que donne le RFC :
#EXTM3U #EXT-X-TARGETDURATION:10 #EXTINF:9.009, http://media.example.com/first.ts #EXTINF:9.009, http://media.example.com/second.ts #EXTINF:3.003, http://media.example.com/third.ts
Ce format de playlist est dérivé du format
M3U et est généralement nommé « M3U étendu ».
On y trouve successivement l'identificateur du format (si ça
commence par #EXTM3U
, c'est bien une
playlist de notre RFC), la durée maximale d'un
segment en secondes, puis les trois segments, avec la durée de
chacun. Cette durée, indiquée par l'étiquette
#EXTINF
, est un des rares éléments
obligatoires dans une playlist. Il y a bien sûr plein d'autres choses qu'on peut mettre
dans un fichier playlist, la section 4 du RFC
en donne la liste complète. Ainsi,
#EXT-X-BYTERANGE
permettra d'indiquer qu'on
ne veut jouer qu'une partie d'une vidéo. Des exemples plus
complets sont donnés dans la section 8 du RFC.
Ça, c'était la media playlist, qui se limite à lister des ressources multimédias. La master playlist, dont le format est le même, est plus riche, et permet de spécifier des versions alternatives d'un même contenu. Ces variantes peuvent être purement techniques (« variant stream », deux encodages d'une même vidéo avec différents formats) ou porter sur le fond : par exemple deux pistes audio dans des langues différentes, ou deux vidéos d'un même évènement filmé depuis deux points de vue distincts. Les variantes techniques servent au client à s'ajuster aux conditions réelles du réseau, les variantes de fond (nommées renditions) servent au client à donner à l'utilisateur ce qu'il préfère. Ici, par exemple, on a dans cette master playlist trois variantes techniques (trois résolutions, de la plus basse, à la plus élevée, qui va nécessiter une bonne capacité réseau), et trois « renditions », correspondant à trois points de vue (centerfield et dugout sont des termes de baseball, ce qui est logique vu l'employeur d'un des deux auteurs du RFC) :
#EXTM3U #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Main", \ DEFAULT=YES,URI="low/main/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Centerfield", \ DEFAULT=NO,URI="low/centerfield/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Dugout", \ DEFAULT=NO,URI="low/dugout/audio-video.m3u8" #EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",VIDEO="low" low/main/audio-video.m3u8 #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Main", \ DEFAULT=YES,URI="mid/main/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Centerfield", \ DEFAULT=NO,URI="mid/centerfield/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Dugout", \ DEFAULT=NO,URI="mid/dugout/audio-video.m3u8" #EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",VIDEO="mid" mid/main/audio-video.m3u8 #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Main", \ DEFAULT=YES,URI="hi/main/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Centerfield", \ DEFAULT=NO,URI="hi/centerfield/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Dugout", \ DEFAULT=NO,URI="hi/dugout/audio-video.m3u8" #EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",VIDEO="hi" hi/main/audio-video.m3u8
Si vous voulez le voir en vrai, et que vous êtes abonné à Free, la Freebox distribue sa playlist sous ce format (mais avec le mauvais type MIME) :
% curl -s http://mafreebox.freebox.fr/freeboxtv/playlist.m3u #EXTM3U #EXTINF:0,2 - France 2 (bas débit) rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201&flavour=ld #EXTINF:0,2 - France 2 (HD) rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201&flavour=hd #EXTINF:0,2 - France 2 (standard) rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201&flavour=sd #EXTINF:0,2 - France 2 (auto) rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201 #EXTINF:0,3 - France 3 (standard) rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=202&flavour=sd ...
(On me dit qu'avec certaines Freebox, il faut ajouter
-L
à curl, en raison d'une redirection HTTP.)
Un client comme vlc peut prendre comme
argument l'URI de la playlist, la télécharger et jouer ensuite les
vidéos.
(Tous les moyens sont bons pour télécharger la playlist, le RFC n'impose pas un
mécanisme particulier.)
Télécharger chacun des segments se fait a priori avec
HTTP, RFC 7230, mais
vous voyez plus haut que, sur la Freebox,
c'est RTSP, RFC 7826. Le serveur, lui, est un serveur HTTP
ordinaire. L'auteur a « juste » eu à découper sa vidéo en segments (ou
la garder en un seul morceau si elle est assez courte), à
fabriquer un fichier playlist, puis à le servir
avec le type MIME application/vnd.apple.mpegurl
. (La
section 6 du RFC décrit plus en détail ce que doivent faire client
et serveur.)
Il est recommandé que chaque segment soit auto-suffisant, c'est-à-dire puisse être décodé sans avoir besoin des autres segments. Ainsi, si les segments sont en H.264, chacun doit avoir un Instantaneous Decoding Refresh (IDR).
Plusieurs formats sont évidemment possibles dans une playlist HTTP Live Streaming, MPEG-2, MPEG-4 fragmenté, WebVTT pour avoir les sous-titres, et un format pour l'audio (qui accepte entre autres MP3).
HTTP Live Streaming permet aussi de servir du direct. Prenons par exemple le flux vidéo de l'Assemblée Nationale, qui permet de suivre les débats parlementaires :
% curl -s http://videos-diffusion-live.assemblee-nationale.fr/live/live1/stream1.m3u8 #EXTM3U #EXT-X-VERSION:3 #EXT-X-ALLOW-CACHE:NO #EXT-X-TARGETDURATION:11 #EXT-X-MEDIA-SEQUENCE:382 #EXTINF:9.996, media_1_4805021_382.ts #EXTINF:11.483, media_1_4805021_383.ts ...
Un peu plus tard, les fichiers ont changé, le client HLS doit recharger la playlist régulièrement (section 6.2.2 du RFC), et charger de nouveaux fichiers vidéo :
% curl -s http://videos-diffusion-live.assemblee-nationale.fr/live/live1/stream1.m3u8 #EXTM3U #EXT-X-VERSION:3 #EXT-X-ALLOW-CACHE:NO #EXT-X-TARGETDURATION:11 #EXT-X-MEDIA-SEQUENCE:390 #EXTINF:10.039, media_1_4805021_390.ts #EXTINF:11.242, media_1_4805021_391.ts ...
(Notez également l'incrémentation du #EXT-X-MEDIA-SEQUENCE
.)
Ainsi, l'utilisateur a l'impression d'une vidéo continue, alors
que le logiciel client passe son temps à charger des segments différents et à les
afficher à la suite.
Notez que HTTP Live Streaming n'est pas le seul mécanisme dans ce domaine, son principal concurrent est MPEG-DASH.
Si vous voulez lire davantage :
Date de publication du RFC : Janvier 2013
Auteur(s) du RFC : P. Hallam-Baker (Comodo), R. Stradling
(Comodo)
Chemin des normes
Première rédaction de cet article le 31 août 2017
Dernière mise à jour le 26 octobre 2017
Ce RFC a quelques années mais n'avait connu que peu de déploiement effectif. Cela va peut-être changer à partir du 8 septembre 2017, où les AC ont promis de commencer à vérifier les enregistrements CAA (Certification Authority Authorization) publiés dans le DNS. Ça sert à quoi ? En gros, un enregistrement CAA publié indique quelles AC sont autorisées à émettre des certificats pour ce domaine. Le but est de corriger très partiellement une des plus grosses faiblesses de X.509, le fait que n'importe quelle AC peut émettre des certificats pour n'importe quel domaine, même si ce n'est pas un de ses clients. Notez qu'il a depuis été remplacé par le RFC 8659.
C'est donc une technique très différente de DANE (RFC 6698), les seuls points communs étant l'utilisation du DNS pour sécuriser les certificats. DANE est déployé chez le client TLS, pour qu'il vérifie le certificat utilisé, CAA est surtout dans l'AC, pour limiter le risque d'émission d'un certificat malveillant (par exemple, CAA aurait peut-être empêché le faux certificat Gmail du ministère des finances.) Disons que CAA est un contrôle supplémentaire, parmi ceux que l'AC doit (devrait) faire. Les clients TLS ne sont pas censés le tester (ne serait-ce que parce que l'enregistrement CAA a pu changer depuis l'émission du certificat, la durée de vie de ceux-ci étant en général de plusieurs mois). CAA peut aussi servir à des auditeurs qui veulent vérifier les pratiques d'une AC (même avertissement : le certificat a pu être émis alors que l'enregistrement CAA était différent.)
La section 3 de notre RFC présente l'enregistrement CAA (la syntaxe détaillée est en section 5). Il a été ajouté au registre des types d'enregistrements sous le numéro 257. Il comprend une série d'options (flags) et une propriété qui est sous la forme {clé, valeur}. Un nom peut avoir plusieurs propriétés. Pour l'instant, une seule option est définie (un registre existe pour les options futures), « issuer critical » qui indique que cette propriété est cruciale : si on ne la comprend pas, le test doit être considéré comme ayant échoué et l'AC ne doit pas produire de certificat.
Les propriétés possibles sont, à l'heure actuelle (d'autres pourront être ajoutée dans le registre IANA) :
issue
, la principale, qui indique une
AC autorisée à émettre des certificats pour ce domaine (l'AC est
indiquée par son nom de domaine),issuewild
, idem, mais avec en plus la
possibilité pour l'AC d'émettre des certificats incluants des
jokers,iodef
, qui indique où l'AC doit envoyer
d'éventuels rapports d'échec, pour que le titulaire du nom de
domaine puisse les corriger. Un URL est
indiqué pour cela, et le rapport
doit être au format IODEF (RFC 7970).Voici par exemple quel était l'enregistrement CAA de mon domaine personnel :
% dig CAA bortzmeyer.org ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61450 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 3, AUTHORITY: 7, ADDITIONAL: 7 ... ;; ANSWER SECTION: bortzmeyer.org. 26786 IN CAA 0 issue "cacert.org" bortzmeyer.org. 26786 IN CAA 0 issuewild "\;" ...
Il indique que seule l'AC CAcert peut
créer un certificat pour ce domaine (et sans les jokers). Bon, c'est
un peu inutile car CAcert ne teste pas les enregistrements CAA, mais
c'était juste pour jouer. Je n'ai pas mis d'iodef
mais il aurait pu être :
bortzmeyer.org. CAA 0 iodef "mailto:security@bortzmeyer.org"
Et, dans ce cas, l'AC peut écrire à
security@bortzmeyer.org
, avec le rapport IODEF
en pièce jointe.
Attention, l'enregistrement CAA est valable pour tous les
sous-domaines (et ce n'est pas une option,contrairement à, par
exemple, HSTS du RFC 6797, avec
son includeSubDomains
). C'est pour cela que
j'ai dû retirer l'enregistrement ci-dessus, pour avoir des
certificats pour les sous-domaines, certificats faits par une autre
AC.
Des paramètres peuvent être ajoutés, le RFC cite l'exemple d'un numéro de client :
example.com. CAA 0 issue "ca.example.net; account=230123"
Une fois les enregistrements CAA publiés, comment sont-ils utilisés (section 4) ? L'AC est censée interroger le DNS pour voir s'il y a un CAA (on note que DNSSEC est recommandé, mais n'est pas obligatoire, ce qui réduit le service déjà faible qu'offre CAA). S'il n'y en a pas, l'AC continue avec ses procédures habituelles. S'il y a un CAA, deux cas : il indique que cette AC peut émettre un certificat pour le domaine, et dans ce cas-là, c'est bon, on continue avec les procédures habituelles. Second cas, le CAA ne nomme pas cette AC et elle doit donc renoncer à faire un certificat sauf s'il y a une exception configurée pour ce domaine (c'est la deuxième faille de CAA : une AC peut facilement passer outre et donc continuer émettre de « vrais/faux certificats »).
Notez que le RFC ne semble pas évoquer la possibilité d'imposer la présence d'un enregistrement CAA. C'est logique, vu le peu de déploiement de cette technique mais cela veut dire que « qui ne dit mot consent ». Pour la plupart des domaines, la vérification du CAA par l'AC ne changera rien.
Notez que, si aucun enregistrement CAA n'est trouvé, l'AC est
censé remonter l'arbre du DNS. (C'est pour cela que SSL [sic]
Labs trouve un enregistrement CAA pour
mercredifiction.bortzmeyer.org
: il a utilisé
le CAA du domaine parent, bortzmeyer.org
.) Si example.com
n'a pas de CAA, l'AC va tester
.com
, demandant ainsi à
Verisign si son client peut avoir un
certificat et de qui. Cette erreur consistant à grimper sur l'arbre
avait déjà été dénoncée dans le RFC 1535, mais apparemment
la leçon n'a pas été retenue.
Enfin, la section 6 du RFC analyse les différents problèmes de sécurité que peut poser CAA :
Comme indiqué au début, le CA/Browser Forum a décidé que le test des CAA serait obligatoire à partir du 8 septembre 2017. (Cf. la décision.) Parmi les rares enregistrements CAA dans la nature, on trouve celui de Google, qui autorise deux AC :
% dig CAA google.com ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55244 ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ... ;; ANSWER SECTION: google.com. 86400 IN CAA 0 issue "pki.goog" google.com. 86400 IN CAA 0 issue "symantec.com" ...
(Le TLD .goog
est
apparemment utilisé par Google pour son infrastructure,
.google
étant plutôt pour les choses publiques.)
Notez que gmail.com
, lui, pourtant souvent
détourné par des gouvernements et des entreprises qui veulent
surveiller le trafic, n'a pas d'enregistrement CAA.
Le célèbre SSL [sic] Labs teste la présence d'un
enregistrement CAA. S'il affiche « DNS Certification Authority Authorization (CAA) Policy found for
this domain », c'est bon. Regardez le
cas de Google.
Le type CAA est relativement récent (quatre ans quand même), donc il crée des problèmes amusants avec les serveurs DNS bogués comme ceux de BNP Paribas, qui timeoutent lorsque le type demandé n'est pas A :
% dig CAA mabanqueprivee.bnpparibas ; <<>> DiG 9.10.3-P4-Debian <<>> CAA mabanqueprivee.bnpparibas ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 42545 ^^^^^^^^
Quelques lectures et ressources pour finir :
urn:acme:error:caa
, « CAA record for
DOMAIN_NAME prevents issuance ».Date de publication du RFC : Août 2017
Auteur(s) du RFC : A. Freytag
Pour information
Première rédaction de cet article le 23 août 2017
Le RFC 7940 définissait un langage, LGR (Label Generation Ruleset), pour exprimer des politiques d'enregistrement de noms écrits en Unicode, avec les éventuelles variantes. Par exemple, avec LGR, on peut exprimer une politique disant que les caractères de l'alphabet latin et eux seuls sont autorisés, et que « é » est considéré équivalent à « e », « ç » à « c », etc. Ce nouveau RFC donne quelques conseils aux auteurs LGR. (Personnellement, je n'ai pas trouvé qu'il apportait grand'chose par rapport au RFC 7940.)
LGR peut s'appliquer à tous les cas où on a des identificateurs en Unicode mais il a surtout été développé pour les IDN (RFC 5890). Les exemples de ce RFC 8228 sont donc surtout pris dans ce domaine d'application. Un ensemble de règles exprimées dans le langage du RFC 7940 va définir deux choses :
google
vs. goog1e
), mais c'est une notion bien trop
floue, et qui dépend en outre de la police de
caractères utilisée (avec
Courier, google
et goog1e
sont difficilement distinguables). Le RFC ajoute qu'il y a
également le cas de la similarité sémantique (le cas de
theatre vs. theater, ou
bien celui des sinogrammes traditionnels vs. les
simplifiés ou encore, mais c'est
compliqué, le cas des accents en
français, avec « café » et « cafe » mais
aussi avec « interne » et « interné »).L'idée est que le registre utilise ensuite ces règles pour accepter ou refuser un nom et, si le nom est accepté, pour définir quelles variantes deviennent ainsi bloquées, ou bien attribuées automatiquement au même titulaire. Notez une nouvelle fois qu'il s'agit d'une décision politique et non technique. Le RFC dit d'ailleurs prudemment dans la section 1 qu'il ne sait pas si ce concept de variante est vraiment utile à quoi que ce soit. (J'ajoute qu'en général ce soi-disant risque de confusion a surtout été utilisé comme FUD anti-Unicode, par des gens qui n'ont jamais digéré la variété des écritures humaines.)
J'ai dit qu'une variante était « le même » que le nom de
base. Du point de vue mathématique, la relation « est une
variante de » doit donc être symétrique et
transitive (section 2). « Est une variante
de » est noté avec un tiret donc
A - B
signifie « A est une variante de B »
(et réciproquement, vu la symétrie). Notez que le RFC 7940 utilisait un langage fondé sur
XML alors que ce RFC 8228
préfère une notation plus compacte. La section 18 de notre RFC
indique la correspondance entre la notation du RFC 7940 et celle du nouveau RFC.
Mais toutes les relations entre noms « proches » ne sont pas forcément symétriques et transitives. Ainsi, la relation « ressemble à » (le chiffre 1 ressemble à la lettre l minuscule) est non seulement vague (cela dépend en effet de la police) mais également non transitive (deux noms ressemblent chacun à un nom dont la forme est quelque part entre eux, sans pour autant se ressembler entre eux).
Le RFC 7940 permet de définir des relations qui ne sont ni symétriques, ni transitives. Mais ce nouveau RFC 8228 préfère les relations d'équivalence.
Bon, et maintenant, comment on crée les variantes d'un nom ou d'un composant d'un nom (section 6) ? On remplace chaque caractère du nom originel par toutes ses variantes possibles (le fait qu'un caractère correspond à un autre est noté -->). Ainsi, si on a décidé que « e » et « é » étaient équivalents (é --> e), le nom « interne » aura comme variantes possibles « interné », « intérne » et « intérné » (oui, une seule de ces variantes a un sens en français, je sais). Une fois cette génération de variantes faites, le registre, selon sa politique, pourra l'utiliser pour, par exemple, refuser l'enregistrement d'« interné » si « interne » est déjà pris (je le répète parce que c'est important : ce RFC ne décrit pas de politique, l'exemple ci-dessus n'est qu'un exemple).
En pratique, travailler caractère par caractère n'est pas toujours réaliste. Il y a des cas où c'est un groupe de caractères qui poserait problème. La section 7 introduit une amélioration où on peut étiqueter les correspondances de manière asymétrique. Le caractère x veut dire que l'allocation du nom est interdite (détails en section 9), le a qu'elle est autorisée (détails en section 8). On pourrait donc avoir « e x--> é » et « é a--> e » ce qui veut dire que la réservation de « interne » bloquerait celle d'« interné » mais pas le contraire.
Le monde des identificateurs étant très riche et complexe, on ne s'étonnera pas que des règles trop simples soient souvent prises en défaut. Ainsi, la variante peut dépendre du contexte : dans certaines écritures (comme l'arabe), la forme d'un caractère dépend de sa position dans le mot, et un même caractère peut donc entrainer de la confusion s'il est à la fin d'un mot mais pas s'il est au début. Il faut donc étiqueter les relations avec ce détail (par exemple, « final: C --> D » si le caractère noté C peut se confondre avec le caractère D mais uniquement si C est en fin de mot, cf. section 15 du RFC).
Si vous développez du LGR, au moins deux logiciels peuvent aider, celui de Viagénie, lgr-crore et lgr-django et un logiciel de l'auteur de ce RFC, développé pour le « ICANN Integration Panel work » mais qui ne semble pas publié.
Date de publication du RFC : Août 2017
Auteur(s) du RFC : T. Burbridge, P. Eardley (BT), M. Bagnulo (Universidad Carlos III de Madrid), J. Schoenwaelder (Jacobs University Bremen)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lmap
Première rédaction de cet article le 22 août 2017
Ce RFC est l'un de ceux du groupe de travail IETF nommé LMAP, pour « Large-Scale Measurement Platforms ». Ce groupe développe des normes pour les systèmes de mesure à grande échelle, ceux où des centaines ou des milliers de sondes quantifient l'Internet. Il décrit le modèle de données utilisé.
Le projet LMAP est décrit dans les RFC 7536 (qui explique à quoi sert LMAP, avec deux exemples concrets) et RFC 7594 (qui normalise le vocabulaire et le cadre général). Pour résumer très vite, LMAP prévoit un Measurement Agent (la sonde) qui parle à un contrôleur, qui lui dit quoi faire, et à un collecteur, qui ramasse les données. Ce nouveau RFC décrit le modèle de données abstrait utilisé dans ces deux communications.
Rappelons que, dans le cadre défini pour LMAP, chaque MA (Measurement Agent) parle à un et un seul contrôleur, qui lui dit ce qu'il doit mesurer. (LMAP sépare le rôle du contrôleur et du collecteur, rôles que beaucoup de systèmes déployés fusionnent.) Il y a donc trois protocoles en jeu, le protocole de contrôle, entre le contrôleur et les MA, le protocole de collecte, entre les collecteurs et les MA, et enfin les protocoles utilisés dans les tests (ICMP ECHO, par exemple, mais aussi HTTP, DNS…) Enfin, il y a aussi la configuration nécessaire dans tous ces équipements. Tous ces points peuvent faire l'objet d'un travail de normalisation, mais ce RFC 8193 présente uniquement un modèle de données adapté au protocole de collecte et au protocole de contrôle.
La notion de modèle de données est expliquée dans le RFC 3444. En gros, il s'agit d'un modèle abstrait (très abstrait) décrivant quel genre de données sont échangées. Il peut se formaliser de manière un peu plus concrète par la suite, par exemple en utilisant le langage YANG (c'est justement ce que fait le RFC d'accompagnement, le RFC 8194). Un tel modèle sert à guider la normalisation, à faciliter la traduction d'un protocole dans un autre s'ils utilisent le même modèle de données, et à définir les données que doit mesurer et conserver le MA.
Le modèle défini ici utilise une notation formelle, utilisant
des types de données classiques en programmation (section 3 du RFC) :
int
(un entier),
boolean
(une valeur
logique), string
, mais aussi des
types plus riches comme datetime
ou uri
.
Le modèle lui-même est dans la section 4 du RFC. Je rappelle qu'il suit le cadre du RFC 7594. Il a six parties :
Le MA peut également avoir d'autres informations, par exemple des détails sur son type de connectivité.
Pour donner des exemples concrets, je vais souvent citer les sondes RIPE Atlas, même si ce système, conçu avant le RFC, ne suis pas exactement ce modèle. C'est sans doute l'un des plus grands, voire le plus grand réseau de points de mesure dans le monde, avec près de 10 000 sondes connectées. Par exemple, pour la configuration, les informations de pré-configuration d'une sonde Atlas comprennent une adresse MAC et un identificateur, qu'on peut voir sur la sonde :
Les sondes sont également pré-configurées avec le nom du contrôleur (qui sert également de collecteur). Elles acquièrent le reste de leur configuration par DHCP et SLAAC. Regardons par exemple la sonde d'identificateur #21660, installée à Cochabamba, on voit les adresses IP obtenues :
Les sondes Atlas disposent également d'étiquettes (tags) décrivant plus précisement certaines de leurs caractéristiques. Certaines des étiquettes sont attribuées manuellement par leur propriétaire (en bleu sur l'image ci-dessous), d'autres, qui peuvent être déterminées automatiquement, sont mises par le système (en vert sur l'image), et rentrent donc dans le cadre de la partie « état général de la sonde » du modèle :
Les classes utilisées dans le modèle sont, entre autres :
Première partie, la pré-configuration (section 4.1). On y trouve les informations dont le MA aura besoin pour accomplir sa mission. Au minimum, il y aura un identificateur (par exemple l'adresse MAC), les coordonnées de son contrôleur (par exemple un nom de domaine, mais le RFC ne cite que le cas où ces coordonnées sont un URL, pour les protocoles de contrôle de type REST), peut-être des moyens d'authentification (comme le certificat de l'AC qui signera le certificat du contrôleur). URL et moyens d'authentification, ensemble, forment un canal de communication.
Dans le langage formel de ce modèle, cela fait :
object { [uuid ma-preconfig-agent-id;] ma-task-obj ma-preconfig-control-tasks<1..*>; ma-channel-obj ma-preconfig-control-channels<1..*>; ma-schedule-obj ma-preconfig-control-schedules<1..*>; [uri ma-preconfig-device-id;] credentials ma-preconfig-credentials; } ma-preconfig-obj;
Cela se lit ainsi : l'information de pré-configuration (type
ma-preconfig-obj
) comprend
divers attributs dont un identificateur (qui est un
UUID, on a vu que les Atlas utilisaient un
autre type d'identificateurs), des canaux de communication avec le
contrôleur, et des lettres de créance à présenter pour
s'authentifier. Notez que l'identificateur est optionnel (entre
crochets).
Vous avez compris le principe ? On peut passer à la suite plus rapidement (je ne vais pas répéter les déclarations formelles, voyez le RFC). La configuration (section 4.2) stocke les informations volatiles (la pré-configuration étant consacrée aux informations plus stables, typiquement stockées sur une mémoire permanente).
Les instructions (section 4.3) sont les tâches que va accomplir la sonde, ainsi que l'agenda d'exécution, et bien sûr les canaux utilisé pour transmettre les résultats. La journalisation (section 4.4) permet d'enregistrer les problèmes (« mon résolveur DNS ne marche plus ») ou les évènements (« le courant a été coupé »).
Il ne sert évidemment à rien de faire des mesures si on ne transmet pas le résultat. D'où la section 4.6, sur la transmission des résultats de mesure au collecteur. La communication se fait via les canaux décrits en section 4.8.
Une dernière photo, pour clore cet article, une sonde RIPE Atlas en fonctionnement, et une autre sonde, la SamKnows :
Merci à André Sintzoff pour avoir trouvé de grosses fautes.
Première rédaction de cet article le 9 août 2017
Aujourd'hui, énormément de données sont distribuées dans le format JSON. La simplicité de ce format, normalisé dans le RFC 8259, et bien sûr son utilisation dans JavaScript expliquent ce succès. Pour le transport de données (celui du texte est une autre histoire), JSON a largement remplacé XML. Il existe donc des bibliothèques pour traiter le JSON dans tous les langages de programmation. Mais si on veut une solution plus simple, permettant de traiter du JSON depuis la ligne de commande ? C'est là qu'intervient jq, qui permet à la fois de faire des opérations simples en ligne de commande sur des données JSON, et de programmer des opérations complexes, si nécessaires.
jq est parfois présenté comme « l'équivalent de sed pour JSON ». Ce n'est pas faux, puisqu'il permet en effet des opérations simples depuis la ligne de commande. Mais c'est assez court, comme description : jq est bien plus que cela, il inclut même un langage de programmation complet.
Commençons par quelques exemples simples. On va utiliser le JSON produit en utilisant l'outil madonctl pour récupérer des messages (les pouètes) envoyées sur Mastodon. Si je fais :
% madonctl --output json timeline :afnic [{"id":3221183,"uri":"tag:mastodon.social,2017-07-20:objectId=13220936:objectType=Status","url":"https://mastodon.soc ial/users/afnic/updates/3830115","account":{"id":37388,"username":"afnic","acct":"afnic@mastodon.social","display_nam e":"Afnic","note":"\u003cp\u003eLe registre des noms de domaine en .fr\u003cbr\u003eRdv sur \u003ca href=\"http://www .afnic.fr/\" rel=\"nofollow noopener\" target=\"_blank\"\u003e ...
J'ai récupéré les pouètes qui parlent de l'AFNIC, sous forme d'une seule ligne de JSON, peu lisible. Alors qu'avec jq :
% madonctl --output json timeline :afnic | jq . [ { "id": 3221183, "uri": "tag:mastodon.social,2017-07-20:objectId=13220936:objectType=Status", "url": "https://mastodon.social/users/afnic/updates/3830115", "account": { "id": 37388, "username": "afnic", "acct": "afnic@mastodon.social", "display_name": "Afnic", "note": "<p>Le registre des noms de domaine en .fr<br>Rdv sur <a href=\"http://www.afnic.fr/\" rel=\"nofollow n oopener\" target=\"_blank\"><span class=\"invisible\">http://www.</span><span class=\"\">afnic.fr/</span><span class= \"invisible\"></span></a></p>", "url": "https://mastodon.social/@afnic",
J'ai au contraire du beau JSON bien lisible. C'est la première utilisation de jq pour beaucoup d'utilisateurs, dans un monde où les services REST fabriquent en général du JSON très compact (peut-être pour gagner quelques octets), jq peut servir de pretty-printer.
Le point après la commande
jq
est un filtre. jq
fonctionne par enchainement de filtres et, par défaut, produit des
données JSON joliment formatées. (Si, par contre, on ne veut pas
de pretty-printing, on lance jq avec l'option
--compact-output
.)
Prenons un exemple où on veut sélectionner une seule information dans un fichier JSON. Mettons qu'on utilise RDAP (RFC 9082) pour trouver de l'information sur une adresse IP. RDAP produit du JSON :
% curl -s http://rdap.db.ripe.net/ip/2001:1470:8000:53::44 { "handle" : "2001:1470:8000::/48", "startAddress" : "2001:1470:8000::/128", "endAddress" : "2001:1470:8000:ffff:ffff:ffff:ffff:ffff/128", "ipVersion" : "v6", "name" : "ARNES-IPv6-NET", "type" : "ASSIGNED", "country" : "SI", ...
Si on ne veut que le pays du titulaire d'un préfixe d'adresses
IP, c'est simple avec jq, avec le filtre
.country
qui veut dire « le champ
country
de l'objet JSON
(.
étant le texte JSON de plus haut niveau,
ici un objet) » :
% curl -s http://rdap.db.ripe.net/ip/131.111.150.25 | jq .country "GB" % curl -s http://rdap.db.ripe.net/ip/192.134.1.1 | jq .country "FR"
On n'aurait pas pu utiliser les outils ligne de commande classiques comme grep ou sed car le format JSON n'est pas orienté ligne (et a trop de subtilités pour eux).
Et si on veut interroger son nœud Bitcoin pour connaitre à quel bloc il en est :
% bitcoin-cli getinfo | jq .blocks 478280
Cet exemple particulier n'est pas super-intéressant, on a déjà
bitcoin-cli getblockcount
mais cela montre
bien qu'on peut utiliser jq très simplement, pour des tâches
d'administration système.
Au passage, pour trouver le filtre à utiliser, il faut connaitre la structure du fichier JSON. On peut lire la documentation (dans l'exemple RDAP ci-dessus, c'est le RFC 9083) mais c'est parfois un peu compliqué alors que JSON, étant un format texte, fournit une solution plus simple : afficher le début du fichier JSON et repérer les choses qui nous intéressent. (Un format binaire comme CBOR, RFC 8949, ne permet pas cela.) Cette méthode peut sembler du bricolage, mais l'expérience prouve que les services REST n'ont pas toujours de documentation ou, lorsqu'ils en ont, elle est souvent fausse. Donc, savoir explorer du JSON est utile.
Notez que jq produit du JSON, donc les chaînes de caractères
sont entre guillemets. Si on veut juste le
texte, on utilise l'option --raw-output
:
% curl -s http://rdap.db.ripe.net/ip/2001:1470:8000:53::44 | jq --raw-output .country SI
Et si on veut un membre d'un objet qui est lui-même un membre de l'objet de plus haut niveau ? On crée un filtre avec les deux clés :
% echo '{"foo": {"zataz": null, "bar": 42}}' | jq .foo.bar 42
Un exemple réel d'un tel « double déréférencement » serait avec l'API de GitHub, ici pour afficher tous les messages de commit :
% curl -s https://api.github.com/repos/bortzmeyer/check-soa/commits | \ jq --raw-output '.[].commit.message' New rules for the Go linker (see <http://stackoverflow.com/questions/32468283/how-to-contain-space-in-value-string-of-link-flag-when-using-go-build>) Stupid regression to bug #8. Fixed. Timeout problem with TCP Query the NN RRset with EDNS. Closes #9 Stupidest bug possible: I forgot to break the loop when we got a reply TCP support ...
Et si le texte JSON était formé d'un tableau et pas d'un objet (rappel : un objet JSON est un dictionnaire) ? jq permet d'obtenir le nième élément d'un tableau (ici, le quatrième, le premier ayant l'indice 0) :
% echo '[1, 2, 3, 5, 8]' | jq '.[3]' 5
(Il a fallu mettre le filtre entre apostrophes car les crochets sont des caractère spéciaux pour le shell Unix.)
Revenons aux exemples réels. Si on veut le cours du Bitcoin en euros, CoinDesk nous fournit une API REST pour cela. On a juste à faire un triple accès :
% curl -s https://api.coindesk.com/v1/bpi/currentprice.json | jq .bpi.EUR.rate "2,315.7568"
Notez que le cours est exprimé sous forme d'une chaîne de caractères, pas d'un nombre, et qu'il n'est pas à la syntaxe correcte d'un nombre JSON. C'est un des inconvénients de JSON : ce format est un tel succès que tout le monde en fait, et souvent de manière incorrecte. (Sinon, regardez une solution sans jq mais avec sed.)
Maintenant, on veut la liste des comptes
Mastodon qui ont pouèté au sujet de
l'ANSSI. Le résultat de la requête
madonctl --output json timeline :anssi
est un
tableau. On va devoir
itérer sur ce tableau, ce qui se fait avec
le filtre []
(déjà utilisé plus haut dans l'exemple GitHub), et faire un double
déréférencement, le membre account
puis le
membre acct
:
% madonctl --output json timeline :anssi | jq '.[].account.acct' "nschont@mastodon.etalab.gouv.fr" "ChristopheCazin@mastodon.etalab.gouv.fr" "barnault@mastodon.etalab.gouv.fr" ...
Parfait, on a bien notre résultat. Mais le collègue qui avait demandé ce travail nous dit qu'il faudrait éliminer les doublons, et trier les comptes par ordre alphabétique. Pas de problème, jq dispose d'un grand nombre de fonctions pré-définies, chaînées avec la barre verticale (ce qui ne déroutera pas les utilisateurs Unix) :
% madonctl --output json timeline :anssi | jq '[.[].account.acct] | unique | .[]' "ChristopheCazin@mastodon.etalab.gouv.fr" "G4RU" "Sangokuss@framapiaf.org" ...
Oulah, ça devient compliqué ! unique
se comprend sans difficulté mais c'est
quoi, tous ces crochets en plus ? Comme
unique
travaille sur un tableau, il a fallu en
fabriquer un : les crochets extérieurs dans l'expression
[.[].account.acct]
disent à jq de faire un
tableau avec tous les .account.acct
extraits. (On peut aussi fabriquer un objet JSON et je rappelle que
objet = dictionnaire.) Une fois ce tableau
fait, unique
peut bien travailler mais le résultat sera alors un tableau :
% madonctl --output json timeline :anssi | jq '[.[].account.acct] | unique' [ "ChristopheCazin@mastodon.etalab.gouv.fr", "G4RU", "Sangokuss@framapiaf.org", ...
Si on veut « aplatir » ce tableau, et avoir juste une suite de
chaînes de caractères, il faut refaire une itération à la fin (le
.[]
).
(Les Unixiens expérimentés savent qu'il existe
uniq et sort comme commandes Unix et
qu'on aurait aussi pu faire jq '.[].account.acct' | sort
| uniq
. Chacun ses goûts. Notez aussi qu'il n'y a pas
besoin d'un tri explicite en jq : unique
trie
le tableau avant d'éliminer les doublons.)
J'ai dit un peu plus haut que jq pouvait construire des textes
JSON structurés comme les tableaux. Ça marche aussi avec les
objets, en indiquant la clé et la valeur de chaque membre. Par
exemple, si je veux un tableau dont les éléments sont des objets
où la clé href
désigne l'URL d'un pouète
Mastodon (ici, les pouètes ayant utilisé le
mot-croisillon « slovénie ») :
% madonctl --output json timeline :slovénie | jq "[.[] | { href: .url }]" [ { "href": "https://mastodon.gougere.fr/users/bortzmeyer/updates/40282" }, { "href": "https://mamot.fr/@Nighten_Dushi/3131270" }, { "href": "https://mastodon.cx/users/DirectActus/updates/29810" } ]
Les accolades entourant la deuxième partie
du programme jq indiquent de construire un objet, dont on indique comme clé
href
et comme valeur le membre
url
de l'objet original.
Rappelez-vous, jq ne sert pas qu'à des filtrages ultra-simples
d'un champ de l'objet JSON. On peut écrire de vrais programmes et
il peut donc être préférable de les mettre dans un fichier. (Il
existe évidemment un mode Emacs pour éditer
plus agréablement ces fichiers source,
jq-mode.)
Si le fichier accounts.jq
contient :
# From a Mastodon JSON file, extract the accounts [.[].account.acct] | unique | .[]
Alors, on pourra afficher tous les comptes (on se ressert de
--raw-output
pour ne pas avoir d'inutiles
guillemets) :
% madonctl --output json timeline :anssi | jq --raw-output --from-file accounts.jq ChristopheCazin@mastodon.etalab.gouv.fr G4RU Sangokuss@framapiaf.org ...
Mais on peut vouloir formater des résultats sous une forme de
texte, par exemple pour inclusion dans un message ou un
rapport. Reprenons notre nœud Bitcoin et
affichons la liste des pairs (Bitcoin est un réseau
pair-à-pair), en les classant selon le
RTT décroissant. On met ça dans le fichier bitcoin.jq
:
"You have " + (length | tostring) + " peers. From the closest (network-wise) to the furthest away", ([.[] | {"address": .addr, "rtt": .pingtime}] | sort_by(.rtt) | .[] | ("Peer " + .address + ": " + (.rtt|tostring) + " ms"))
Et on peut faire :
% bitcoin-cli getpeerinfo | jq --raw-output --from-file bitcoin.jq You have 62 peers. From the closest (network-wise) to the furthest away Peer 10.105.127.82:38806: 0.00274 ms Peer 192.0.2.130:8333: 0.003272 ms Peer [2001:db8:210:5046::2]:33567: 0.014099 ms ...
On a utilisé des constructions de tableaux et d'objets, la
possibilité de trier sur un critère quelconque (ici la valeur de
rtt
, en paramètre à sort_by
) et des fonctions utiles pour l'affichage
de messages : le signe plus indique la
concaténation de chaînes, et la fonction
tostring
transforme un entier en chaîne de
caractères (jq ne fait pas de conversion de type implicite).
jq a également des fonctions pré-définies pour faire de
l'arithmétique. Utilisons-les pour analyser les résultats de
mesures faites par des sondes
RIPE Atlas. Une fois une mesure lancée (par exemple la
mesure de RTT
#9211624,
qui a été créée par la commande atlas-reach -r 100 -t 1
2001:500:2e::1
, cf. mon article « Using RIPE Atlas to Debug Network Connectivity Problems »),
les résultats peuvent être récupérés sous forme d'un fichier JSON
(à l'URL
https://atlas.ripe.net/api/v2/measurements/9211624/results/
). Cherchons
d'abord le RTT maximal :
% jq '[.[].result[0].rtt] | max' < 9211624.json 505.52918
On a pris le premier élément du tableau car, pour cette mesure,
chaque sonde ne faisait qu'un test. Ensuite, on utilise les
techniques jq déjà vues, plus la fonction
max
(qui prend comme argument un tableau, il
a donc fallu construire un tableau). La sonde la plus lointaine de l'amer est donc à plus de 500 millisecondes. Et le
RTT minimum ? Essayons la fonction min
% jq '[.[].result[0].rtt] | min' < 9211624.json null
Ah, zut, certaines sondes n'ont pas réussi et on n'a donc pas de
RTT, ce que jq traduit en null
. Il faut donc
éliminer du tableau ces échecs. jq permet de tester une valeur,
avec select
. Par exemple,
(select(. != null)
va tester que la valeur
existe. Et une autre fonction jq, map
, bien
connue des programmeurs qui aiment le style fonctionnel, permet
d'appliquer une fonction à tous les éléments d'un tableau. Donc,
réessayons :
% jq '[.[].result[0].rtt] | map(select(. != null)) | min' < 9211624.json 1.227755
C'est parfait, une milli-seconde pour la sonde la plus
proche. Notez que, comme map
prend un tableau
en entrée et rend un tableau, on aurait aussi pu l'utiliser au
début, à la place des crochets :
% jq 'map(.result[0].rtt) | map(select(. != null)) | min' < 9211624.json 1.227755
Et la moyenne des RTT ? On va utiliser
les fonctions
add
(additionner tous les éléments du
tableau) et length
(longueur du tableau) :
% jq '[.[].result[0].rtt] | add / length' < 9211624.json 76.49538228
(Il y a une faille dans le programme, les valeurs nulles auraient dû être exclues. Je vous laisse modifier le code.) La moyenne n'est pas toujours très utile quand on mesure des RTT. C'est une métrique peu robuste, que quelques outliers suffisent à perturber. Il est en général bien préférable d'utiliser la médiane. Une façon simple de la calculer est de trier le tableau et de prendre l'élément du milieu (ou le plus proche du milieu) :
% jq '[.[].result[0].rtt] | sort | .[length/2]' < 9211624.json 43.853845
On voit que la médiane est bien plus petite que la moyenne, quelques
énormes RTT ont en effet tiré la moyenne vers le haut. J'ai un peu
triché dans le filtre jq ci-dessus car il ne marche que pour des
tableaux de taille paire. Si elle est impaire,
length/2
ne donnera pas un nombre entier et on
récupérera null
. Corrigeons, ce sera l'occasion
d'utiliser pour la première fois la structure de contrôle
if
. Notez qu'une expression jq doit toujours
renvoyer quelque chose (les utilisateurs de langages
fonctionnels comme Haskell ne
seront pas surpris par cette règle), donc pas de if
sans une
branche else
:
% jq '[.[].result[0].rtt] | sort | if length % 2 == 0 then .[length/2] else .[(length-1)/2] end' < 9211624.json 43.853845
Et maintenant, si on veut la totale, les quatre métriques avec du texte, on mettra ceci dans le fichier source jq. On a utilisé un nouvel opérateur, la virgule, qui permet de lancer plusieurs filtres sur les mêmes données :
map(.result[0].rtt) | "Median: " + (sort | if length % 2 == 0 then .[length/2] else .[(length-1)/2] end | tostring), "Average: " + (map(select(. != null)) | add/length | tostring), "Min: " + (map(select(. != null)) | min | tostring), "Max: " + (max | tostring)
Et cela nous donne :
% jq --raw-output --from-file atlas-rtt.jq < 9211624.json Median: 43.853845 Average: 77.26806290909092 Min: 1.227755 Max: 505.52918
Peut-on passer des arguments à un programme jq ?
Évidemment. Voyons un exemple avec la base des « espaces blancs »
décrite dans le RFC 7545. On va chercher la
puissance d'émission maximale autorisée pour une fréquence
donnée, avec ce script jq, qui contient la variable
frequency
, et qui cherche une plage de
fréquences (entre startHz
et
stopHz
) comprenant cette fréquence :
.result.spectrumSchedules[0].spectra[0].frequencyRanges[] | select (.startHz <= ($frequency|tonumber) and .stopHz >= ($frequency|tonumber)) | .maxPowerDBm
On l'appelle en passant la fréquence où on souhaite émettre :
% jq --from-file paws.jq --arg frequency 5.2E8 paws-chicago.json 15.99999928972511
(Ne l'utilisez pas telle quelle : par principe, les espaces blancs ne sont pas les mêmes partout. Celui-ci était pour Chicago en juillet 2017.)
Il est souvent utile, quand on joue avec des données, de les
grouper par une certaine caractéristique, par exemple pour compter
le nombre d'occurrences d'un certain phénomène, ou bien pour
classer. C'est le classique GROUP BY
de
SQL, qui a son équivalent en jq. Revenons à
la liste des comptes Mastodon qui ont pouèté sur
l'ANSSI et affichons combien chacun a émis
de pouètes. On va grouper les pouètes par compte, fabriquer
un objet JSON dont les clés sont le compte, le trier, puis afficher le résultat. Avec ce code jq :
# Count the number of occurrences [.[].account] | group_by(.acct) | # Create an array of objects {account, number} [.[] | {"account": .[0].acct, "number": length}] | # Now sort sort_by(.number) | reverse | # Now, display .[] | .account + ": " + (.number | tostring)
On obtiendra :
% madonctl --output json timeline :anssi | jq -r -f anssi.jq gapz@mstdn.fr: 4 alatitude77@mastodon.social: 4 nschont@mastodon.etalab.gouv.fr: 2 zorglubu@framapiaf.org: 1 sb_51_@mastodon.xyz: 1
Voilà, on a bien avancé et je n'ai toujours pas donné
d'exemple avec le DNS. Un autre cas
d'utilisation de select
va y pourvoir. Le
DNS Looking Glass peut produire
des résultats en JSON, par exemple ici le
SOA du TLD
.xxx
: curl
-s -H 'Accept: application/json'
https://dns.bortzmeyer.org/xxx/SOA
. Si je veux juste le
numéro de série de cet enregistrement SOA ?
% curl -s -H 'Accept: application/json' https://dns.bortzmeyer.org/xxx/SOA | \ jq '.AnswerSection[0].Serial' 2011210193
Mais il y a un piège. En prenant le premier élément de la
Answer Section, j'ai supposé qu'il s'agissait
bien du SOA demandé. Mais l'ordre des éléments dans les sections
DNS n'est pas défini. Par exemple, si une signature
DNSSEC est renvoyée, elle sera peut-être le
premier élément. Il faut donc être plus subtil, et utiliser
select
pour ne garder que la réponse de type SOA :
% curl -s -H 'Accept: application/json' https://dns.bortzmeyer.org/xxx/SOA | \ jq '.AnswerSection | map(select(.Type=="SOA")) | .[0].Serial' 2011210193
Notre code jq est désormais plus robuste. Ainsi, sur un nom qui a plusieurs adresses IP, on peut ne tester que celles de type IPv6, ignorant les autres, ainsi que les autres enregistrements que le serveur DNS a pu renvoyer :
% curl -s -H 'Accept: application/json' https://dns.bortzmeyer.org/gmail.com/ADDR | \ jq '.AnswerSection | map(select(.Type=="AAAA")) | .[] | .Address' "2607:f8b0:4006:810::2005"
Bon, on approche de la fin, vous devez être fatigué·e·s, mais encore deux exemples, pour illustrer des trucs et concepts utiles. D'abord, le cas où une clé dans un objet JSON n'a pas la syntaxe d'un identificateur jq. C'est le cas du JSON produit par le compilateur Solidity. Si je compile ainsi :
% solc --combined-json=abi registry.sol > abi-registry.json
Le JSON produit a deux défauts. Le premier est que certains utilisateurs ont une syntaxe inhabituelle (des points dans la clé, et le caractère deux-points) :
{"registry.sol:Registry": {"abi": ...
jq n'accepte pas cette clé comme filtre :
% jq '.contracts.registry.sol:Registry.abi' abi-registry.json jq: error: syntax error, unexpected ':', expecting $end (Unix shell quoting issues?) at <top-level>, line 1: .contracts.registry.sol:Registry.abi jq: 1 compile error
Corrigeons cela en mettant la clé bizarre entre guillemets :
% jq '.contracts."registry.sol:Registry".abi' abi-registry.json
On récupère ainsi l'ABI du
contrat.
Mais, et là c'est gravement nul de la part du compilateur, l'ABI est
une chaîne de caractères à évaluer pour en faire du vrai JSON !
Heureusement, jq a tout ce qu'il faut pour cette évaluation, avec la
fonction fromjson
:
% jq '.contracts."registry.sol:Registry".abi | fromjson' abi-registry.json
Ce problème des clés qui ne sont pas des identificateurs à la
syntaxe traditionnelle se pose aussi si l'auteur du JSON a choisi,
comme la norme le lui permet, d'utiliser des caractères
non-ASCII pour les identificateurs. Prenons
par exemple le fichier JSON des verbes
conjugués du
français, en
. Le
JSON est :
https://github.com/Drulac/Verbes-Francais-Conjugues
[{ "Indicatif" : { "Présent" : [ "abade", "abades", "abade", "abadons", "abadez", "abadent" ], "Passé composé" : [ "abadé", "abadé", "abadé", "abadé", "abadé", "abadé" ], "Imparfait" : [ "abadais", "abadais", "abadait", "abadions", "abadiez", "abadaient" ], "Plus-que-parfait" : [ "abadé", "abadé", "abadé", "abadé", "abadé", "abadé" ], "Passé simple" : [ "abadai", "abadas", "abada", "abadâmes", "abadâtes", "abadèrent" ], "Passé antérieur" : [ "abadé", "abadé", "abadé", "abadé", "abadé", "abadé" ], ...
Il faut donc mettre les clés non-ASCII entre guillemets. Ici, la conjugaison du verbe « reposer » :
% jq 'map(select(.Infinitif."Présent"[0] == "reposer"))' verbs.json [ { "Indicatif": { "Présent": [ "repose", "reposes", "repose", ...
Notez toutefois que jq met tout en mémoire. Cela peut l'empêcher de lire de gros fichiers :
% ls -lh la-crime.json -rw-r--r-- 1 stephane stephane 798M Sep 9 19:09 la-crime.json % jq .data la-crime.json error: cannot allocate memory zsh: abort jq .data la-crime.json
Et un dernier exemple, pour montrer les manipulations de date en
jq, ainsi que la définition de fonctions. On va
utiliser l'API d'Icinga pour récupérer
l'activité de la supervision. La commande curl -k
-s -u operator:XXXXXXX -H 'Accept: application/json' -X POST
'https://ADRESSE:PORT/v1/events?types=StateChange&queue=operator'
va nous donner les changements d'état des machines et des services
supervisés. Le résultat de cette commande est du JSON : on souhaite
maintenant le formater de manière plus
compacte et lisible. Un des membres JSON est une date
écrite sous forme d'un nombre de secondes depuis une
epoch. On
va donc la convertir en jolie date avec la fonction
todate
. Ensuite, les états Icinga
(.state
) sont affichés
par des nombres (0 = OK, 1 = Avertissement, etc.), ce qui n'est pas
très agréable. On les
convertit en chaînes de caractères, et mettre cette conversion dans
une fonction, s2n
:
def s2n(s): if s==0 then "OK" else if s==1 then "Warning" else "Critical" end end; (.timestamp | todate) + " " + .host + " " + .service + " " + " " + s2n(.state) + " " + .check_result.output
Avec ce code, on peut désormais exécuter :
% curl -k -s -u operator:XXXXXXX -H 'Accept: application/json' -X POST 'https://ADRESSE:PORT/v1/events?types=StateChange&queue=operator' | \ jq --raw-output --unbuffered --from-file logicinga.jq ... 2017-08-08T19:12:47Z server1 example Critical HTTP CRITICAL: HTTP/1.1 200 OK - string 'Welcome' not found on 'http://www.example.com:80/' - 1924 bytes in 0.170 second response time
Quelques lectures pour aller plus loin :
Merci à Jean-Edouard Babin et Manuel Pégourié-Gonnard pour la correction d'une grosse bogue dans la première version de cet article.
Date de publication du RFC : Juillet 2017
Auteur(s) du RFC : K. Fujiwara (JPRS), A. Kato
(Keio/WIDE), W. Kumari (Google)
Chemin des nor