Je suis Charlie

Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

Ève

Les RFC (Request For Comments) sont les documents de référence de l'Internet. Produits par l'IETF pour la plupart, ils spécifient des normes, documentent des expériences, exposent des projets...

Leur gratuité et leur libre distribution ont joué un grand rôle dans le succès de l'Internet, notamment par rapport aux protocoles OSI de l'ISO organisation très fermée et dont les normes coûtent cher.

Je ne tente pas ici de traduire les RFC en français (un projet pour cela existe mais je n'y participe pas, considérant que c'est une mauvaise idée), mais simplement, grâce à une courte introduction en français, de donner envie de lire ces excellents documents. (Au passage, si vous les voulez présentés en italien...)

Le public visé n'est pas le gourou mais l'honnête ingénieur ou l'étudiant.


RFC 8484: DNS Queries over HTTPS (DoH)

Date de publication du RFC : Octobre 2018
Auteur(s) du RFC : P. Hoffman (ICANN), P. McManus (Mozilla)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF doh
Première rédaction de cet article le 22 octobre 2018


Voici un nouveau moyen d'envoyer des requêtes DNS, DoH (DNS over HTTPS). Requêtes et réponses, au lieu de voyager directement sur UDP ou TCP sont encapsulées dans HTTP, plus exactement HTTPS. Le but ? Il s'agit essentiellement de contourner la censure, en fournissant un canal sécurisé avec un serveur supposé digne de confiance. Et le chiffrement sert également à préserver la vie privée du client. Toutes ces fonctions pourraient être assurées en mettant le DNS sur TLS (RFC 7858) mais DoH augmente les chances de succès puisque le trafic HTTPS est rarement bloqué par les pare-feux, alors que le port 853 utilisé par DNS-sur-TLS peut être inaccessible, vu le nombre de violations de la neutralité du réseau. DoH marque donc une nouvelle étape dans la transition vers un Internet « port 443 seulement ».

La section 1 du RFC détaille les buts de DoH. Deux buts principaux sont décrits :

  • Le premier, et le plus important, est celui indiqué au paragraphe précédent : échapper aux middleboxes qui bloquent le trafic DNS, ou bien le modifient. De telles violations de la neutralité du réseau sont fréquentes, imposées par les États ou les entreprises à des fins de censure, ou bien décidées par les FAI pour des raisons commerciales. Dans la lutte sans fin entre l'épée et la cuirasse, le DNS est souvent le maillon faible : infrastructure indispensable, il offre une cible tentante aux attaquants. Et il est utilisable pour la surveillance (cf. RFC 7626). DNS-sur-TLS, normalisé dans le RFC 7858, était une première tentative de protéger l'intégrité et la confidentialité du trafic DNS par la cryptographie. Mais il utilise un port dédié, le port 853, qui peut être bloqué par un intermédiaire peu soucieux de neutralité du réseau. Dans de nombreux réseaux, hélas, le seul protocole qui soit à peu près sûr de passer partout est HTTPS sur le port 443. D'où la tendance actuelle à tout mettre sur HTTP.
  • Il y a une deuxième motivation à DoH, moins importante, la possibilité de faire des requêtes DNS complètes (pas seulement des demandes d'adresses IP) depuis des applications JavaScript tournant dans le navigateur, et tout en respectant CORS.

L'annexe A de notre RFC raconte le cahier des charges du protocole DoH de manière plus détaillée :

  • Sémantique habituelle de HTTP (on ne change pas HTTP),
  • Possibilité d'exprimer la totalité des requêtes et réponses DNS actuelles, d'où le choix de l'encodage binaire du DNS et pas d'un nouvel encodage, par exemple en JSON, plus simple mais limité à un sous-ensemble du DNS,
  • Et aussi des « non-considérations » : DoH n'avait pas à traiter le cas des réponses synthétisées, comme DNS64, ni celui des réponses à la tête du client, ni le HTTP tout nu sans TLS.

Passons maintenant à la technique. DoH est un protocole très simple. Au hackathon de l'IETF en mars 2018 à Londres, les sept ou huit personnes travaillant sur DoH avaient très vite réussi à créer clients et serveurs, et à les faire interopérer (même moi, j'y étais arrivé). Vous pouvez lire le compte-rendu du hackathon, et la présentation quelques jours après au groupe de travail DoH.)

DoH peut s'utiliser de plusieurs façons : c'est une technique, pas une politique. Néanmoins, son principal usage sera entre un résolveur simple, situé sur la machine de l'utilisateur ou quelque part dans son réseau local, et un résolveur complet situé plus loin dans le réseau (section 1 du RFC). Ce résolveur simple peut être un démon comme stubby ou systemd, ou bien l'application elle-même (ce qui me semble personnellement une mauvaise idée, car cela empêche de partager configuration et cache). À l'heure actuelle, les serveurs faisant autorité ne parlent pas DoH et il n'est pas prévu qu'ils s'y mettent à brève échéance. Le schéma suivant montre l'utilisation typique de DoH : l'application (le client final) parle à un résolveur simple, en utilisant le protocole DNS (ce qui évite de mettre du DoH dans toutes les applications), le résolveur simple parlera en DoH avec un résolveur de confiance situé quelque part dans l'Internet, et ce résolveur de confiance utilisera le DNS pour parler aux serveurs faisant autorité (soit il sera lui-même un résolveur complet, soit il parlera à un résolveur classique proche de lui, et le serveur DoH sera alors un proxy comme décrit dans le RFC 5625) : doh.png

Les requêtes et réponses DNS (RFC 1034 et RFC 1035) ont leur encodage habituel (le DNS est un protocole binaire, donc je ne peux pas faire de copier/coller pour montrer cet encodage), la requête est mise dans le chemin dans l'URL ou dans le corps d'une requête HTTP (RFC 7540), la réponse se trouvera dans le corps de la réponse HTTP. Toute la sécurité (intégrité et confidentialité) est assurée par TLS (RFC 8446), via HTTPS (RFC 2818). Un principe essentiel de DoH est d'utiliser HTTP tel quel, avec ses avantages et ses inconvénients. Cela permet de récupérer des services HTTP comme la négociation de contenu, la mise en cache, l'authentification, les redirections, etc.

La requête HTTP elle-même se fait avec les méthodes GET ou POST (section 4 du RFC), les deux devant être acceptées (ce qui fut le sujet d'une assez longue discussion à l'IETF.) Quand la méthode utilisée est GET, la variable nommée dns est le contenu de la requête DNS, suivant l'encodage habituel du DNS, surencodée en Base64, plus exactement la variante base64url normalisée dans le RFC 4648. Et, avec GET, le corps de la requête est vide (RFC 7231, section 4.3.1). Quand on utilise POST, la requête DNS est dans le corps de la requête HTTP et n'a pas ce surencodage. Ainsi, la requête avec POST sera sans doute plus petite, mais par contre GET est certainement plus apprécié par les caches.

On l'a dit, DoH utilise le HTTP habituel. L'utilisation de HTTP/2, la version 2 de HTTP (celle du RFC 7540) est très recommandée, et clients et serveurs DoH peuvent utiliser la compression et le remplissage que fournit HTTP/2 (le remplissage étant très souhaitable pour la vie privée). HTTP/2 a également l'avantage de multiplexer plusieurs ruisseaux (streams) sur la même connexion HTTP, ce qui évite aux requêtes DoH rapides de devoir attendre le résultat d'une requête lente qui aurait été émise avant. (HTTP 1, lui, impose le respect de l'ordre des requêtes.) HTTP/2 n'est pas formellement imposé, car on ne peut pas forcément être sûr du comportement des bibliothèques utilisées, ni de celui des différents relais sur le trajet.

Requêtes et réponses ont actuellement le type MIME application/dns-message, mais d'autres types pourront apparaitre dans le futur (par exemple fondés sur JSON et non plus sur l'encodage binaire du DNS, qui n'est pas amusant à analyser en JavaScript). Le client DoH doit donc inclure un en-tête HTTP Accept: pour indiquer quels types MIME il accepte. En utilisant HTTP, DoH bénéfice également de la négociation de contenu HTTP (RFC 7231, section 3.4).

Petit détail DNS : le champ ID (identification de la requête) doit être mis à zéro. Avec UDP, il sert à faire correspondre une requête et sa réponse mais c'est inutile avec HTTP, et cela risquerait d'empêcher la mise en cache de deux réponses identiques mais avec des ID différents.

En suivant la présentation des requêtes HTTP du RFC 7540 (rappelez-vous que HTTP/2, contrairement à la première version de HTTP, a un encodage binaire), cela donnerait, par exemple :

:method = GET
:scheme = https
:authority = dns.example.net
:path = /?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB
accept = application/dns-message
    

(Si vous vous le demandez, AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB est une requête DNS pour l'adresse IPv4 de www.example.com.)

Et la réponse HTTP ? Aujourd'hui, elle est forcément de type MIME application/dns-message, mais d'autres types pourront apparaitre. En attendant, le corps de la réponse HTTP est une réponse DNS avec son encodage binaire habituel normalisé dans le RFC 1035, section 4.1 (tel qu'utilisé pour UDP ; notez que HTTP permettant d'indiquer la longueur du message, les deux octets de longueur utilisés par le DNS au-dessus de TCP ne sont pas nécessaires et sont donc absents).

Le serveur DoH doit mettre un code de retour HTTP (RFC 7231). 200 signifie que la requête HTTP a bien été traitée. Mais cela ne veut pas dire que la requête DNS, elle, ait connu un succès. Si elle a obtenu une erreur DNS NXDOMAIN (nom non trouvé) ou SERVFAIL (échec de la requête), le code de retour HTTP sera quand même 200, indiquant qu'il y a une réponse DNS, même négative. Le client DoH, en recevant ce 200, devra donc analyser le message DNS et y trouver le code de retour DNS (NOERROR, NXDOMAIN, REFUSED, etc). Le serveur DoH ne mettra un code d'erreur HTTP que s'il n'a pas du tout de réponse DNS à renvoyer. Il mettra 403 s'il refuse de servir ce client DoH, 429 si le client fait trop de requêtes (RFC 6585), 500 si le serveur DoH a une grosse bogue, 415 si le type MIME utilisé n'est pas connu du serveur, et bien sûr 404 si le serveur HTTP ne trouve rien à l'URL indiqué par exemple parce que le service a été retiré. Dans tous ces cas, il n'y a pas de réponse DNS incluse. La sémantique de ces codes de retour, et le comportement attendu du client, suit les règles habituelles de HTTP, sans spécificité DoH. (C'est un point important et général de DoH : c'est du DNS normal sur du HTTP normal). Par exemple, lorsque le code de retour commence par un 4, le client n'est pas censé réessayer la même requête sur le même serveur : elle donnera forcément le même résultat.

Voici un exemple de réponse DoH :

:status = 200
content-type = application/dns-message
content-length = 61
cache-control = max-age=3709
[Les 61 octets, ici représentés en hexadécimal pour la lisibilité]
   00 00 81 80 00 01 00 01  00 00 00 00 03 77 77 77
   07 65 78 61 6d 70 6c 65  03 63 6f 6d 00 00 1c 00
   01 c0 0c 00 1c 00 01 00  00 0e 7d 00 10 20 01 0d
   b8 ab cd 00 12 00 01 00  02 00 03 00 04
    

La réponse DNS signifie « l'adresse de www.example.com est 2001:db8:abcd:12:1:2:3:4 et le TTL est de 3709 secondes [notez comme il est repris dans le Cache-control: HTTP] ».

Comment un client DoH trouve-t-il le serveur ? La section 3 du RFC répond à cette question. En gros, c'est manuel. DoH ne fournit pas de mécanisme de sélection automatique. Concevoir un tel mécanisme et qu'il soit sécurisé est une question non triviale, et importante : changer le résolveur DNS utilisé par une machine revient quasiment à pirater complètement cette machine. Cela avait fait une très longue discussion au sein du groupe de travail DoH à l'IETF, entre ceux qui pensaient qu'un mécanisme automatique de découverte du gabarit faciliterait nettement la vie de l'utilisateur, et ceux qui estimaient qu'un tel mécanisme serait trop facile à subvertir. Donc, pour l'instant, le client DoH reçoit manuellement un gabarit d'URI (RFC 6570) qui indique le serveur DoH à utiliser. Par exemple, un client recevra le gabarit https://dns.example.net/{?dns}, et il fera alors des requêtes HTTPS à dns.example.net, en passant ?dns=[valeur de la requête].

Notez que l'URL dans le gabarit peut comporter un nom de domaine, qui devra lui-même être résolu via le DNS, créant ainsi un amusant problème d'œuf et de poule (cf. section 10 de notre RFC). Une solution possible est de ne mettre que des adresses IP dans l'URL, mais cela peut poser des problèmes pour l'authentification du serveur DoH, toutes les autorités de certification n'acceptant pas de mettre des adresses IP dans le certificat (cf. RFC 6125, section 1.7.2, et annexe B.2).

La section 5 du RFC détaille quelques points liés à l'intégration avec HTTP. D'abord, les caches. DNS et HTTP ont chacun son système. Et entre le client et le serveur aux extrémités, il peut y avoir plusieurs caches DNS et plusieurs caches HTTP, et ces derniers ne connaissent pas forcément DoH. Que se passe-t-il si on réinjecte dans le DNS des données venues d'un cache HTTP ? En général, les réponses aux requêtes POST ne sont pas mises en cache (elles le peuvent, en théorie) mais les requêtes GET le sont, et les implémenteurs de DoH doivent donc prêter attention à ces caches. La méthode recommandée est de mettre une durée de validité explicite dans la réponse HTTP (comme dans l'exemple plus haut avec Cache-control:), en suivant le RFC 7234, notamment sa section 4.2. La durée de validité doit être inférieure ou égale au plus petit TTL de la section principale de la réponse DNS. Par exemple, si un serveur DoH renvoie cette réponse DNS :

ns1.bortzmeyer.org.	27288 IN AAAA 2605:4500:2:245b::42
ns2.bortzmeyer.org.	26752 IN AAAA 2400:8902::f03c:91ff:fe69:60d3
ns4.bortzmeyer.org.	26569 IN AAAA 2001:4b98:dc0:41:216:3eff:fe27:3d3f
    

alors, la réponse HTTP aura un Cache-Control: max-age=26569, le plus petit des TTL.

Si la réponse DNS varie selon le client, le serveur DoH doit en tenir compte pour construire la réponse HTTP. Le but est d'éviter que cette réponse adaptée à un client spécifique soit réutilisée. Cela peut se faire avec Cache-Control: max-age=0 ou bien avec un en-tête Vary: (RFC 7231, section 7.1.4 et RFC 7234, section 4.1) qui va ajouter une condition supplémentaire à la réutilisation des données mises en cache.

S'il y a un en-tête Age: dans la réponse HTTP (qui indique depuis combien de temps cette information était dans un cache Web, RFC 7234, section 5.1), le client DoH doit en tenir compte pour calculer le vrai TTL. Si le TTL DNS dans la réponse est de 600 secondes, mais que Age: indiquait que cette réponse avait séjourné 250 secondes dans le cache Web, le client DoH doit considérer que cette réponse n'a plus que 350 secondes de validité. Évidemment, un client qui veut des données ultra-récentes peut toujours utiliser le Cache-control: no-cache dans sa requête HTTP, forçant un rafraichissement. (Il est à noter que le DNS n'a aucun mécanisme équivalent, et qu'un serveur DoH ne saura donc pas toujours rafraichir son cache DNS.)

La définition formelle du type MIME application/dns-message figure en section 6 de notre RFC, et ce type est désormais enregistré à l'IANA.

La section 8 du RFC est consacrée aux questions de vie privée. C'est à la fois un des principaux buts de DoH (empêcher l'écoute par un tiers) et un point qui a fait l'objet de certaines polémiques, puisque DoH peut être utilisé pour envoyer toutes les requêtes à un gros résolveur public auquel on ne fait pas forcément confiance. Le RFC 7626 traite séparément deux problèmes : l'écoute sur le réseau, et l'écoute effectuée par le serveur. Sur le réseau, DoH protège : tout est chiffré, via un protocole bien établi, TLS. Du fait que le serveur est authentifié, l'écoute par un homme du milieu est également empêchée. DNS sur TLS (RFC 7858) a exactement les mêmes propriétés, mais pour principal inconvénient d'utiliser un port dédié, le 853, trop facile à bloquer. Au contraire, le trafic DoH, passant au milieu d'autres échanges HTTP sur le port 443, est bien plus difficile à restreindre.

Mais et sur le serveur de destination ? Une requête DNS normale contient peu d'informations sur le client (sauf si on utilise la très dangereuse technique du RFC 7871). Au contraire, une requête HTTP est bien trop bavarde : cookies (RFC 6265), en-têtes User-Agent: et Accept-Language:, ordre des en-têtes sont trop révélateurs de l'identité du client. L'utilisation de HTTP présente donc des risques pour la vie privée du client, risques connus depuis longtemps dans le monde HTTP mais qui sont nouveaux pour le DNS. Il avait été envisagé, pendant la discussion à l'IETF, de définir un sous-ensemble de HTTP ne présentant pas ces problèmes, mais cela serait rentré en contradiction avec les buts de DoH (qui étaient notamment de permettre l'utilisation du code HTTP existant). Pour l'instant, c'est donc au client DoH de faire attention. Si la bibliothèque HTTP qu'il utilise le permet, il doit veiller à ne pas envoyer de cookies, à envoyer moins d'en-têtes, etc.

Notez que la question de savoir si les requêtes DoH doivent voyager sur la même connexion que le trafic HTTPS normal (ce que permet HTTP/2, avec son multiplexage) reste ouverte. D'un côté, cela peut aider à les dissimuler. De l'autre, les requêtes HTTP typiques contiennent des informations qui peuvent servir à reconnaitre le client, alors qu'une connexion servant uniquement à DoH serait moins reconnaissable, le DNS étant nettement moins sensible au fingerprinting.

Comme TLS ne dissimule pas la taille des messages, et qu'un observateur passif du trafic, et qui peut en plus envoyer des requêtes au serveur DNS, peut en déduire les réponses reçues, le RFC recommande aux clients DoH de remplir les requêtes DNS selon le RFC 7830.

Le choix de Mozilla d'utiliser DoH pour son navigateur Firefox (voir un compte-rendu de la première expérience) et le fait que, dans certaines configurations, le serveur DoH de Cloudflare était systématiquement utilisé a été très discuté (cf. cette discussion sur le forum des développeurs et cet article du Register). Mais cela n'a rien à voir avec DoH : c'est le choix d'utiliser un résolveur public géré par un GAFA qui est un problème, pas la technique utilisée pour accéder à ce résolveur public. DNS-sur-TLS aurait posé exactement le même problème. Si Mozilla a aggravé les choses avec leur discours corporate habituel (« nous avons travaillé très dur pour trouver une entreprise de confiance »), il faut rappeler que le problème de la surveillance et de la manipulation des requête et réponses DNS par les FAI est un problème réel (essayez de demander à votre FAI s'il s'engage à ne jamais le faire). On a vu plus haut que DoH ne prévoit pas de système de découverte du serveur. Il faut donc que cela soit configuré en dur (un travail supplémentaire pour les utilisateurs, s'il n'y a pas de résolveur par défaut). En tout cas, le point important est que DoH (ou DNS-sur-TLS) ne protège la vie privée que si le serveur DoH est honnête. C'est une limitation classique de TLS : « TLS permet de s'assurer qu'on communique bien avec Satan, et qu'un tiers ne peut pas écouter ». Mais DoH n'impose pas d'utiliser un serveur public, et impose encore moins qu'il s'agisse d'un serveur d'un GAFA.

La section 9 de notre RFC traite des autres problèmes de sécurité. D'abord, sur la relation entre DoH et DNSSEC. C'est simple, il n'y en a pas. DNSSEC protège les données, DoH protège le canal (une distinction que les promoteurs de DNSCurve n'ont jamais comprise). DNSSEC protège contre les modifications illégitimes des données, DoH (ou bien DNS-sur-TLS) protège contre l'écoute illégitime. Ils résolvent des problèmes différents, et sont donc tous les deux nécessaires.

Quant à la section 10 du RFC, elle expose diverses considérations pratiques liées à l'utilisation de DoH. Par exemple, si un serveur faisant autorité sert des réponses différentes selon l'adresse IP source du client (RFC 6950, section 4), utiliser un résolveur public, qu'on y accède via DoH ou par tout autre moyen, ne donnera pas le résultat attendu, puisque l'adresse IP vue par le serveur faisant autorité sera celle du résolveur public, probablement très distincte de celle du « vrai » client. Un exemple similaire figure dans le RFC : une technique comme DNS64 (RFC 6147) risque fort de ne pas marcher avec un résolveur DNS extérieur au réseau local.

Quelles sont les mises en œuvre de DoH ? Le protocole est assez récent donc votre système favori n'a pas forcément DoH, ou alors c'est seulement dans les toutes dernières versions. Mais DoH est très simple à mettre en œuvre (c'est juste la combinaison de trois protocoles bien maitrisés, et pour lesquels il existe de nombreuses bibliothèques, DNS, HTTP et TLS) et le déploiement ne devrait donc pas poser de problème.

Voyons maintenant ce qui existe, question logiciels et serveurs. On a vu que Cloudflare a un serveur public, le fameux 1.1.1.1 étant accessible en DoH (et également en DNS-sur-TLS). Je ne parlerai pas ici de la question de la confiance qu'on peut accorder à ce serveur (je vous laisse lire sa politique de vie privée et l'évaluer vous-même), qui avait été contestée lors de la polémique Mozilla citée plus haut. Cloudflare fournit également une bonne documentation sur DoH, avec une explication de l'encodage. Enfin, Cloudflare fournit un résolveur simple (comme stubby ou systemd cités plus haut) qui est un client DoH, cloudflared.

Un autre serveur DoH public, cette fois issu du monde du logiciel libre, est celui de l'équipe PowerDNS, https://doh.powerdns.org/ (cf. leur annonce). Il utilise leur logiciel dnsdist.

Vous trouverez une liste de serveurs DoH publics chez DefaultRoutes ou bien chez curl ou encore sur le portail dnsprivacy.org. Testons ces serveurs DoH publics avec le programme doh-nghttp.c, qui avait été écrit au hackathon IETF 101, on lui donne l'URL du serveur DoH, et le nom à résoudre, et il cherche l'adresse IPv4 correspondant à ce nom :

% ./doh-nghttp https://doh.powerdns.org/ www.bortzmeyer.org
The address is 204.62.14.153

% ./doh-nghttp  https://1.1.1.1/dns-query www.bortzmeyer.org 
The address is 204.62.14.153

% ./doh-nghttp  https://doh.defaultroutes.de/dns-query www.bortzmeyer.org 
The address is 204.62.14.153

% ./doh-nghttp   https://mozilla.cloudflare-dns.com/dns-query www.bortzmeyer.org
The address is 204.62.14.153
    

Parfait, tout a bien marché. Un autre serveur DoH a la particularité d'être un résolveur menteur (regardez son nom) :

% ./doh-nghttp    https://doh.cleanbrowsing.org/doh/family-filter/ www.bortzmeyer.org
The address is 204.62.14.153

% ./doh-nghttp    https://doh.cleanbrowsing.org/doh/family-filter/ pornhub.com       
The search had no results, and a return value of 8. Exiting.

% ./doh-nghttp https://doh.powerdns.org/   pornhub.com      
The address is 216.18.168.16
    

Bon, et si je veux faire mon propre serveur DoH, on a quelles solutions ? Voyons d'abord le doh-proxy de Facebook. On lui indique le résolveur qu'il va utiliser (il n'est pas inclus dans le code, il a besoin d'un résolveur complet, a priori sur la même machine ou le même réseau local) :


% doh-proxy --port=9443 --upstream-resolver=192.168.2.254 --certfile=server.crt --keyfile=server.key --uri=/
2018-09-27 10:04:21,997:     INFO: Serving on <Server sockets=[<socket.socket fd=6, family=AddressFamily.AF_INET6, type=2049, proto=6, laddr=('::1', 9443, 0, 0)>]>

Et posons-lui des questions avec le même client doh-nghttp :

% ./doh-nghttp  https://ip6-localhost:9443/ www.bortzmeyer.org 
The address is 204.62.14.153
   

C'est parfait, il a marché et affiche les visites :

   
2018-09-27 10:04:24,264:     INFO: [HTTPS] ::1 www.bortzmeyer.org. A IN 0 RD
2018-09-27 10:04:24,264:     INFO: [DNS] ::1 www.bortzmeyer.org. A IN 56952 RD
2018-09-27 10:04:24,639:     INFO: [DNS] ::1 www.bortzmeyer.org. A IN 56952 QR/RD/RA 1/0/0 -1/0/0 NOERROR 374ms
2018-09-27 10:04:24,640:     INFO: [HTTPS] ::1 www.bortzmeyer.org. A IN 0 QR/RD/RA 1/0/0 -1/0/0 NOERROR 375ms

Au même endroit, il y a aussi un client DoH :

% doh-client --domain 1.1.1.1 --uri /dns-query --qname www.bortzmeyer.org
2018-09-27 10:14:12,191:    DEBUG: Opening connection to 1.1.1.1
2018-09-27 10:14:12,210:    DEBUG: Query parameters: {'dns': 'AAABAAABAAAAAAAAA3d3dwpib3J0em1leWVyA29yZwAAHAAB'}
2018-09-27 10:14:12,211:    DEBUG: Stream ID: 1 / Total streams: 0
2018-09-27 10:14:12,219:    DEBUG: Response headers: [(':status', '200'), ('date', 'Thu, 27 Sep 2018 08:14:12 GMT'), ('content-type', 'application/dns-message'), ('content-length', '103'), ('access-control-allow-origin', '*'), ('cache-control', 'max-age=5125'), ('expect-ct', 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"'), ('server', 'cloudflare-nginx'), ('cf-ray', '460c83ee69f73c53-CDG')]
id 0
opcode QUERY
rcode NOERROR
flags QR RD RA AD
edns 0
payload 1452
;QUESTION
www.bortzmeyer.org. IN AAAA
;ANSWER
www.bortzmeyer.org. 5125 IN AAAA 2001:4b98:dc0:41:216:3eff:fe27:3d3f
www.bortzmeyer.org. 5125 IN AAAA 2605:4500:2:245b::42
;AUTHORITY
;ADDITIONAL
2018-09-27 10:14:12,224:    DEBUG: Response trailers: {}

Ainsi qu'un résolveur simple (serveur DNS et client DoH).

Il ne faut pas confondre ce doh-proxy écrit en Python avec un logiciel du même nom écrit en Rust (je n'ai pas réussi à le compiler, celui-là, des compétences Rust plus avancées que les miennes sont nécessaires).

Et les clients, maintenant ? Commençons par le bien connu curl, qui a DoH est depuis la version 7.62.0 (pas encore publiée à l'heure où j'écris, le développement est documenté ici.) curl (comme Mozilla Firefox) fait la résolution DNS lui-même, ce qui est contestable (il me semble préférable que cette fonction soit dans un logiciel partagé par toutes les applications). Voici un exemple :


% ./src/.libs/curl -v --doh-url  https://doh.powerdns.org/ www.bortzmeyer.org
... [Se connecter au serveur DoH]
* Connected to doh.powerdns.org (2a01:7c8:d002:1ef:5054:ff:fe40:3703) port 443 (#2)
* ALPN, offering h2
...
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* Server certificate:
*  subject: CN=doh.powerdns.org
... [Envoyer la requête DoH]
* Using Stream ID: 1 (easy handle 0x5631606cbd50)
> POST / HTTP/2
> Host: doh.powerdns.org
> Accept: */*
> Content-Type: application/dns-message
> Content-Length: 36
...
< HTTP/2 200 
< server: h2o/2.2.5
< date: Thu, 27 Sep 2018 07:39:14 GMT
< content-type: application/dns-message
< content-length: 92
... [On a trouvé la réponse DoH]
* DOH Host name: www.bortzmeyer.org
* TTL: 86392 seconds
* DOH A: 204.62.14.153
* DOH AAAA: 2001:4b98:0dc0:0041:0216:3eff:fe27:3d3f
* DOH AAAA: 2605:4500:0002:245b:0000:0000:0000:0042
... [On peut maintenant se connecter au serveur HTTP - le but
principal de curl - maintenant qu'on a son adresse IP]
* Connected to www.bortzmeyer.org (204.62.14.153) port 80 (#0)
> GET / HTTP/1.1
> Host: www.bortzmeyer.org
> User-Agent: curl/7.62.0-20180927
> Accept: */*
> 

Pour les programmeurs Go, l'excellente bibliothèque godns n'a hélas pas DoH (pour des raisons internes). Du code expérimental avait été écrit dans une branche mais a été abandonné. Les amateurs de Go peuvent essayer à la place cette mise en œuvre (on notera que ce client sait parler DoH mais aussi le protocole spécifique et non-standard du résolveur public Google Public DNS).

Pour les programmeurs C, la référence est la bibliothèque getdns (notez que c'est elle qui a été utilisée pour le client doh-nghttp cité plus haut). Le code DoH est, à la parution du RFC, toujours en cours de développement et pas encore dans un dépôt public. Une fois que cela sera fait, stubby, qui utilise getdns, pourra parler DoH.

Voilà, si vous n'êtes pas épuisé·e·s, il y a encore des choses à lire :


Téléchargez le RFC 8484


L'article seul

RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3

Date de publication du RFC : Août 2018
Auteur(s) du RFC : E. Rescorla (RTFM)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 11 août 2018


Après un très long processus, et d'innombrables polémiques, la nouvelle version du protocole de cryptographie TLS, la 1.3, est enfin publiée. Les changements sont nombreux et, à bien des égards, il s'agit d'un nouveau protocole (l'ancien était décrit dans le RFC 5246, que notre nouveau RFC remplace).

Vous pouvez voir l'histoire de ce RFC sur la Datatracker de l'IETF. Le premier brouillon a été publié en avril 2014, plus de trois années avant le RFC. C'est en partie pour des raisons techniques (TLS 1.3 est très différent de ses prédécesseurs) et en partie pour des raisons politiques. C'est que c'est important, la sécurité ! Cinq ans après les révélations de Snowden, on sait désormais que des acteurs puissants et sans scrupules, par exemple les États, espionnent massivement le trafic Internet. Il est donc crucial de protéger ce trafic, entre autres par la cryptographie. Mais dire « cryptographie » ne suffit pas ! Il existe des tas d'attaques contre les protocoles de cryptographie, et beaucoup ont réussi contre les prédécesseurs de TLS 1.3. Il était donc nécessaire de durcir le protocole TLS, pour le rendre moins vulnérable. Et c'est là que les ennuis ont commencé. Car tout le monde ne veut pas de la sécurité. Les États veulent continuer à espionner (le GCHQ britannique s'était clairement opposé à TLS 1.3 sur ce point). Les entreprises veulent espionner leurs employés (et ont pratiqué un lobbying intense contre TLS 1.3). Bref, derrière le désir de « sécurité », partagé par tout le monde, il y avait un désaccord de fond sur la surveillance. À chaque réunion de l'IETF, une proposition d'affaiblir TLS pour faciliter la surveillance apparaissait, à chaque fois, elle était rejetée et, tel le zombie des films d'horreur, elle réapparaissait, sous un nom et une forme différente, à la réunion suivante. Par exemple, à la réunion IETF de Prague en juillet 2017, l'affrontement a été particulièrement vif, alors que le groupe de travail TLS espérait avoir presque fini la version 1.3. Des gens se présentant comme enterprise networks ont critiqué les choix de TLS 1.3, notant qu'il rendait la surveillance plus difficile (c'était un peu le but…) gênant notamment leur déboguage. Ils réclamaient un retour aux algorithmes n'ayant pas de sécurité persistante. Le début a suivi le schéma classique à l'IETF : « vous réclamez un affaiblissement de la sécurité » vs. « mais si on ne le fait pas à l'IETF, d'autres le feront en moins bien », mais, au final, l'IETF est restée ferme et n'a pas accepté de compromissions sur la sécurité de TLS. (Un résumé du débat est dans « TLS 1.3 in enterprise networks ».)

Pour comprendre les détails de ces propositions et de ces rejets, il faut regarder un peu en détail le protocole TLS 1.3.

Revenons d'abord sur les fondamentaux : TLS est un mécanisme permettant aux applications client/serveur de communiquer au travers d'un réseau non sûr (par exemple l'Internet) tout en empêchant l'écoute et la modification des messages. TLS suppose un mécanisme sous-jacent pour acheminer les bits dans l'ordre, et sans perte. En général, ce mécanisme est TCP. Avec ce mécanisme de transport, et les techniques cryptographiques mises en œuvre par dessus, TLS garantit :

  • L'authentification du serveur (celle du client est facultative), authentification qui permet d'empêcher l'attaque de l'intermédiaire, et qui se fait en général via la cryptographie asymétrique,
  • La confidentialité des données (mais attention, TLS ne masque pas la taille des données, permettant certaines analyses de trafic),
  • L'intégrité des données (qui est inséparable de l'authentification : il ne servirait pas à grand'chose d'être sûr de l'identité de son correspondant, si les données pouvaient être modifiées en route).

Ces propriétés sont vraies même si l'attaquant contrôle complètement le réseau entre le client et le serveur (le modèle de menace est détaillé dans la section 3 - surtout la 3.3 - du RFC 3552, et dans l'annexe E de notre RFC).

TLS est un protocole gros et compliqué (ce qui n'est pas forcément optimum pour la sécurité). Le RFC fait 147 pages. Pour dompter cette complexité, TLS est séparé en deux composants :

  • Le protocole de salutation (handshake protocol), chargé d'organiser les échanges du début, qui permettent de choisir les paramètres de la session (c'est un des points délicats de TLS, et plusieurs failles de sécurité ont déjà été trouvées dans ce protocole pour les anciennes versions de TLS),
  • Et le protocole des enregistrements (record protocol), au plus bas niveau, chargé d'acheminer les données chiffrées.

Pour comprendre le rôle de ces deux protocoles, imaginons un protocole fictif simple, qui n'aurait qu'un seul algorithme de cryptographie symétrique, et qu'une seule clé, connue des deux parties (par exemple dans leur fichier de configuration). Avec un tel protocole, on pourrait se passer du protocole de salutation, et n'avoir qu'un protocole des enregistrements, indiquant comment encoder les données chiffrées. Le client et le serveur pourraient se mettre à communiquer immédiatement, sans salutation, poignée de mains et négociation, réduisant ainsi la latence. Un tel protocole serait très simple, donc sa sécurité serait bien plus facile à analyser, ce qui est une bonne chose. Mais il n'est pas du tout réaliste : changer la clé utilisée serait complexe (il faudrait synchroniser exactement les deux parties), remplacer l'algorithme si la cryptanalyse en venait à bout (comme c'est arrivé à RC4, cf. RFC 7465) créerait un nouveau protocole incompatible avec l'ancien, communiquer avec un serveur qu'on n'a jamais vu serait impossible (puisque on ne partagerait pas de clé commune), etc. D'où la nécessité du protocole de salutation, où les partenaires :

  • S'authentifient avec leur clé publique (ou, si on veut faire comme dans le protocole fictif simple, avec une clé secrète partagée),
  • Sélectionnent l'algorithme de cryptographie symétrique qui va chiffrer la session, ainsi que ses paramètres divers,
  • Choisir la clé de la session TLS (et c'est là que se sont produites les plus grandes bagarres lors de la conception de TLS 1.3).

Notez que TLS n'est en général pas utilisé tel quel mais via un protocole de haut niveau, comme HTTPS pour sécuriser HTTP. TLS ne suppose pas un usage particulier : on peut s'en servir pour HTTP, pour SMTP (RFC 7672), pour le DNS (RFC 7858), etc. Cette intégration dans un protocole de plus haut niveau pose parfois elle-même des surprises en matière de sécurité, par exemple si l'application utilisatrice ne fait pas attention à la sécurité (Voir mon exposé à Devoxx, et ses transparents.)

TLS 1.3 est plutôt un nouveau protocole qu'une nouvelle version, et il n'est pas directement compatible avec son prédécesseur, TLS 1.2 (une application qui ne connait que 1.3 ne peut pas parler avec une application qui ne connait que 1.2.) En pratique, les bibliothèques qui mettent en œuvre TLS incluent en général les différentes versions, et un mécanisme de négociation de la version utilisée permet normalement de découvrir la version maximum que les deux parties acceptent (historiquement, plusieurs failles sont venues de ce point, avec des pare-feux stupidement configurés qui interféraient avec la négociation).

La section 1.3 de notre RFC liste les différences importantes entre TLS 1.2 (qui était normalisé dans le RFC 5246) et 1.3 :

  • La liste des algorithmes de cryptographie symétrique acceptés a été violemment réduite. Beaucoup trop longue en TLS 1.2, offrant trop de choix, comprenant plusieurs algorithmes faibles, elle ouvrait la voie à des attaques par repli. Les « survivants » de ce nettoyage sont tous des algorithmes à chiffrement intègre.
  • Un nouveau service apparait, 0-RTT (zero round-trip time, la possibilité d'établir une session TLS avec un seul paquet, en envoyant les données tout de suite), qui réduit la latence du début de l'échange. Attention, rien n'est gratuit en ce monde, et 0-RTT présente des nouveaux dangers, et ce nouveau service a été un des plus controversés lors de la mise au point de TLS 1.3, entrainant de nombreux débats à l'IETF.
  • Désormais, la sécurité future est systématique, la compromission d'une clé secrète ne permet plus de déchiffrer les anciennes communications. Plus de clés publiques statiques, tout se fera par clés éphémères. C'était le point qui a suscité le plus de débats à l'IETF, car cela complique sérieusement la surveillance (ce qui est bien le but) et le déboguage. L'ETSI, représentante du patronat, a même normalisé son propre TLS délibérement affaibli, eTLS.
  • Plusieurs messages de négociation qui étaient auparavant en clair sont désormais chiffrés. Par contre, l'indication du nom du serveur (SNI, section 3 du RFC 6066) reste en clair et c'est l'une des principales limites de TLS en ce qui concerne la protection de la vie privée. Le problème est important, mais très difficile à résoudre (voir par exemple la proposition ESNI, Encrypted SNI.)
  • Les fonctions de dérivation de clé ont été refaites.
  • La machine à états utilisée pour l'établissement de la connexion également (elle est détaillée dans l'annexe A du RFC).
  • Les algorithmes asymétriques à courbes elliptiques font maintenant partie de la définition de base de TLS (cf. RFC 7748), et on voit arriver des nouveaux comme ed25519 (cf. RFC 8422).
  • Par contre, DSA a été retiré.
  • Le mécanisme de négociation du numéro de version (permettant à deux machines n'ayant pas le même jeu de versions TLS de se parler) a changé. L'ancien était très bien mais, mal implémenté, il a suscité beaucoup de problèmes d'interopérabilité. Le nouveau est censé mieux gérer les innombrables systèmes bogués qu'on trouve sur l'Internet (la bogue ne provenant pas tant de la bibliothèque TLS utilisée que des pare-feux mal programmés et mal configurés qui sont souvent mis devant).
  • La reprise d'une session TLS précédente fait l'objet désormais d'un seul mécanisme, qui est le même que celui pour l'usage de clés pré-partagées. La négociation TLS peut en effet être longue, en terme de latence, et ce mécanisme permet d'éviter de tout recommencer à chaque connexion. Deux machines qui se parlent régulièrement peuvent ainsi gagner du temps.

Un bon résumé de ce nouveau protocole est dans l'article de Mark Nottingham.

Ce RFC concerne TLS 1.3 mais il contient aussi quelques changements pour la version 1.2 (section 1.4 du RFC), comme un mécanisme pour limiter les attaques par repli portant sur le numéro de version, et des mécanismes de la 1.3 « portés » vers la 1.2 sous forme d'extensions TLS.

La section 2 du RFC est un survol général de TLS 1.3 (le RFC fait 147 pages, et peu de gens le liront intégralement). Au début d'une session TLS, les deux parties, avec le protocole de salutation, négocient les paramètres (version de TLS, algorithmes cryptographiques) et définissent les clés qui seront utilisées pour le chiffrement de la session. En simplifiant, il y a trois phases dans l'établissement d'une session TLS :

  • Définition des clés de session, et des paramètres cryptographiques, le client envoie un ClientHello, le serveur répond avec un ServerHello,
  • Définition des autres paramètres (par exemple l'application utilisée au-dessus de TLS, ou bien la demande CertificateRequest d'un certificat client), cette partie est chiffrée, contrairement à la précédente,
  • Authentification du serveur, avec le message Certificate (qui ne contient pas forcément un certificat, cela peut être une clé brute - RFC 7250 ou une clé d'une session précédente - RFC 7924).

Un message Finished termine cette ouverture de session. (Si vous êtes fana de futurisme, notez que seule la première étape pourrait être remplacée par la distribution quantique de clés, les autres resteraient indispensables. Contrairement à ce que promettent ses promoteurs, la QKD ne dispense pas d'utiliser les protocoles existants.)

Comment les deux parties se mettent-elles d'accord sur les clés ? Trois méthodes :

  • Diffie-Hellman sur courbes elliptiques qui sera sans doute la plus fréquente,
  • Clé pré-partagée,
  • Clé pré-partagée avec Diffie-Hellman,
  • Et la méthode RSA, elle, disparait de la norme (mais RSA peut toujours être utilisé pour l'authentification, autrement, cela ferait beaucoup de certificats à jeter…)

Si vous connaissez la cryptographie, vous savez que les PSK, les clés partagées, sont difficiles à gérer, puisque devant être transmises de manière sûre avant l'établissement de la connexion. Mais, dans TLS, une autre possibilité existe : si une session a été ouverte sans PSK, en n'utilisant que de la cryptographie asymétrique, elle peut être enregistrée, et resservir, afin d'ouvrir les futures discussions plus rapidement. TLS 1.3 utilise le même mécanisme pour des « vraies » PSK, et pour celles issues de cette reprise de sessions précédentes (contrairement aux précédentes versions de TLS, qui utilisaient un mécanisme séparé, celui du RFC 5077, désormais abandonné).

Si on a une PSK (gérée manuellement, ou bien via la reprise de session), on peut même avoir un dialogue TLS dit « 0-RTT ». Le premier paquet du client peut contenir des données, qui seront acceptées et traitées par le serveur. Cela permet une importante diminution de la latence, dont il faut rappeler qu'elle est souvent le facteur limitant des performances. Par contre, comme rien n'est idéal dans cette vallée de larmes, cela se fait au détriment de la sécurité :

  • Plus de confidentialité persistante, si la PSK est compromise plus tard, la session pourra être déchiffrée,
  • Le rejeu devient possible, et l'application doit donc savoir gérer ce problème.

La section 8 du RFC et l'annexe E.5 détaillent ces limites, et les mesures qui peuvent être prises.

Le protocole TLS est décrit avec un langage spécifique, décrit de manière relativement informelle dans la section 3 du RFC. Ce langage manipule des types de données classiques :

  • Scalaires (uint8, uint16),
  • Tableaux, de taille fixe - Datum[3] ou variable, avec indication de la longueur au début - uint16 longer<0..800>,
  • Énumérations (enum { red(3), blue(5), white(7) } Color;),
  • Enregistrements structurés, y compris avec variantes (la présence de certains champs dépendant de la valeur d'un champ).

Par exemple, tirés de la section 4 (l'annexe B fournit la liste complète), voici, dans ce langage, la liste des types de messages pendant les salutations, une énumération :

       enum {
          client_hello(1),
          server_hello(2),
          new_session_ticket(4),
          end_of_early_data(5),
          encrypted_extensions(8),
          certificate(11),
          certificate_request(13),
          certificate_verify(15),
          finished(20),
          key_update(24),
          message_hash(254),
          (255)
      } HandshakeType;
    

Et le format de base d'un message du protocole de salutation :

      struct {
          HandshakeType msg_type;    /* handshake type */
          uint24 length;             /* bytes in message */
          select (Handshake.msg_type) {
              case client_hello:          ClientHello;
              case server_hello:          ServerHello;
              case end_of_early_data:     EndOfEarlyData;
              case encrypted_extensions:  EncryptedExtensions;
              case certificate_request:   CertificateRequest;
              case certificate:           Certificate;
              case certificate_verify:    CertificateVerify;
              case finished:              Finished;
              case new_session_ticket:    NewSessionTicket;
              case key_update:            KeyUpdate;
          };
      } Handshake;     
    

La section 4 fournit tous les détails sur le protocole de salutation, notamment sur la délicate négociation des paramètres cryptographiques. Notez que la renégociation en cours de session a disparu, donc un ClientHello ne peut désormais plus être envoyé qu'au début.

Un problème auquel a toujours dû faire face TLS est celui de la négociation de version, en présence de mises en œuvre boguées, et, surtout, en présence de boitiers intermédiaires encore plus bogués (pare-feux ignorants, par exemple, que des DSI ignorantes placent un peu partout). Le modèle original de TLS pour un client était d'annoncer dans le ClientHello le plus grand numéro de version qu'on gère, et de voir dans ServerHello le maximum imposé par le serveur. Ainsi, un client TLS 1.2 parlant à un serveur qui ne gère que 1.1 envoyait ClientHello(client_version=1.2) et, en recevant ServerHello(server_version=1.1), se repliait sur TLS 1.1, la version la plus élevée que les deux parties gèraient. En pratique, cela ne marche pas aussi bien. On voyait par exemple des serveurs (ou, plus vraisemblablement, des pare-feux bogués) qui raccrochaient brutalement en présence d'un numéro de version plus élevé, au lieu de suggérer un repli. Le client n'avait alors que le choix de renoncer, ou bien de se lancer dans une série d'essais/erreurs (qui peut être longue, si le serveur ou le pare-feu bogué ne répond pas).

TLS 1.3 change donc complètement le mécanisme de négociation. Le client annonce toujours la version 1.2 (en fait 0x303, pour des raisons historiques), et la vraie version est mise dans une extension, supported_versions (section 4.2.1), dont on espère qu'elle sera ignorée par les serveurs mal gérés. (L'annexe D du RFC détaille ce problème de la négociation de version.) Dans la réponse ServerHello, un serveur 1.3 doit inclure cette extension, autrement, il faut se rabattre sur TLS 1.2.

En parlant d'extensions, concept qui avait été introduit originellement dans le RFC 4366, notre RFC reprend des extensions déjà normalisées, comme le SNI (Server Name Indication) du RFC 6066, le battement de cœur du RFC 6520, le remplissage du ClientHello du RFC 7685, et en ajoute dix, dont supported_versions. Certaines de ces extensions doivent être présentes dans les messages Hello, car la sélection des paramètres cryptographiques en dépend, d'autres peuvent être uniquement dans les messages EncryptedExtensions, une nouveauté de TLS 1.3, pour les extensions qu'on n'enverra qu'une fois le chiffrement commencé. Le RFC en profite pour rappeler que les messages Hello ne sont pas protégés cryptographiquement, et peuvent donc être modifiés (le message Finished résume les décisions prises et peut donc protéger contre ce genre d'attaques).

Autrement, parmi les autres nouvelles extensions :

  • Le petit gâteau (cookie), pour tester la joignabilité,
  • Les données précoces (early data), extension qui permet d'envoyer des données dès le premier message (« O-RTT »), réduisant ainsi la latence, un peu comme le fait le TCP Fast Open du RFC 7413,
  • Liste des AC (certificate authorities), qui, en indiquant la liste des AC connues du client, peut aider le serveur à choisir un certificat qui sera validé (par exemple en n'envoyant le certificat CAcert que si le client connait cette AC).

La section 5 décrit le protocole des enregistrements (record protocol). C'est ce sous-protocole qui va prendre un flux d'octets, le découper en enregistrements, les protéger par le chiffrement puis, à l'autre bout, déchiffrer et reconstituer le flux… Notez que « protégé » signifie à la fois confidentialité et intégrité puisque TLS 1.3, contrairement à ses prédécesseurs, impose AEAD (RFC 5116).

Les enregistrements sont typés et marqués handshake (la salutation, vue dans la section précédente), change cipher spec, alert (pour signaler un problème) et application data (les données elle-mêmes) :

enum {
          invalid(0),
          change_cipher_spec(20),
          alert(21),
          handshake(22),
          application_data(23),
          (255)
      } ContentType;
    

Le contenu des données est évidemment incompréhensible, en raison du chiffrement (voici un enregistrement de type 23, données, vu par tshark) :

    TLSv1.3 Record Layer: Application Data Protocol: http-over-tls
        Opaque Type: Application Data (23)
        Version: TLS 1.2 (0x0303)
        Length: 6316
        Encrypted Application Data: eb0e21f124f82eee0b7a37a1d6d866b075d0476e6f00cae7...
    

Et décrite par la norme dans son langage formel :

struct {
          ContentType opaque_type = application_data; /* 23 */
          ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
          uint16 length;
          opaque encrypted_record[TLSCiphertext.length];
      } TLSCiphertext;
    

(Oui, le numéro de version reste à TLS 1.2 pour éviter d'énerver les stupides middleboxes.) Notez que des extensions à TLS peuvent introduire d'autres types d'enregistrements.

Une faiblesse classique de TLS est que la taille des données chiffrées n'est pas dissimulée. Si on veut savoir à quelle page d'un site Web un client HTTP a accédé, on peut parfois le déduire de l'observation de cette taille. D'où la possibilité de faire du remplissage pour dissimuler cette taille (section 5.4 du RFC). Notez que le RFC ne suggère pas de politique de remplissage spécifique (ajouter un nombre aléatoire ? Tout remplir jusqu'à la taille maximale ?), c'est un choix compliqué. Il note aussi que certaines applications font leur propre remplissage, et qu'il n'est alors pas nécessaire que TLS le fasse.

La section 6 du RFC est dédiée au cas des alertes. C'est un des types d'enregistrements possibles, et, comme les autres, il est chiffré, et les alertes sont donc confidentielles. Une alerte a un niveau et une description :

 struct {
          AlertLevel level;
          AlertDescription description;
      } Alert;
    

Le niveau indiquait si l'alerte est fatale mais n'est plus utilisé en TLS 1.2, où il faut se fier uniquement à la description, une énumération des problèmes possibles (message de type inconnu, mauvais certificat, enregistrement non décodable - rappelez-vous que TLS 1.3 n'utilise que du chiffrement intègre, problème interne au client ou au serveur, extension non acceptée, etc). La section 6.2 donne une liste des erreurs fatales, qui doivent mener à terminer immédiatement la session TLS.

La section 8 du RFC est entièrement consacrée à une nouveauté délicate, le « 0-RTT ». Ce terme désigne la possibilité d'envoyer des données dès le premier paquet, sans les nombreux échanges de paquets qui sont normalement nécessaires pour établir une session TLS. C'est très bien du point de vue des performances, mais pas forcément du point de vue de la sécurité puisque, sans échanges, on ne peut plus vérifier à qui on parle. Un attaquant peut réaliser une attaque par rejeu en envoyant à nouveau un paquet qu'il a intercepté. Un serveur doit donc se défendre en se souvenant des données déjà envoyées et en ne les acceptant pas deux fois. (Ce qui peut être plus facile à dire qu'à faire ; le RFC contient une bonne discussion très détaillée des techniques possibles, et de leurs limites. Il y en a des subtiles, comme d'utiliser des systèmes de mémorisation ayant des faux positifs, comme les filtres de Bloom, parce qu'ils ne produiraient pas d'erreurs, ils rejetteraient juste certains essais 0-RTT légitimes, cela ne serait donc qu'une légère perte de performance.)

La section 9 de notre RFC se penche sur un problème difficile, la conformité des mises en œuvres de TLS. D'abord, les algorithmes obligatoires. Afin de permettre l'interopérabilité, toute mise en œuvre de TLS doit avoir la suite de chiffrement TLS_AES_128_GCM_SHA256 (AES en mode GCM avec SHA-256). D'autres suites sont recommandées (cf. annexe B.4). Pour l'authentification, RSA avec SHA-256 et ECDSA sont obligatoires. Ainsi, deux programmes différents sont sûrs de pouvoir trouver des algorithmes communs. La possibilité d'authentification par certificats PGP du RFC 6091 a été retirée.

De plus, certaines extensions à TLS sont obligatoires, un pair TLS 1.3 ne peut pas les refuser :

  • supported_versions, nécessaire pour annoncer TLS 1.3,
  • cookie,
  • signature_algorithms, signature_algorithms_cert, supported_groups et key_share,
  • server_name, c'est à dire SNI (Server Name Indication), souvent nécessaire pour pouvoir choisir le bon certificat (cf. section 3 du RFC 6066).

La section 9 précise aussi le comportement attendu des équipements intermédiaires. Ces dispositifs (pare-feux, par exemple, mais pas uniquement) ont toujours été une plaie pour TLS. Alors que TLS vise à fournir une communication sûre, à l'abri des équipements intermédiaires, ceux-ci passent leur temps à essayer de s'insérer dans la communication, et souvent la cassent. Normalement, TLS 1.3 est conçu pour que ces interférences ne puissent pas mener à un repli (le repli est l'utilisation de paramètres moins sûrs que ce que les deux machines auraient choisi en l'absence d'interférence).

Il y a deux grandes catégories d'intermédiaires, ceux qui tripotent la session TLS sans être le client ou le serveur, et ceux qui terminent la session TLS de leur côté. Attention, dans ce contexte, « terminer » ne veut pas dire « y mettre fin », mais « la sécurité TLS se termine ici, de manière à ce que l'intermédiaire puisse accéder au contenu de la communication ». Typiquement, une middlebox qui « termine » une session TLS va être serveur TLS pour le client et client TLS pour le serveur, s'insérant complètement dans la conversation. Normalement, l'authentification vise à empêcher ce genre de pratiques, et l'intermédiaire ne sera donc accepté que s'il a un certificat valable. C'est pour cela qu'en entreprise, les machines officielles sont souvent installées avec une AC contrôlée par le vendeur du boitier intermédiaire, de manière à permettre l'interception.

Le RFC ne se penche pas sur la légitimité de ces pratiques, uniquement sur leurs caractéristiques techniques. (Les boitiers intermédiaires sont souvent programmés avec les pieds, et ouvrent de nombreuses failles.) Le RFC rappelle notamment que l'intermédiaire qui termine une session doit suivre le RFC à la lettre (ce qui devrait aller sans dire…)

Depuis le RFC 4346, il existe plusieurs registres IANA pour TLS, décrits en section 11, avec leurs nouveautés. En effet, plusieurs choix pour TLS ne sont pas « câblés en dur » dans le RFC mais peuvent évoluer indépendamment. Par exemple, le registre de suites cryptographiques a une politique d'enregistrement « spécification nécessaire » (cf. RFC 8126, sur les politiques d'enregistrement). La cryptographie fait régulièrement des progrès, et il faut donc pouvoir modifier la liste des suites acceptées (par exemple lorsqu'il faudra y ajouter les algorithmes post-quantiques) sans avoir à toucher au RFC (l'annexe B.4 donne la liste actuelle). Le registre des types de contenu, lui, a une politique d'enregistrement bien plus stricte, « action de normalisation ». On crée moins souvent des types que des suites cryptographiques. Même chose pour le registre des alertes ou pour celui des salutations.

L'annexe C du RFC plaira aux programmeurs, elle donne plusieurs conseils pour une mise en œuvre correcte de TLS 1.3 (ce n'est pas tout d'avoir un protocole correct, il faut encore qu'il soit programmé correctement). Pour aider les développeurs à déterminer s'ils ont correctement fait le travail, un futur RFC fournira des vecteurs de test.

Un des conseils les plus importants est évidemment de faire attention au générateur de nombres aléatoires, source de tant de failles de sécurité en cryptographie. TLS utilise des nombres qui doivent être imprévisibles à un attaquant pour générer des clés de session. Si ces nombres sont prévisibles, toute la cryptographie s'effondre. Le RFC conseille fortement d'utiliser un générateur existant (comme /dev/urandom sur les systèmes Unix) plutôt que d'écrire le sien, ce qui est bien plus difficile qu'il ne semble. (Si on tient quand même à le faire, le RFC 4086 est une lecture indispensable.)

Le RFC conseille également de vérifier le certificat du partenaire par défaut (quitte à fournir un moyen de débrayer cette vérification). Si ce n'est pas le cas, beaucoup d'utilisateurs du programme ou de la bibliothèque oublieront de le faire. Il suggère aussi de ne pas accepter certains certificats trop faibles (clé RSA de seulement 1 024 bits, par exemple).

Il existe plusieurs moyens avec TLS de ne pas avoir d'authentification du serveur : les clés brutes du RFC 7250 (à la place des certificats), ou bien les certificats auto-signés. Dans ces conditions, une attaque de l'homme du milieu est parfaitement possibe, et il faut donc prendre des précautions supplémentaires (par exemple DANE, normalisé dans le RFC 6698, que le RFC oublie malheureusement de citer).

Autre bon conseil de cryptographie, se méfier des attaques fondées sur la mesure du temps de calcul, et prendre des mesures appropriées (par exemple en vérifiant que le temps de calcul est le même pour des données correctes et incorrectes).

Il n'y a aucune bonne raison d'utiliser certains algorithmes faibles (comme RC4, abandonné depuis le RFC 7465), et le RFC demande que le code pour ces algorithmes ne soit pas présent, afin d'éviter une attaque par repli (annexes C.3 et D.5 du RFC). De la même façon, il demande de ne jamais accepter SSL v3 (RFC 7568).

L'expérience a prouvé que beaucoup de mises en œuvre de TLS ne réagissaient pas correctement à des options inattendues, et le RFC rappelle donc qu'il faut ignorer les suites cryptographiques inconnues (autrement, on ne pourrait jamais introduire une nouvelle suite, puisqu'elle casserait les programmes), et ignorer les extensions inconnues (pour la même raison).

L'annexe D, elle, est consacrée au problème de la communication avec un vieux partenaire, qui ne connait pas TLS 1.3. Le mécanisme de négociation de la version du protocole à utiliser a complètement changé en 1.3. Dans la 1.3, le champ version du ClientHello contient 1.2, la vraie version étant dans l'extension supported_versions. Si un client 1.3 parle avec un serveur <= 1.2, le serveur ne connaitra pas cette extension et répondra sans l'extension, avertissant ainsi le client qu'il faudra parler en 1.2 (ou plus vieux). Ça, c'est si le serveur est correct. S'il ne l'est pas ou, plus vraisemblablement, s'il est derrière une middlebox boguée, on verra des problèmes comme par exemple le refus de répondre aux clients utilisant des extensions inconnues (ce qui sera le cas pour supported_versions), soit en rejettant ouvertement la demande soit, encore pire, en l'ignorant. Arriver à gérer des serveurs/middleboxes incorrects est un problème complexe. Le client peut être tenté de re-essayer avec d'autres options (par exemple tenter du 1.2, sans l'extension supported_versions). Cette méthode n'est pas conseillée. Non seulement elle peut prendre du temps (attendre l'expiration du délai de garde, re-essayer…) mais surtout, elle ouvre la voie à des attaques par repli : l'attaquant bloque les ClientHello 1.3 et le client, croyant bien faire, se replie sur une version plus ancienne et sans doute moins sûre de TLS.

En parlant de compatibilité, le « 0-RTT » n'est évidemment pas compatible avec les vieilles versions. Le client qui envoie du « 0-RTT » (des données dans le ClientHello) doit donc savoir que, si la réponse est d'un serveur <= 1.2, la session ne pourra pas être établie, et il faudra donc réessayer sans 0-RTT.

Naturellement, les plus gros problèmes ne surviennent pas avec les clients et les serveurs mais avec les middleboxes. Plusieurs études ont montré leur caractère néfaste (cf. présentation à l'IETF 100, mesures avec Chrome (qui indique également que certains serveurs TLS sont gravement en tort, comme celui installé dans les imprimantes Canon), mesures avec Firefox, et encore d'autres mesures). Le RFC suggère qu'on limite les risques en essayant d'imiter le plus possible une salutation de TLS 1.2, par exemple en envoyant des messages change_cipher_spec, qui ne sont plus utilisés en TLS 1.3, mais qui peuvent rassurer la middlebox (annexe D.4).

Enfin, le RFC se termine par l'annexe E, qui énumère les propriétés de sécurité de TLS 1.3 : même face à un attaquant actif (RFC 3552), le protocole de salutation de TLS garantit des clés de session communes et secrètes, une authentification du serveur (et du client si on veut), et une sécurité persistante, même en cas de compromission ultérieure des clés (sauf en cas de 0-RTT, un autre des inconvénients sérieux de ce service, avec le risque de rejeu). De nombreuses analyses détaillées de la sécurité de TLS sont listées dans l'annexe E.1.6. À lire si vous voulez travailler ce sujet.

Quant au protocole des enregistrements, celui de TLS 1.3 garantit confidentialité et intégrité (RFC 5116).

TLS 1.3 a fait l'objet de nombreuses analyses de sécurité par des chercheurs, avant même sa normalisation, ce qui est une bonne chose (et qui explique en partie les retards). Notre annexe E pointe également les limites restantes de TLS :

  • Il est vulnérable à l'analyse de trafic. TLS n'essaie pas de cacher la taille des paquets, ni l'intervalle de temps entre eux. Ainsi, si un client accède en HTTPS à un site Web servant quelques dizaines de pages aux tailles bien différentes, il est facile de savoir quelle page a été demandée, juste en observant les tailles. (Voir « I Know Why You Went to the Clinic: Risks and Realization of HTTPS Traffic Analysis », de Miller, B., Huang, L., Joseph, A., et J. Tygar et « HTTPS traffic analysis and client identification using passive SSL/TLS fingerprinting », de Husak, M., Čermak, M., Jirsik, T., et P. Čeleda). TLS fournit un mécanisme de remplissage avec des données bidon, permettant aux applications de brouiller les pistes. Certaines applications utilisant TLS ont également leur propre remplissage (par exemple, pour le DNS, c'est le RFC 7858). De même, une mise en œuvre de TLS peut retarder les paquets pour rendre l'analyse des intervalles plus difficile. On voit que dans les deux cas, taille des paquets et intervalle entre eux, résoudre le problème fait perdre en performance (c'est pour cela que ce n'est pas intégré par défaut).
  • TLS peut être également vulnérable à des attaques par canal auxiliaire. Par exemple, la durée des opérations cryptographiques peut être observée, ce qui peut donner des informations sur les clés. TLS fournit quand même quelques défenses : l'AEAD facilite la mise en œuvre de calculs en temps constant, et format uniforme pour toutes les erreurs, empêchant un attaquant de trouver quelle erreur a été déclenchée.

Le 0-RTT introduit un nouveau risque, celui de rejeu. (Et 0-RTT a sérieusement contribué aux délais qu'à connu le projet TLS 1.3, plusieurs participants à l'IETF protestant contre cette introduction risquée.) Si l'application est idempotente, ce n'est pas très grave. Si, par contre, les effets d'une requête précédentes peuvent être rejoués, c'est plus embêtant (imaginez un transfert d'argent répété…) TLS ne promet rien en ce domaine, c'est à chaque serveur de se défendre contre le rejeu (la section 8 donne des idées à ce sujet). Voilà pourquoi le RFC demande que les requêtes 0-RTT ne soient pas activées par défaut, mais uniquement quand l'application au-dessus de TLS le demande. (Cloudflare, par exemple, n'active pas le 0-RTT par défaut.)

Voilà, vous avez maintenant fait un tour complet du RFC, mais vous savez que la cryptographie est une chose difficile, et pas seulement dans les algorithmes cryptographiques (TLS n'en invente aucun, il réutilise des algorithmes existants comme AES ou ECDSA), mais aussi dans les protocoles cryptographiques, un art complexe. N'hésitez donc pas à lire le RFC en détail, et à vous méfier des résumés forcément toujours sommaires, comme cet article.

À part le 0-RTT, le plus gros débat lors de la création de TLS 1.3 avait été autour du concept que ses partisans nomment « visibilité » et ses adversaires « surveillance ». C'est l'idée qu'il serait bien pratique si on (on : le patron, la police, le FAI…) pouvait accéder au contenu des communications TLS. « Le chiffrement, c'est bien, à condition que je puisse lire les données quand même » est l'avis des partisans de la visibilité. Cela avait été proposé dans les Internet-Drafts draft-green-tls-static-dh-in-tls13 et draft-rhrd-tls-tls13-visibility. Je ne vais pas ici pouvoir capturer la totalité du débat, juste noter quelques points qui sont parfois oubliés dans la discussion. Côté partisans de la visibilité :

  • Dans une entreprise capitaliste, il n'y pas de citoyens, juste un patron et des employés. Les ordinateurs appartiennent au patron, et les employés n'ont pas leur mot à dire. Le patron peut donc décider d'accéder au contenu des communications chiffrées.
  • Il existe des règles (par exemple PCI-DSS dans le secteur financier ou HIPAA dans celui de la santé) qui requièrent de certaines entreprises qu'elles sachent en détail tout ce qui circule sur le réseau. Le moyen le plus simple de le faire est de surveiller le contenu des communications, même chiffrées. (Je ne dis pas que ces règles sont intelligentes, juste qu'elles existent. Notons par exemple que les mêmes règles imposent d'utiliser du chiffrement fort, sans faille connue, ce qui est contradictoire.)
  • Enregistrer le trafic depuis les terminaux est compliqué en pratique : applications qui n'ont pas de mécanisme de journalisation du trafic, systèmes d'exploitation fermés, boîtes noires…
  • TLS 1.3 risque de ne pas être déployé dans les entreprises qui tiennent à surveiller le trafic, et pourrait même être interdit dans certains pays, où la surveillance passe avant les droits humains.

Et du côté des adversaires de la surveillance :

  • La cryptographie, c'est compliqué et risqué. TLS 1.3 est déjà assez compliqué comme cela. Lui ajouter des fonctions (surtout des fonctions délibérement conçues pour affaiblir ses propriétés de sécurité) risque fort d'ajouter des failles de sécurité. D'autant plus que TLS 1.3 a fait l'objet de nombreuses analyses de sécurité avant son déploiement, et qu'il faudrait tout recommencer.
  • Contrairement à ce que semblent croire les partisans de la « visibilité », il n'y a pas que HTTPS qui utilise TLS. Ils ne décrivent jamais comment leur proposition marcherait avec des protocoles autres que HTTPS.
  • Pour HTTPS, et pour certains autres protocoles, une solution simple, si on tient absolument à intercepter tout le trafic, est d'avoir un relais explicite, configuré dans les applications, et combiné avec un blocage dans le pare-feu des connexions TLS directes. Les partisans de la visibilté ne sont en général pas enthousiastes pour cette solution car ils voudraient faire de la surveillance furtive, sans qu'elle se voit dans les applications utilisées par les employés ou les citoyens.
  • Les partisans de la « visibilité » disent en général que l'interception TLS serait uniquement à l'intérieur de l'entreprise, pas pour l'Internet public. Mais, dans ce cas, tous les terminaux sont propriété de l'entreprise et contrôlés par elle, donc elle peut les configurer pour copier tous les messages échangés. Et, si certains de ces terminaux sont des boîtes noires, non configurables et dont on ne sait pas bien ce qu'ils font, eh bien, dans ce cas, on se demande pourquoi des gens qui insistent sur leurs obligations de surveillance mettent sur leur réseau des machines aussi incontrôlables.
  • Dans ce dernier cas (surveillance uniquement au sein d'une entreprise), le problème est interne à l'entreprise, et ce n'est donc pas à l'IETF, organisme qui fait des normes pour l'Internet, de le résoudre. Après tout, rien n'empêche ces entreprises de garder TLS 1.2.

Revenons maintenant aux choses sérieuses, avec les mises en œuvre de TLS 1.3. Il y en existe au moins une dizaine à l'heure actuelle mais, en général, pas dans les versions officiellement publiées des logiciels. Notons quand même que Firefox 61 sait faire du TLS 1.3. Les autres mises en œuvre sont prêtes, même si pas forcément publiées. Prenons l'exemple de la bibliothèque GnuTLS. Elle dispose de TLS 1.3 depuis la version 3.6.3. Pour l'instant, il faut compiler cette version avec l'option ./configure --enable-tls13-support, qui n'est pas encore activée par défaut. Un bon article du mainteneur de GnuTLS explique bien les nouveautés de TLS 1.3.

Une fois GnuTLS correctement compilé, on peut utiliser le programme en ligne de commande gnutls-cli avec un serveur qui accepte TLS 1.3 :

% gnutls-cli  gmail.com 
...
- Description: (TLS1.3)-(ECDHE-X25519)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM)
- Ephemeral EC Diffie-Hellman parameters
 - Using curve: X25519
 - Curve size: 256 bits
- Version: TLS1.3
- Key Exchange: ECDHE-RSA
- Server Signature: RSA-PSS-RSAE-SHA256
- Cipher: AES-256-GCM
- MAC: AEAD
...

Et ça marche, on fait du TLS 1.3. Si vous préférez écrire le programme vous-même, regardez ce petit programme. Si GnuTLS est en /local, il se compilera avec cc -I/local/include -Wall -Wextra -o test-tls13 test-tls13.c -L/local/lib -lgnutls et s'utilisera avec :

% ./test-tls13 www.ietf.org      
TLS connection using "TLS1.3 AES-256-GCM"

%  ./test-tls13 gmail.com  
TLS connection using "TLS1.3 AES-256-GCM"

%  ./test-tls13 mastodon.gougere.fr
TLS connection using "TLS1.2 AES-256-GCM"

% ./test-tls13 www.bortzmeyer.org
TLS connection using "TLS1.2 AES-256-GCM"

% ./test-tls13 blog.cloudflare.com
TLS connection using "TLS1.3 AES-256-GCM"
  

Cela vous donne une petite idée des serveurs qui acceptent TLS 1.3.

Un pcap d'une session TLS 1.3 est disponible en tls13.pcap. Notez que le numéro de version n'est pas encore celui du RFC (0x304). Ici, 0x7f1c désigne l'Internet-Draft numéro 28. Voici la session vue par tshark :

    1   0.000000 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 94 36866 → https(443) [SYN] Seq=0 Win=28800 Len=0 MSS=1440 SACK_PERM=1 TSval=3528788861 TSecr=0 WS=128
    2   0.003052 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 86 https(443) → 36866 [SYN, ACK] Seq=0 Ack=1 Win=24400 Len=0 MSS=1220 SACK_PERM=1 WS=1024
    3   0.003070 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [ACK] Seq=1 Ack=1 Win=28800 Len=0
    4   0.003354 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TLSv1 403 Client Hello
    5   0.006777 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 74 https(443) → 36866 [ACK] Seq=1 Ack=330 Win=25600 Len=0
    6   0.011393 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TLSv1.3 6496 Server Hello, Change Cipher Spec, Application Data
    7   0.011413 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [ACK] Seq=330 Ack=6423 Win=41728 Len=0
    8   0.011650 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TLSv1.3 80 Change Cipher Spec
    9   0.012685 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TLSv1.3 148 Application Data
   10   0.015693 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 74 https(443) → 36866 [ACK] Seq=6423 Ack=411 Win=25600 Len=0
   11   0.015742 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TLSv1.3 524 Application Data
   12   0.015770 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [RST] Seq=411 Win=0 Len=0
   13   0.015788 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 74 https(443) → 36866 [FIN, ACK] Seq=6873 Ack=411 Win=25600 Len=0
   14   0.015793 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [RST] Seq=411 Win=0 Len=0

Et, complètement décodée par tshark :

Secure Sockets Layer [sic]
    TLSv1 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Version: TLS 1.2 (0x0303)
...
            Extension: supported_versions (len=9)
                Type: supported_versions (43)
                Length: 9
                Supported Versions length: 8
                Supported Version: Unknown (0x7f1c)
                Supported Version: TLS 1.2 (0x0303)
                Supported Version: TLS 1.1 (0x0302)
                Supported Version: TLS 1.0 (0x0301)

Le texte complet est en tls13.txt. Notez bien que la négociation est en clair.

Quelques autres articles à lire :


Téléchargez le RFC 8446


L'article seul

RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier

Date de publication du RFC : Août 2018
Auteur(s) du RFC : Y. Nir (Check Point), S. Josefsson (SJD AB), M. Pegourie-Gonnard (Independent / PolarSSL)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 7 août 2018


Ce RFC décrit les algorithmes cryptographiques à base de courbes elliptiques utilisés dans TLS. Il remplace le RFC 4492.

Plus exactement, il normalise les algorithmes utilisés dans les versions de TLS allant jusqu'à 1.2 incluse. L'utilisation des courbes elliptiques par TLS 1.3 est décrite dans le RFC sur TLS 1.3, le RFC 8446. Les deux points importants de ce nouveau RFC sont :

  • L'utilisation de courbes elliptiques pour un échange Diffie-Hellman de la clé de session TLS (ce qu'on nomme ECDHE),
  • Les algorithmes de signature à courbes elliptiques ECDSA et EdDSA pour authentifier le pair TLS.

Commençons par l'échange de clés de session (section 2). TLS nécessite que les deux pairs se mettent d'accord sur une clé de chiffrement symétrique qui sera ensuite utilisée pendant toute la session, avec des algorithmes comme AES. Une des façons de synchroniser cette clé de session est qu'un des pairs la génère aléatoirement, puis la chiffre avec la clé publique (chiffrement asymétrique) de son pair avant de lui transmettre (cela marche avec RSA mais je n'ai pas l'impression qu'il y ait un moyen normalisé de faire cela avec les courbes elliptiques). Une autre façon est d'utiliser un échange Diffie-Hellman. Contrairement à l'échange Diffie-Hellman originel, celui présenté dans ce RFC, ECDHE, utilise la cryptographie sur courbes elliptiques. (J'ai simplifié pas mal : par exemple, l'échange ECDHE ne donnera pas directement la clé de session, celle-ci sera en fait dérivée de la clé obtenue en ECDHE.) Le principal avantage de Diffie-Hellman est de fournir de la sécurité même en cas de compromission ultérieure de la clé privée.

Notre RFC présente trois variantes d'ECDHE, selon la manière dont l'échange est authentifié, l'une utilisant ECDSA ou EdDSA, l'autre utilisant le traditionnel RSA, et la troisième n'authentifiant pas du tout, et étant donc vulnérable aux attaques de l'Homme du Milieu, sauf si une authentification a lieu en dehors de TLS. (Attention, dans le cas de ECDHE_RSA, RSA n'est utilisé que pour authentifier l'échange, la génération de la clé se fait bien en utilisant les courbes elliptiques.)

Lorsque l'échange est authentifié (ECDHE_ECDSA - qui, en dépit de son nom, inclut EdDSA - ou bien ECDHE_RSA), les paramètres ECDH (Diffie-Hellman avec courbes elliptiques) sont signés par la clé privée (ECDSA, EdDSA ou RSA). S'il n'est pas authentifié (ECDH_anon, mais notez que le nom est trompeur, bien que le E final - ephemeral - manque, la clé est éphémère), on n'envoie évidemment pas de certificat, ou de demande de certificat.

Voilà, avec cette section 2, on a pu générer une clé de session avec Diffie-Hellman, tout en authentifiant le serveur avec des courbes elliptiques. Et pour l'authentification du client ? C'est la section 3 de notre RFC. Elle décrit un mécanisme ECDSA_sign (là encore, en dépit du nom du mécanisme, il fonctionne aussi bien pour EdDSA), où le client s'authentifie en signant ses messages avec un algorithme à courbes elliptiques.

Les courbes elliptiques ont quelques particularités qui justifient deux extensions à TLS que présente la section 4 du RFC. Il y a Supported Elliptic Curves Extension et Supported Point Formats Extension, qui permettent de décrire les caractéristiques de la courbe elliptique utilisée (on verra plus loin que la deuxième extension ne sert plus guère). Voici, vue par tshark, l'utilisation de ces extensions dans un ClientHello TLS envoyé par OpenSSL :

Extension: elliptic_curves
                Type: elliptic_curves (0x000a)
                Length: 28
                Elliptic Curves Length: 26
                Elliptic curves (13 curves)
                    Elliptic curve: secp256r1 (0x0017)
                    Elliptic curve: secp521r1 (0x0019)
                    Elliptic curve: brainpoolP512r1 (0x001c)
                    Elliptic curve: brainpoolP384r1 (0x001b)
                    Elliptic curve: secp384r1 (0x0018)
                    Elliptic curve: brainpoolP256r1 (0x001a)
                    Elliptic curve: secp256k1 (0x0016)
...
Extension: ec_point_formats
                Type: ec_point_formats (0x000b)
                Length: 4
                EC point formats Length: 3
                Elliptic curves point formats (3)
                    EC point format: uncompressed (0)
                    EC point format: ansiX962_compressed_prime (1)
                    EC point format: ansiX962_compressed_char2 (2)

La section 5 du RFC donne les détails concrets. Par exemple, les deux extensions citées plus haut s'écrivent, dans le langage de TLS (cf. section 4 du RFC 5246) :

enum {
          elliptic_curves(10),
          ec_point_formats(11)
} ExtensionType;

La première extension permet d'indiquer les courbes utilisées. Avec celles du RFC 7748, cela donne, comme possibilités :

enum {
               deprecated(1..22),
               secp256r1 (23), secp384r1 (24), secp521r1 (25),
               x25519(29), x448(30),
               reserved (0xFE00..0xFEFF),
               deprecated(0xFF01..0xFF02),
               (0xFFFF)
} NamedCurve;  

secp256r1 est la courbe P-256 du NIST, x25519 est la Curve-25519 de Bernstein. Notez que beaucoup des courbes de l'ancien RFC 4492, jamais très utilisées, ont été abandonnées. (Les courbes se trouvent dans un registre IANA.)

Normalement, dans TLS, on peut choisir séparément l'algorithme de signature et celui de condensation (cf. section 7.4.1.4.1 du RFC 5246). Avec certains algorithmes comme EdDSA dans sa forme « pure », il n'y a pas de condensation séparée et un « algorithme » bidon, Intrinsic (valeur 8) a été créé pour mettre dans le champ « algorithme de condensation » de l'extension signature_algorithms.

Voici une négociation TLS complète, vue par curl :

%  curl -v https://www.nextinpact.com
...
* Connected to www.nextinpact.com (2400:cb00:2048:1::6819:f815) port 443 (#0)
...
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
...
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=CA; L=San Francisco; O=CloudFlare, Inc.; CN=nextinpact.com
...
> GET / HTTP/1.1
> Host: www.nextinpact.com
> User-Agent: curl/7.52.1
> Accept: */*

On voit que l'algorithme utilisé par TLS est ECDHE-ECDSA-AES128-GCM-SHA256, ce qui indique ECDHE avec ECDSA. Le certificat du serveur doit donc inclure une clé « courbe elliptique ». Regardons ledit certificat :

% openssl s_client -connect www.nextinpact.com:443 -showcerts | openssl x509 -text
Certificate:
...
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO ECC Domain Validation Secure Server CA 2
...
        Subject: OU = Domain Control Validated, OU = PositiveSSL Multi-Domain, CN = ssl378410.cloudflaressl.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
...
                ASN1 OID: prime256v1
                NIST CURVE: P-256
...
            X509v3 Subject Alternative Name: 
                DNS:ssl378410.cloudflaressl.com, DNS:*.baseballwarehouse.com, DNS:*.campusgroups.com, DNS:*.cretedoc.gr, DNS:*.groupment.com, DNS:*.icstage.com, DNS:*.ideacouture.com, DNS:*.industrialtour-deloitte.com, DNS:*.jonessnowboards.com, DNS:*.nextinpact.com, DNS:*.pcinpact.com, DNS:*.pinkapple.com, DNS:*.softballrampage.com, DNS:*.undercovercondoms.co.uk, DNS:baseballwarehouse.com, DNS:campusgroups.com, DNS:cretedoc.gr, DNS:groupment.com, DNS:icstage.com, DNS:ideacouture.com, DNS:industrialtour-deloitte.com, DNS:jonessnowboards.com, DNS:nextinpact.com, DNS:pcinpact.com, DNS:pinkapple.com, DNS:softballrampage.com, DNS:undercovercondoms.co.uk
    Signature Algorithm: ecdsa-with-SHA256

On a bien une clé sur la courbe P-256.

Quel est l'état des mises en œuvre de ces algorithmes dans les bibliothèques TLS existantes ? ECDHE et ECDSA avec les courbes NIST sont très répandus. ECDHE avec la courbe Curve25519 est également dans plusieurs bibliothèques TLS. Par contre, EdDSA, ou ECDHE avec la courbe Curve448, sont certes implémentés mais pas encore largement déployés.

Les changements depuis le RFC 4492 sont résumés dans l'annexe B. Souvent, une norme récente ajoute beaucoup de choses par rapport à l'ancienne mais, ici, pas mal de chose ont au contraire été retirées :

  • Plusieurs courbes peu utilisées disparaissent,
  • Il n'y a plus qu'un seul format de point accepté (uncompressed),
  • Des algorithmes comme toute la famille ECDH_ECDSA (ECDH et pas ECDHE, donc ceux dont la clé n'est pas éphémère) sont retirés.

Parmi les ajouts, le plus important est évidemment l'intégration des « courbes Bernstein », Curve25519 et Curve448, introduites par le RFC 7748. Et il y avait également plusieurs erreurs techniques dans le RFC 4492, qui sont corrigées par notre nouveau RFC.

Et, pendant que j'y suis, si vous voulez générer un certificat avec les courbes elliptiques, voici comment faire avec OpenSSL :

% openssl ecparam -out ec_key.pem -name prime256v1 -genkey
% openssl req -new -key ec_key.pem  -nodes -days 1000 -out cert.csr
  

J'ai utilisé ici la courbe P-256 (prime256v1 est encore un autre identificateur pour la courbe NIST P-256, chaque organisme qui normalise dans ce domaine ayant ses propres identificateurs). Si vous voulez la liste des courbes que connait OpenSSL :

% openssl ecparam -list_curves 

Ce blog est accessible en TLS mais pas avec des courbes elliptiques. En effet, l'AC que j'utilise, CAcert, ne les accepte hélas pas (« The keys you supplied use an unrecognized algorithm. For security reasons these keys can not be signed by CAcert. ») Il y a des raisons pour cela mais c'est quand même déplorable. (Enfin, j'accepte quand même ECDHE.)

Enfin, un échange TLS complet vu par tshark est visible ici.

Merci à Manuel Pégourié-Gonnard pour sa relecture vigilante.


Téléchargez le RFC 8422


L'article seul

RFC 8399: Internationalization Updates to RFC 5280

Date de publication du RFC : Mai 2018
Auteur(s) du RFC : R. Housley (Vigil Security)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lamps
Première rédaction de cet article le 30 juillet 2018


Ce court RFC ajoute aux certificats PKIX du RFC 5280 la possibilité de contenir des adresses de courrier électronique dont la partie locale est en Unicode. Et il modifie légèrement les règles pour les noms de domaine en Unicode dans les certificats.

Les certificats sur l'Internet sont normalisés dans le RFC 5280, qui décrit un profil de X.509 nommé PKIX (définir un profil était nécessaire car la norme X.509 est bien trop riche et complexe). Ce RFC 5280 permettait des noms de domaine en Unicode (sections 4.2.1.10 et 7 du RFC 5280) mais il suivait l'ancienne norme IDN, celle des RFC 3490 et suivants. Depuis, les IDN sont normalisés dans le RFC 5890 et suivants, et notre nouveau RFC 8399 modifie très légèrement le RFC 5280 pour s'adapter à cette nouvelle norme de noms de domaines en Unicode. Les noms de domaine dans un certificat peuvent être présents dans les champs Sujet (titulaire du certificat) et Émetteur (AC ayant signé le certificat) mais aussi dans les contraintes sur le nom (une autorité de certification peut être limitée à des noms se terminant en example.com, par exemple).

Notez que, comme avant, ces noms sont exprimés dans le certificat en Punycode (RFC 3492, xn--caf-dma.fr au lieu de café.fr). C'est un bon exemple du fait que les limites qui rendaient difficiles d'utiliser des noms de domaine en Unicode n'avaient rien à voir avec le DNS (qui n'a jamais été limité à ASCII, contrairement à ce qu'affirme une légende courante). En fait, le problème venait des applications (comme PKIX), qui ne s'attendaient pas à des noms en Unicode. Un logiciel qui traite des certificats aurait été bien étonné de voir des noms de domaines non-ASCII, et aurait peut-être planté. D'où ce choix du Punycode.

Nouveauté plus importante de notre RFC 8399, les adresses de courrier électronique en Unicode. Elles étaient déjà permises par la section 7.5 du RFC 5280, mais seulement pour la partie domaine (à droite du @). Désormais, elles sont également possibles dans la partie locale (à gauche du @). Le RFC 8398 donne tous les détails sur ce sujet.

Reste à savoir quelles AC vont accepter Unicode. J'ai testé avec Let's encrypt (avec le client Dehydrated, en mettant le Punycode dans domains.txt) et ça marche, regardez le certificat de https://www.potamochère.fr/. Le voici, affiché par GnuTLS :

% gnutls-cli www.potamochère.fr
...
- subject `CN=www.xn--potamochre-66a.fr', issuer `CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US', serial 0x03ed9617bb88bab3ad5b236675d1dd6e5d27, ...
    

D'autres AC acceptent ces noms en Unicode : Gandi le fait aussi, regardez le certificat de https://réussir-en.fr. On notera que le célèbre service de test de la qualité des configurations TLS, SSLlabs, gère bien les IDN : ssllabs-potamochere.png


Téléchargez le RFC 8399


L'article seul

RFC 8375: Special-Use Domain 'home.arpa.'

Date de publication du RFC : Mai 2018
Auteur(s) du RFC : P. Pfister (Cisco Systems), T. Lemon (Nominum)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF homenet
Première rédaction de cet article le 18 mai 2018


Ce nouveau RFC a l'air compliqué comme cela, mais en fait il ne fait qu'une chose : remplacer, dans le protocole Homenet/HNCP (Home Networking Control Protocol), le nom de domaine .home par home.arpa.

home.arpa est désormais enregistré dans la liste officielle des noms de domaine spéciaux, ceux qui ne passent pas par les mécanismes habituels d'enregistrement de noms de domaine, et/ou les mécanismes habituels de résolution DNS. (Cette liste a été créée par le RFC 6761, et critiquée par le RFC 8244. home.arpa n'étant pas un TLD, il pose moins de problèmes politiciens.)

Quelle est l'utilité de ce nom home.arpa ? La série de protocoles Homenet (l'architecture de Homenet est décrite dans le RFC 7368) vise à doter la maison de l'utilisateur normal (pas participant à l'IETF) d'un ensemble de réseaux IPv6 qui marchent automatiquement, sans intervention humaine. Parmi les protocoles Homenet, HNCP, normalisé dans le protocole RFC 7788 est le protocole de configuration. Il utilise un suffixe pour les noms de domaines comme nas.SUFFIXE ou printer.SUFFIX. C'est ce home.arpa qui va désormais servir de suffixe.

Mais quel était le problème avec le suffixe .home du RFC 7788 ? D'abord, le RFC 7788 avait commis une grosse erreur, enregistrée sous le numéro 4677 : il ne tenait pas compte des règles du RFC 6761, et réservait ce TLD .home sans suivre les procédures du RFC 6761. Notamment, il ne listait pas les particularités qui font que ce domaine est spécial (pour home.arpa, notre nouveau RFC 8375 le fait dans sa section 5), et il ne demandait pas à l'IANA de le mettre dans le registre des noms de domaine spéciaux. Cela avait des conséquences pratiques comme le fait que ce .home ne pouvait pas marcher à travers un résolveur DNS validant (puisque ce nom n'existait pas du tout dans la racine). Un bon article sur ce choix et sur les problèmes qu'il posait était « Homenet, and the hunt for a name ».

On peut aussi ajouter que le risque de « collision » entre deux noms de domaine était élevé puisque pas mal de réseaux locaux sont nommés sous .home et que ce nom est un de ceux qui « fuitent » souvent vers les serveurs racines (voir par exemple les statistiques du serveur racine L.). On peut consulter à ce sujet les documents de l'ICANN « New gTLD Collision Risk Mitigation » et « New gTLD Collision Occurence Management ». Notons qu'il y avait eu plusieurs candidatures (finalement rejetées en février 2018) pour un .home en cours auprès de l'ICANN. Exit, donc, .home, plus convivial mais trop convoité. Demander à l'ICANN de déléguer un .home pour l'IETF (ce qui aurait été nécessaire pour faire une délégation DNSSEC non signée, cf. RFC 4035, section 4.3) aurait pris dix ou quinze ans.

À la place, voici home.arpa, qui profite du RFC 3172, et du caractère décentralisé du DNS, qui permet de déléguer des noms sous .arpa.

L'utilisation de home.arpa n'est pas limitée à HNCP, tous les protocoles visant le même genre d'usage domestique peuvent s'en servir. Il n'a évidemment qu'une signification locale.

La section 3 décrit le comportement général attendu avec home.arpa. Ce n'est pas un nom de domaine comme les autres. Sa signification est purement locale. printer.home.arpa désignera une machine à un endroit et une autre machine dans une autre maison. Les serveurs DNS globaux ne peuvent pas être utilisés pour résoudre les noms sous home.arpa. Tous les noms se terminant par ce suffixe doivent être traités uniquement par les résolveurs locaux, et jamais transmis à l'extérieur.

Notez que, la plupart du temps, les utilisateurs ne verront pas le suffixe home.arpa, l'interface des applications « Homenet » leur masquera cette partie du nom. Néanmoins, dans certains cas, le nom sera sans doute visible, et il déroutera sans doute certains utilisateurs, soit à cause du suffixe arpa qui n'a pas de signification pour eux, soit parce qu'ils ne sont pas anglophones et qu'ils ne comprennent pas le home. Il n'y a pas de solution miracle à ce problème.

La section 4 est le formulaire d'enregistrement dans le registre des noms spéciaux, suivant les formalités du RFC 6761, section 5. (Ce sont ces formalités qui manquaient au RFC 7788 et qui expliquent l'errata.) Prenons-les dans l'ordre (relisez bien la section 5 du RFC 6761) :

  • Les humains et les applications qu'ils utilisent n'ont pas à faire quelque chose de particulier, ces noms, pour eux, sont des noms comme les autres.
  • Les bibliothèques de résolution de noms (par exemple, sur Mint, la GNU libc) ne devraient pas non plus appliquer un traitement spécial aux noms en home.arpa. Elles devraient passer par le mécanisme de résolution normal. Une exception : si la machine a été configurée pour utiliser un autre résolveur DNS que celui de la maison (un résolveur public, par exemple, qui ne connaîtra pas votre home.arpa ), il peut être nécessaire de mettre une règle particulière pour faire résoudre ces noms par un résolveur local.
  • Les résolveurs locaux (à la maison), eux, doivent traiter ces noms à part, comme étant des « zones locales » à l'image de celles décrites dans le RFC 6303. Bref, le résolveur ne doit pas transmettre ces requêtes aux serveurs publics faisant autorité (il y a une exception pour le cas particulier des enregistrements DS). Ils doivent transmettre ces requêtes aux serveurs locaux qui font autorité pour ces noms (cf. section 7).
  • Les serveurs publics faisant autorité n'ont pas besoin d'un comportement particulier. Par exemple, ceux qui font autorité pour .arpa retournent une délégation normale.

Voici la délégation :


% dig @a.root-servers.net ANY home.arpa

; <<>> DiG 9.10.3-P4-Debian <<>> @a.root-servers.net ANY home.arpa
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48503
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;home.arpa.		IN ANY

;; AUTHORITY SECTION:
home.arpa.		172800 IN NS blackhole-1.iana.org.
home.arpa.		172800 IN NS blackhole-2.iana.org.
home.arpa.		86400 IN NSEC in-addr.arpa. NS RRSIG NSEC
home.arpa.		86400 IN RRSIG NSEC 8 2 86400 (
				20180429000000 20180415230000 56191 arpa.
				K4+fNoY6SXQ+VtHsO5/F0oYrRjZdNSG0MSMaeDSQ78aC
				NHko4uqNAzoQzoM8a2joFeP4wOL6kVQ72UJ5zqd/iZJD
				0ZSh/57lCUVxjYK8sL0dWy/3xr7kbaqi58tNVTLkp8GD
				TfyQf5pW1rtRB/1pGzbmTZkK1jXw4ThG3e9kLHk= )

;; Query time: 24 msec
;; SERVER: 2001:503:ba3e::2:30#53(2001:503:ba3e::2:30)
;; WHEN: Mon Apr 16 09:35:35 CEST 2018
;; MSG SIZE  rcvd: 296

      

La section 5 rassemble les changements dans la norme HNCP (RFC 7788. C'est juste un remplacement de .home par home.arpa.

Quelques petits trucs de sécurité (section 6). D'abord, il ne faut pas s'imaginer que ces noms locaux en home.arpa sont plus sûrs que n'importe quel autre nom. Ce n'est pas parce qu'il y a home dedans qu'on peut leur faire confiance. D'autant plus qu'il y a, par construction, plusieurs home.arpa, et aucun moyen, lorsqu'on se déplace de l'un à l'autre, de les différencier. (Des travaux ont lieu pour concevoir un mécanisme qui pourrait permettre d'avertir l'utilisateur « ce n'est pas le home.arpa que vous pensez » mais ils n'ont pas encore abouti.)

home.arpa n'est pas sécurisé par DNSSEC. Il ne serait pas possible de mettre un enregistrement DS dans .arpa puisqu'un tel enregistrement est un condensat de la clé publique de la zone et que chaque home.arpa qui serait signé aurait sa propre clé. Une solution possible aurait été de ne pas déléguer home.arpa. .arpa étant signé, une telle non-délégation aurait pu être validée par DNSSEC (« denial of existence »). La réponse DNS aurait été (commande tapée avant la délégation de home.arpa) :


% dig A printer.home.arpa
...
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 37887
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 1
...
;; AUTHORITY SECTION:
arpa.			10800 IN SOA a.root-servers.net. nstld.verisign-grs.com. (
				2017112001 ; serial
				1800       ; refresh (30 minutes)
				900        ; retry (15 minutes)
				604800     ; expire (1 week)
				86400      ; minimum (1 day)
				)
arpa.			10800 IN RRSIG SOA 8 1 86400 (
				20171203120000 20171120110000 36264 arpa.
				QqiRv85fb6YO/79ZdtQ8Ke5FmZHF2asjLrNejjcivAAo...
arpa.			10800 IN RRSIG NSEC 8 1 86400 (
				20171203120000 20171120110000 36264 arpa.
				dci8Yr95yQtL9nEBFL3dpdMVTK3Z2cOq+xCujeLsUm+W...
arpa.			10800 IN NSEC as112.arpa. NS SOA RRSIG NSEC DNSKEY
e164.arpa.		10800 IN RRSIG NSEC 8 2 86400 (
				20171203120000 20171120110000 36264 arpa.
				jfJS6QuBEFHWgc4hhtvdfR0Q7FCCgvGNIoc6169lsxz7...
e164.arpa.		10800 IN NSEC in-addr.arpa. NS DS RRSIG NSEC

;; Query time: 187 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Nov 20 20:28:27 CET 2017
;; MSG SIZE  rcvd: 686

Ici, on reçoit un NXDOMAIN (ce domaine n'existe pas), et les enregistrements NSEC qui prouvent que home.arpa n'existe pas non plus (rien entre e164.arpa et in-addr.arpa). Mais cela aurait nécessité un traitement spécial de home.arpa par le résolveur validant (par exemple, sur Unbound, domain-insecure: "home.arpa"). Finalement, le choix fait a été celui d'une délégation non sécurisée (section 7 du RFC), vers les serveurs blackhole-1.iana.org et blackhole-2.iana.org :


% dig NS home.arpa

; <<>> DiG 9.10.3-P4-Debian <<>> NS home.arpa
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 64059
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;home.arpa.		IN NS

;; ANSWER SECTION:
home.arpa.		190 IN NS blackhole-1.iana.org.
home.arpa.		190 IN NS blackhole-2.iana.org.

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Apr 16 09:36:25 CEST 2018
;; MSG SIZE  rcvd: 98

    

Cette délégation a été faite le 15 mars 2018.

Le domaine home.arpa a été ajouté dans le registre des noms de domaine spéciaux ainsi que dans celui des noms servis localement.

En testant avec les sondes RIPE Atlas, on voit que tous les résolveurs ne voient pas la même chose, ce qui est normal, chaque maison pouvant avoir son home.arpa local :

    
% blaeu-resolve -r 1000 -q SOA home.arpa
[prisoner.iana.org. hostmaster.root-servers.org. 1 604800 60 604800 604800] : 548 occurrences 
[prisoner.iana.org. hostmaster.root-servers.org. 1 1800 900 604800 604800] : 11 occurrences 
[prisoner.iana.org. hostmaster.root-servers.org. 1 1800 900 604800 15] : 33 occurrences 
[prisoner.iana.org. hostmaster.root-servers.org. 2002040800 1800 900 604800 60480] : 229 occurrences 
[ERROR: FORMERR] : 1 occurrences 
[ERROR: SERVFAIL] : 132 occurrences 
[] : 4 occurrences 
[prisoner.iana.org. hostmaster.root-servers.org. 1 604800 60 604800 3600] : 11 occurrences 
[prisoner.iana.org. hostmaster.trex.fi. 1 604800 86400 2419200 86400] : 4 occurrences 
[prisoner.iana.org. ops.inx.net.za. 1513082668 10800 3600 604800 3600] : 2 occurrences 
[TIMEOUT(S)] : 19 occurrences 
Test #12177308 done at 2018-04-16T07:38:32Z

On voit sur ce premier test que la grande majorité des sondes voient le vrai SOA (numéro de série 1 ou 2002040800 ; curieusement, les serveurs faisant autorité envoient des numéros différents). Certaines voient un tout autre SOA (par exemple celle où l'adresse du responsable est en Afrique du Sud ou bien en Finlande), et le numéro de série très différent. Ce n'est pas un problème ou un piratage : le principe de home.arpa est que chacun peut avoir le sien.

À l'heure actuelle, toutes les mises en œuvre en logiciel libre que j'ai regardées utilisent encore .home, mais elles semblent souvent non maintenues.


Téléchargez le RFC 8375


L'article seul

RFC 8374: BGPsec Design Choices and Summary of Supporting Discussions

Date de publication du RFC : Avril 2018
Auteur(s) du RFC : K. Sriram (USA NIST)
Pour information
Première rédaction de cet article le 1 mai 2018


Ce RFC est un peu spécial : il ne normalise pas un protocole, ni des procédures internes à l'IETF, et il n'est pas non plus la description d'un problème à résoudre, ou un cahier des charges d'une solution à développer. Non, ce RFC est la documentation a posteriori des choix qui ont été effectués lors du développement de BGPsec, une solution de sécurisation du routage Internet. C'est donc un document utile si vous lisez les RFC sur BGPsec, comme le RFC 8205 et que vous vous demandez « mais pourquoi diable ont-ils choisi cette approche et pas cette autre, qui me semble bien meilleure ? »

Un petit rappel du contexte : le protocole de routage BGP fonctionne en échangeant des informations entre pairs, sur les routes que chaque pair sait joindre. Par défaut, un pair peut raconter n'importe quoi, dire qu'il a une route vers 2001:db8::/32 alors qu'il n'est pas le titulaire de ce préfixe et n'a pas non plus appris cette route d'un de ses pairs. Cela rend donc le routage Internet assez vulnérable. Pour le sécuriser, il existe plusieurs mécanismes qui font que, en pratique, ça ne marche pas trop mal. L'IETF a développé une solution technique, qui a deux couches : une infrastructure à clés publiques, la RPKI, normalisée dans les RFC 6480 et RFC 6481, et une seconde couche, les services qui utilisent la RPKI pour authentifier tel ou tel aspect du routage. Deux de ces services sont normalisés, les ROA (Route Origin Authorization) des RFC 6482 et RFC 6811, qui servent à authentifier l'AS d'origine d'un préfixe, et BGPsec (RFC 8205), qui sert à authentifier le chemin d'AS, la liste des AS empruntés par une annonce de route (cf. section 2.1.1 de notre RFC). Sans BGPsec, les ROA, utilisés seuls, ne peuvent pas arrêter certaines attaques (cf. RFC 7132, qui explique quelles menaces traite BGPsec, et RFC 7353, cahier des charges de BGPsec). Par exemple, si l'AS 64641, malhonnête, veut tromper son pair, l'AS 64642, à propos du préfixe 2001:db8::/32, et que ce préfixe a un ROA n'autorisant que 64643 à être à l'origine, le malhonnête peut fabriquer une annonce avec le chemin d'AS 64641 [éventuellement d'autres AS] 64643 (rappelez-vous que les chemins d'AS se lisent de droite à gauche) et l'envoyer à 64641. Si celui-ci vérifie le ROA, il aura l'impression que tout est normal, l'AS 64643 étant bien indiqué comme l'origine. Et cela marchera même si les annonces de l'AS 64643 ne sont jamais passées par ce chemin ! BGPsec répare ce problème en transportant un chemin d'AS signé, authentifiant toutes les étapes.

Lors du développement de BGPsec, un document avait été rédigé pour documenter tous les choix effectués, mais n'avait pas été publié. C'est désormais chose faite avec ce RFC, qui documente les choix, les justifie, et explique les différences entre ces choix initiaux et le protocole final, modifié après un long développement. Parmi les points importants du cahier des charges (RFC 7353) :

  • Valider la totalité du chemin d'AS (et pas seulement détecter certains problèmes, comme le font les ROA),
  • Déployable de manière incrémentale puisqu'il est évidemment impossible que tous les opérateurs adoptent BGPsec le même jour,
  • Ne pas diffuser davantage d'informations que celles qui sont déjà diffusées. Par exemple, les opérateurs ne souhaitent pas forcément publier la liste de tous leurs accords d'appairage.

Finie, l'introduction, passons tout de suite à certains des choix qui ont été effectués. (Il est évidemment recommandé de lire le RFC 8205 avant, puis de lire notre RFC 8374 après, si on veut tous les choix documentés.) D'abord, à quoi ressemblent les signatures, et les nouveaux messages BGP (section 2 de notre RFC). Les signatures BGPsec signent le préfixe IP, l'AS signataire, et l'AS suivant à qui on va transmettre l'annonce. Le premier point important est qu'on signe l'AS suivant, de manière à créer une chaîne de signatures vérifiables. Autrement, un attaquant pourrait fabriquer un faux chemin à partir de signatures correctes.

Si on utilise le prepending d'AS, la première version de BGPsec prévoyait une signature à chaque fois, mais on peut finalement mettre une seule signature pour la répétition d'AS, avec une variable pCount qui indique leur nombre (RFC 8205, section 3.1), afin de diminuer le nombre de signatures (les signatures peuvent représenter la grande majorité des octets d'une annonce BGP).

Le second point important est que certains attributs ne sont pas pas signés, comme par exemple la préférence locale (RFC 4271, section 5.1.5) ou les communautés (cf. RFC 1997). Ces attributs pourront donc être modifiés par un attaquant à sa guise. Ce n'est pas si grave que ça en a l'air car la plupart de ces attributs n'ont de sens qu'entre deux AS (c'est le cas de la communauté NO_EXPORT) ou sont internes à un AS (la préférence locale). Sur ces courtes distances, on espère de toute façon que la session BGP sera protégée, par exemple par AO (RFC 5925).

La signature est transportée dans un attribut BGP optionnel et non-transitif (qui n'est pas transmis tel quel aux routeurs suivants). Optionnel car, sinon, il ne serait pas possible de déployer BGPsec progressivement. Et non-transitif car un routeur BGPsec n'envoie les signatures que si le routeur suivant lui a dit qu'il savait gérer BGPsec.

Dans le message BGP signé, le routeur qui signe est identifié par sa clé publique, pas par le certificat entier. Cela veut dire qu'on ne peut valider les signatures que si on a accès à une dépôt de la RPKI, avec les certificats.

La section 3 de notre RFC traite le cas des retraits de route : contrairement aux annonces, ils ne sont pas signés. Un AS est toujours libre de retirer une route qu'il a annoncée (BGP n'accepte pas un retrait venant d'un autre pair). Si on a accepté l'annonce, il est logique d'accepter le retrait (en supposant évidemment que la session entre les deux routeurs soit raisonnablement sécurisée).

La section 4, elle, parle des algorithmes de signature. Le RFC 8208 impose ECDSA avec la courbe P-256 (cf. RFC 6090). RSA avait été envisagé mais ECDSA avait l'avantage de signatures bien plus petites (cf. l'étude du NIST sur les conséquences de ce choix).

Une autre décision importante dans cette section est la possibilité d'avoir une clé par routeur et pas par AS (cf. RFC 8207). Cela évite de révoquer un certificat global à l'AS si un seul routeur a été piraté. (Par contre, il me semble que c'est indiscret, permettant de savoir quel routeur de l'AS a relayé l'annonce, une information qu'on n'a pas forcément actuellement.)

Décision plus anecdotique, en revanche, celle comme quoi le nom dans le certificat (Subject) sera la chaîne router suivie du numéro d'AS (cf. RFC 5396) puis de l'identité BGP du routeur. Les détails figurent dans le RFC 8209.

Voyons maintenant les problèmes de performance (section 5). Par exemple, BGPsec ne permet pas de regrouper plusieurs préfixes dans une annonce, comme on peut le faire traditionnellement avec BGP. C'est pour simplifier le protocole, dans des cas où un routeur recevrait une annonce avec plusieurs préfixes et n'en transmettrait que certains. Actuellement, il y a en moyenne quatre préfixes par annonce (cf. l'étude faite par l'auteur du RFC). Si tout le monde adoptait BGPsec, on aurait donc quatre fois plus d'annonces, et il faudra peut-être dans le futur optimiser ce cas.

On l'a vu plus haut, il n'est pas envisageable de déployer BGPsec partout du jour au lendemain. Il faut donc envisager les problèmes de coexistence entre BGPsec et BGP pas sécurisé (section 6 de notre RFC). Dès que deux versions d'un protocole, une sécurisée et une qui ne l'est pas, coexistent, il y a le potentiel d'une attaque par repli, où l'attaquant va essayer de convaindre une des parties de choisir la solution la moins sécurisée. Actuellement, BGPsec ne dispose pas d'une protection contre cette attaque. Ainsi, il n'y a pas de moyen de savoir si un routeur qui envoie des annonces non-signées le fait parce qu'il ne connait pas BGPsec, ou simplement parce qu'un attaquant a modifié le trafic.

La possibilité de faire du BGPsec est négociée à l'établissement de la session BGP. Notez qu'elle est asymétrique. Par exemple, il est raisonnable qu'un routeur de bordure signe ses propres annonces mais accepte tout de la part de ses transitaires, et ne lui demande donc pas d'envoyer les signatures. Pendant un certain temps (probablement plusieurs années), nous aurons donc des ilots BGPsec au milieu d'un océan de routeurs qui font du BGP traditionnel. On peut espérer qu'au fur et à mesure du déploiement, ces ilots se rejoindront et formeront des iles, puis des continents.

La question de permettre des chemins d'AS partiellement signés avait été discutée mais cela avait été rejeté : il faut signer tout le chemin, ou pas du tout. Des signatures partielles auraient aidé au déploiement progressif mais auraient été dangereuses : elle aurait permis aux attaquants de fabriquer des chemins valides en collant des bouts de chemins signés - et donc authentiques - avec des bouts de chemins non-signés et mensongers.

La section 7 de notre RFC est consacrée aux interactions entre BGPsec et les fonctions habituelles de BGP, qui peuvent ne pas bien s'entendre avec la nouvelle sécurité. Par exemple, les très utiles communautés BGP (RFC 1997 et RFC 8092). On a vu plus haut qu'elles n'étaient pas signées du tout et donc pas protégées. La raison est que les auteurs de BGPsec considèrent les communautés comme mal fichues, question sécurité. Certaines sont utilisées pour des décisions effectives par les routeurs, d'autres sont juste pour le déboguage, d'autres encore purement pour information. Certaines sont transitives, d'autres utilisées seulement entre pairs se parlant directement. Et elles sont routinement modifiées en route. Le RFC conseille, pour celles qui ne sont utilisées qu'entre pairs directs, de plutôt sécuriser la session BGP.

Pour les communautés qu'on veut voir transmises transitivement, il avait été envisagé d'utiliser un bit libre pour indiquer que la communauté était transitive et donc devait être incluse dans la signature. Mais la solution n'a pas été retenue. Conseil pratique, dans la situation actuelle : attention à ne pas utiliser des communautés transmises transitivement pour des décisions de routage.

Autre cas pratique d'interaction avec un service très utile, les serveurs de route. Un point d'échange peut fonctionner de trois façons :

  • Appairages directs entre deux membres et, dans ce cas, BGPsec n'a rien de particulier à faire.
  • Utilisation d'un serveur de routes qui ajoute son propre AS dans le chemin (mais, évidemment, ne change pas le NEXT_HOP, c'est un serveur de routes, pas un routeur). Cette méthode est de loin la plus rare des trois. Là aussi, BGPsec n'a rien de particulier à faire.
  • Utilisation d'un serveur de routes qui n'ajoute pas son propre AS dans le chemin. Sur un gros point d'échange, cette méthode permet d'éviter de gérer des dizaines, voire des centaines d'appairages. Pour BGPsec, c'est le cas le plus délicat. Il faut que le serveur de routes mette son AS dans le chemin, pour qu'il puisse être validé, mais en positionnant pCount à 0 (sa valeur normale est 1, ou davantage si on utilise le prepending) pour indiquer qu'il ne faut pas en tenir compte pour les décisions de routage (fondées sur la longueur du chemin), seulement pour la validaton.

Un point de transition intéressant est celui des numéros d'AS de quatre octets, dans le RFC 4893. La technique pour que les AS ayant un tel numéro puisse communiquer avec les vieux routeurs qui ne comprennent pas ces AS est un bricolage utilisant un AS spécial (23456), bricolage incompatible avec BGPsec, qui, d'ailleurs, exige que les AS de quatre octets soient acceptés. En pratique, on peut espérer que les derniers routeurs ne gérant pas les AS de quatre octets auront disparu bien avant que BGPsec soit massivement déployé.

La section 8 du RFC discute de la validation des chemins d'AS signés. Par exemple, le RFC 8205 demande qu'un routeur transmette les annonces ayant des signatures invalides. Pourquoi ? Parce que la RPKI n'est que modérement synchrone : il est parfaitement possible qu'un nouveau certificat ne soit arrivé que sur certains routeurs et que, donc, certains acceptent la signature et d'autres pas. Il ne faut donc pas préjuger de ce que pourront valider les copains.

Une question qui revient souvent avec les techniques de sécurité (pas seulement BGPsec mais aussi des choses comme DNSSEC) est « et si la validation échoue, que doit-on faire des données invalides ? » Vous ne trouverez pas de réponse dans le RFC : c'est une décision locale. Pour BGPsec, chaque routeur, ou plus exactement son administrateur, va décider de ce qu'il faut faire avec les annonces dont le chemin d'AS signé pose un problème. Contrairement à DNSSEC, où la validation peut donner trois résultats (oui, en fait, quatre, mais je simplifie, cf. RFC 4035), « sûr », « non sûr », et « invalide », BGPsec n'a que deux résultats possibles, « valide » et « invalide ». L'état « invalide » regroupe aussi bien les cas où le chemin d'AS n'est pas signé (par exemple parce qu'un routeur non-BGPsec se trouvait sur le trajet) que le cas où une signature ne correspond pas aux données (les deux états « non sûr » et « invalide » de DNSSEC se réduisent donc à un seul ici). Il avait été discuté de faire une division plus fine entre les différents cas d'invalidité mais il avait semblé trop complexe de rendre en compte tous les cas possibles. Notez que « invalide » couvre même le cas où un ROA valide l'origine (un cas particulier des chemins partiellement signés, déjà traités).

Donc, si une annonce est invalide, que doit faire le routeur ? D'abord, la décision d'accepter ou pas une route dépend de plusieurs facteurs, la validation BGPsec n'en étant qu'un seul. Ensuite, il n'est pas évident de traiter tous les cas. D'où la décision de laisser le problème à l'administrateur réseaux.

Ah, et si vous utilisez iBGP, notez que la validation BGPsec ne se fait qu'en bord d'AS. Vous pouvez transporter l'information comme quoi une annonce était valide ou invalide comme vous voulez (une communauté à vous ?), il n'existe pas de mécanisme standard dans iBGP pour cela.

Enfin, la section 9 de notre RFC traite de quelques problèmes d'ordre opérationnel. Mais pensez à lire le RFC 8207 avant. Par exemple, elle estime que BGPsec, contrairement à la validation avec les ROA seuls, nécessitera sans doute du nouveau matériel dans les routeurs, comme un coprocesseur cryptographique, et davantage de RAM. C'est une des raisons pour lesquelles on ne verra certainement pas de déploiement significatif de BGPsec avant des années. Ceci dit, au début, les routeurs BGPsec auront peu de travail supplémentaire, précisément puisqu'il y aura peu d'annonces signées, donc pourront retarder les mises à jour matérielles. D'ici que BGPsec devienne banal, des optimisations comme celles décrites dans cet exposé ou celui-ci, ou encore dans l'article « Design and analysis of optimization algorithms to minimize cryptographic processing in BGP security protocols » aideront peut-être.


Téléchargez le RFC 8374


L'article seul

RFC 8373: Negotiating Human Language in Real-Time Communications

Date de publication du RFC : Mai 2018
Auteur(s) du RFC : R. Gellens (Core Technology Consulting)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF slim
Première rédaction de cet article le 26 mai 2018


Le groupe de travail SLIM de l'IETF s'occupe de définir des mécanismes pour le choix d'une langue lors de la communication. Son premier RFC, le RFC 8255, concernait le courrier électronique. Ce nouveau RFC concerne, lui, les communications « en temps réel », comme la téléphonie sur IP.

Un scénario d'usage typique est celui d'un client qui appelle le support d'une société internationale. Mettons que le client a pour langue maternelle l'ourdou mais peut se débrouiller en anglais. On veut qu'il puisse préciser cet ordre de préférences, et, idéalement, que le logiciel utilisé dans le call center le route automatiquement vers un employé qui parle ourdou ou, si aucun n'est disponible, vers un employé qui parle anglais. Plus vital, le scénario d'un appel d'urgence où un touriste danois en vacances en Italie appelle le 112 et où il faut trouver très vite quelqu'un qui peut parler une langue qu'il comprend (sachant qu'en situation d'urgence, on est moins à l'aise avec les langues étrangères). Comme le dit avec euphémisme le RFC « avoir une langue en commun est utile pour la communication ». Pour gérer tous ces scénarios, notre RFC va utiliser les attributs de SDP (RFC 4566, SDP est un format - pas un protocole, en dépit de son nom - déjà très utilisé dans les protocoles de communication instantanée pour transmettre des métadonnées au sujet d'une communication).

Parfois, on a déjà l'information disponible (si on appelle une personne qu'on connait et qui nous connait), et déjà choisi une langue (par exemple une audioconférence dans une entreprise où la règle est que tout se fasse en anglais). Notre RFC traite le cas où on n'a pas cette information, et il faut donc une négociation au début de la communication. Cela implique que le logiciel des deux côtés ait été configuré avec les préférences et capacités linguistiques des deux parties (une question d'interface utilisateur, non spécifiée par ce RFC).

Notez qu'il peut y avoir plusieurs langues différentes utilisées, une pour chaque flux de données. Par exemple, l'appelant peut parler dans une langue que son interlocuteur comprend, mais qu'il a du mal à parler, et il utilisera donc une autre langue pour la réponse. Notez aussi que la communication n'est pas uniquement orale, elle peut être écrite, par exemple pour les malentendants. Le RFC rappelle à juste titre qu'un sourd n'est pas forcément muet et qu'il ou elle peut donc choisir l'oral dans une direction et le texte dans une autre. (Au passage, la synchronisation des lèvres, pour la lecture sur les lèvres, est traitée dans le RFC 5888.)

La solution choisie est décrite en détail dans la section 5 de notre RFC. Elle consiste en deux attributs SDP, hlang-send et hlang-recv (hlang = human language). Leur valeur est évidemment une étiquette de langue, telles qu'elles sont normalisées dans le RFC 5646. Dans une offre SDP, hlang-send est une liste (pas une langue unique) de langues que l'offreur sait parler, séparées par des espaces, donnée dans l'ordre de préférence décroissante, et hlang-recv une liste de langues qu'elle ou lui comprend. Notez qu'il est de la responsabilité de l'offreur (typiquement celui ou celle qui appelle) de proposer des choix réalistes (le RFC donne le contre-exemple d'un offreur qui demanderait à parler en hongrois et à avoir la réponse en portugais…) D'autre part, notre RFC recommande de bien lire la section 4.1 du RFC 5646, qui indique d'étiqueter intelligement, et notamment de ne pas être trop spécifique : si on est australien et qu'on comprend bien l'anglais, indiquer comme langue en est suffisant, préciser (ce qui serait une étiquette légale) en-AU est inutile et même dangereux si le répondant se dit « je ne sais pas parler avec l'accent australien, tant pis, je raccroche ».

La langue choisie par le répondant est indiquée dans la réponse. hlang-send et hlang-recv sont cette fois des langues uniques. Attention, ce qui est envoi pour l'une des parties est réception pour l'autre : hlang-send dans la réponse est donc un choix parmi les hlang-recv de l'offre. L'offreur (l'appelant) est ainsi prévenu du choix qui a été effectué et peut se préparer à parler la langue indiquée par le hlang-recv du répondant, et à comprendre celle indiquée par le hlang-send.

Voici un exemple simple d'un bloc SDP (on n'en montre qu'une partie), où seul l'anglais est proposé ou accepté (cet exemple peut être une requête ou une réponse) :

m=audio 49170 RTP/AVP 0
a=hlang-send:en
a=hlang-recv:en
    

Le cas où hlang-send et hlang-recv ont la même valeur sera sans doute fréquent. Il avait même été envisagé de permettre un seul attribut (par exemple hlang) dans ce cas courant mais cela avait été écarté, au profit de la solution actuelle, plus générale.

Un exemple un peu plus compliqué où la demande propose trois langues (espagnol, basque et anglais dans l'ordre des préférences décroissantes) :

m=audio 49250 RTP/AVP 20
a=hlang-send:es eu en
a=hlang-recv:es eu en
    

Avec une réponse où l'espagnol est utilisé :

m=audio 49250 RTP/AVP 20
a=hlang-send:es
a=hlang-recv:es     
    

Et si ça rate ? S'il n'y a aucune langue en commun ? Deux choix sont possibles, se résigner à utiliser une langue qu'on n'avait pas choisi, ou bien raccrocher. Le RFC laisse aux deux parties la liberté du choix. En cas de raccrochage, le code d'erreur SIP à utiliser est 488 (Not acceptable here) ou bien 606 (Not acceptable), accompagné d'un en-tête d'avertissement avec le code 308, par exemple :

Warning: 308 code proxy.example.com
         "Incompatible language specification: Requested languages "fr
	 zh" not supported. Supported languages are: "es en".
    

Si la langue indiquée est une langue des signes, elle peut être utilisée dans un canal vidéo, mais évidemment pas dans un canal audio. (Le cas d'un canal texte est laissé à l'imagination des lecteurs. Le cas des sous-titres, ou autres textes affichés dans une vidéo, n'est pas traité par notre RFC.)

Voici un exemple bien plus riche, avec plusieurs médias. La vidéo en langue des signes argentine, le texte en espagnol (ou à la rigueur en portugais), et un canal audio, mêmes préférences que le texte :

m=video 51372 RTP/AVP 31 32
a=hlang-send:aed

m=text 45020 RTP/AVP 103 104
a=hlang-send:es pt

m=audio 49250 RTP/AVP 20
a=hlang-recv:es pt     
    

Voici une réponse possible à cette requête, avec de l'espagnol pour le canal texte et pour la voix. Aucune vidéo n'est proposée, sans doute car aucune n'était disponible dans la langue demandée :

m=video 0 RTP/AVP 31 32

m=text 45020 RTP/AVP 103 104
a=hlang-recv:es

m=audio 49250 RTP/AVP 20
a=hlang-send:es    
    

Notez que ce RFC ne fournit pas de mécanisme pour exprimer des préférences entre les différents canaux (texte et audio, par exempe), uniquement entre langues pour un même canal.

Les deux attributs hlang-recv et hlang-send ont été ajoutés au registre IANA des attributs SDP.

Notons que la section 8 du RFC, sur la protection de la vie privée, rappelle qu'indiquer les préférences linguistiques peut permettre d'apprendre des choses sur l'utilisateur, par exemple sa nationalité. Une section Privacy considerations, quoique non obligatoire, est de plus en plus fréquente dans les RFC.

Enfin, question alternatives, le RFC note aussi (section 4) qu'on aurait pu utiliser l'attribut existant lang, qui existe déjà dans SDP (RFC 4566, section 6). Mais il n'est pas mentionné dans le RFC 3264, ne semble pas utilisé à l'heure actuelle, et ne permet pas de spécifier une langue différente par direction de communication.

À ma connaissance, il n'y a pas encore de mise en œuvre de ce RFC mais comme il est cité dans des documents normatifs, par exemple dans le NENA 08-01 de la North American Emergency Number Association, il est possible qu'elles apparaissent bientôt.


Téléchargez le RFC 8373


L'article seul

RFC 8360: Resource Public Key Infrastructure (RPKI) Validation Reconsidered

Date de publication du RFC : Avril 2018
Auteur(s) du RFC : G. Huston (APNIC), G. Michaelson (APNIC), C. Martinez (LACNIC), T. Bruijnzeels (RIPE NCC), A. Newton (ARIN), D. Shaw (AFRINIC)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sidr
Première rédaction de cet article le 6 avril 2018


La RPKI est un ensemble de formats et de règles pour certifier qui est le titulaire d'une ressource Internet, une ressource étant un préfixe d'adresses IP, un numéro de système autonome, etc. La RPKI est utilisée dans les mécanismes de sécurisation de BGP, par exemple pour vérifier qu'un AS est bien autorisé à être à l'origine d'un préfixe donné. Les règles initiales de la RPKI pour valider un certificat était trop strictes et ce RFC les assouplit légèrement (normalement, en gardant le même niveau de sécurité).

La RPKI est normalisée dans le RFC 6480. Elle prévoit notamment un système de certificats par lequel une autorité affirme qu'une autorité de niveau inférieur a bien reçu une délégation pour un groupe de ressources Internet. (On appelle ces ressources les INR, pour Internet Number Resource.) La validation des certificats est décrite dans le RFC 6487. L'ancienne règle était que le certificat de l'autorité inférieure ne devait lister que des ressources qui étaient dans le certificat de l'autorité qui signait (RFC 6487, section 7.2, notamment la condition 6 de la seconde énumération). En pratique, cette règle s'est avérée trop rigide, et la nouvelle, décrite dans notre RFC 8360 est que le certificat de l'autorité inférieure n'est accepté que pour des ressources qui étaient dans le certificat de l'autorité qui signait. S'il y a d'autres ressources, le certificat n'est plus invalide, simplement, ces ressources sont ignorées. Dit autrement, l'ensemble des ressources dont la « possession » est certifiée, est l'intersection des ressources du certificat de l'autorité parente et de celui de l'autorité fille. Cette intersection se nomme VRS, pour Verified Resource Set.

Voici d'abord une chaîne de certificats qui était valide avec l'ancienne règle, et qui l'est toujours :

Certificate 1 (trust anchor):
Issuer TA,
Subject TA,
Resources 192.0.2.0/24, 198.51.100.0/24,
      2001:db8::/32, AS64496-AS64500

Certificate 2:
Issuer TA,
Subject CA1,
Resources 192.0.2.0/24, 198.51.100.0/24, 2001:db8::/32

Certificate 3:
Issuer CA1,
Subject CA2,
Resources 192.0.2.0/24, 198.51.100.0/24, 2001:db8::/32

ROA 1:
Embedded Certificate 4 (EE certificate):
Issuer CA2,
Subject R1,
Resources 192.0.2.0/24
    

La chaîne part d'un certificat (TA, pour Trust Anchor, le point de départ de la validation). Elle aboutit à un ROA (Route Origin Authorization, cf. RFC 6482). Chaque certificat est valide puisque chacun certifie un jeu de ressources qui est un sous-ensemble du jeu de ressources de l'autorité du dessus. Idem pour le ROA.

Et voici par contre une chaîne de certificats qui était invalide selon les anciennes règles, et est désormais valide avec les règles de notre RFC 8360 :

Certificate 1 (trust anchor):
Issuer TA,
Subject TA,
Resources 192.0.2.0/24, 198.51.100.0/24,
     2001:db8::/32, AS64496-AS64500

Certificate 2:
Issuer TA,
Subject CA1,
Resources 192.0.2.0/24, 2001:db8::/32

Certificate 3 (invalid before, now valid):
Issuer CA1,
Subject CA2,
Resources 192.0.2.0/24, 198.51.100.0/24, 2001:db8::/32

ROA 1 (invalid before, now valid):
Embedded Certificate 4 (EE certificate, invalid before, now valid):
Issuer CA2,
Subject R1,
Resources 192.0.2.0/24

La chaîne était invalide car le troisième certificat ajoutait 198.51.100.0/24, qui n'était pas couvert par le certificat supérieur. Désormais, elle est valide mais vous noterez que seuls les préfixes 192.0.2.0/24 et 2001:db8::/32 sont couverts, comme précédemment (198.51.100.0/24 est ignoré). Avec les règles de notre nouveau RFC, le ROA final est désormais valide (il n'utilise pas 198.51.100.0/24). Notez que les nouvelles règles, comme les anciennes, n'autoriseront jamais l'acceptation d'un ROA pour des ressources dont l'émetteur du certificat n'est pas titulaire (c'est bien le but de la RPKI). Ce point est important car l'idée derrière ce nouveau RFC de rendre plus tolérante la validation n'est pas passée toute seule à l'IETF.

Un cas comme celui des deux chaînes ci-dessus est probablement rare. Mais il pourrait avoir des conséquences sérieuses si une ressource, par exemple un préfixe IP, était supprimée d'un préfixe situé très haut dans la hiérarchie : plein de certificats seraient alors invalidés avec les règles d'avant.

La section 4 de notre RFC détaille la procédure de validation. Les anciens certificats sont toujours validés avec l'ancienne politique, celle du RFC 6487. Pour avoir la nouvelle politique, il faut de nouveaux certificats, identifiés par un OID différent. La section 1.2 du RFC 6484 définissait id-cp-ipAddr-asNumber / 1.3.6.1.5.5.7.14.2 comme OID pour l'ancienne politique. La nouvelle politique est id-cp-ipAddr-asNumber-v2 / 1.3.6.1.5.5.7.14.3 et c'est elle qui doit être indiquée dans un certificat pour que lui soient appliquées les nouvelles règles. (Ces OID sont dans un registre IANA.) Comme les logiciels existants rejetteront les certificats avec ces nouveaux OID, il faut adapter les logiciels avant que les AC ne se mettent à utiliser les nouveaux OID (cf. section 6 du RFC). Les changements du RFC n'étant pas dans le protocole mais uniquement dans les règles de validation, le déploiement devrait être relativement facile.

Le cœur des nouvelles règles est dans la section 4.2.4.4 de notre RFC, qui remplace la section 7.2 du RFC 6487. Un nouveau concept est introduit, le VRS (Verified Resource Set). C'est l'intersection des ressources d'un certificat et de son certificat supérieur. Dans le deuxième exemple plus haut, le VRS du troisième certificat est {192.0.2.0/24, 2001:db8::/32}, intersection de {192.0.2.0/24, 2001:db8::/32} et {192.0.2.0/24, 198.51.100.0/24, 2001:db8::/32}. Si le VRS est vide, le certificat est invalide (mais on ne le rejette pas, cela peut être un cas temporaire puisque la RPKI n'est pas cohérente en permanence).

Un ROA est valide si les ressources qu'il contient sont un sous-ensemble du VRS du certificat qui l'a signé (si, rappelons-le, ce certificat déclare la nouvelle politique). La règle exacte est un peu plus compliquée, je vous laisse lire le RFC pour les détails. Notez qu'il y a également des règles pour les AS, pas seulement pour les préfixes IP.

La section 5 du RFC contient davantage d'exemples, illustrant les nouvelles règles.


Téléchargez le RFC 8360


L'article seul

RFC 8358: Update to Digital Signatures on Internet-Draft Documents

Date de publication du RFC : Mars 2018
Auteur(s) du RFC : R. Housley (Vigil Security)
Pour information
Première rédaction de cet article le 13 mars 2018
Dernière mise à jour le 5 avril 2018


Les Internet-Drafts sont signés suivant les règles du RFC 5485, afin qu'une lectrice ou un lecteur puissent vérifier qu'un Internet-Draft n'a pas été modifié en cours de route. Ce nouveau RFC modifie légèrement le RFC 5485 sur un seul point : la signature d'Internet-Drafts qui sont écrits en Unicode.

En effet, depuis le RFC 7997, les RFC ne sont plus forcément en ASCII, ils peuvent intégrer des caractères Unicode. Le premier RFC publié avec ces caractères a été le RFC 8187, en septembre 2017. Bientôt, cela sera également possible pour les Internet-Drafts. Cela affecte forcément les règles de signature, d'où cette légère mise à jour.

Le RFC 5485 normalisait l'utilisation de CMS (RFC 5652) pour le format des signatures. Vous pouvez télécharger ces signatures sur n'importe lequel des sites miroirs. CMS utilise ASN.1, avec l'obligation d'utiliser l'encodage DER, le seul encodage d'ASN.1 qui ne soit pas ambigu (une seule façon de représenter un texte).

Les Internet-Drafts sont actuellement tous en texte brut, limité à ASCII. Mais cela ne va pas durer (RFC 7990). Les signatures des Internet-Drafts sont détachées de l'Internet-Draft (section 2 de notre RFC), dans un fichier portant le même nom auquel on ajoute l'extension .p7s (RFC 5751). Par exemple avec wget, pour récupérer un Internet-Draft et sa signature :

% wget  https://www.ietf.org/id/draft-bortzmeyer-dname-root-05.txt
% wget  https://www.ietf.org/id/draft-bortzmeyer-dname-root-05.txt.p7s

(Ne le faites pas avec un Internet-Draft trop récent, les signatures n'apparaissent qu'au bout de quelques jours, la clé privée n'est pas en ligne.)

La signature est au format CMS (RFC 5652). Son adaptation aux RFC et Internet-Drafts est normalisée dans le RFC 5485. Le champ SignedData.SignerInfo.EncapsulatedContentInfo.eContentType du CMS identifie le type d'Internet-Draft signé. Les valeurs possibles figurent dans un registre IANA. Il y avait déjà des valeurs comme id-ct-asciiTextWithCRLF qui identifiait l'Internet-Draft classique en texte brut en ASCII, notre RFC ajoute (section 5) id-ct-utf8TextWithCRLF (texte brut en UTF-8), id-ct-htmlWithCRLF (HTML) et id-ct-epub (EPUB). Chacun de ces types a un OID, par exemple le texte brut en UTF-8 sera 1.2.840.113549.1.9.16.1.37.

Maintenant, passons à un morceau délicat, la canonicalisation des Internet-Drafts. Signer nécessite de canonicaliser, autrement, deux textes identiques aux yeux de la lectrice pourraient avoir des signatures différentes. Pour le texte brut en ASCII, le principal problème est celui des fins de ligne, qui peuvent être représentées différemment selon le système d'exploitation. Nous utilisons donc la canonicalisation traditionnelle des fichiers texte sur l'Internet, celle de FTP : le saut de ligne est représenté par deux caractères, CR et LF. Cette forme est souvent connue sous le nom de NVT (Network Virtual Terminal) mais, bien que très ancienne, n'avait été formellement décrite qu'en 2008, dans l'annexe B du RFC 5198, qui traitait pourtant un autre sujet.

Pour les Internet-Drafts au format XML, notre RFC renvoie simplement au W3C et à sa norme XML, section 2.11 de la cinquième édition, qui dit qu'il faut translating both the two-character sequence #xD #xA and any #xD that is not followed by #xA to a single #xA character. La canonicalisation XML (telle que faite par xmllint --c14n) n'est pas prévue.

Les autres formats ne subissent aucune opération particulière de canonicalisation. Un fichier EPUB, par exemple, est considéré comme une simple suite d'octets. On notera que le texte brut en Unicode ne subit pas de normalisation Unicode. C'est sans doute à cause du fait que le RFC 7997, dans sa section 4, considère que c'est hors-sujet. (Ce qui m'a toujours semblé une drôle d'idée, d'autant plus qu'il existe une norme Internet sur la canonicalisation du texte brut en Unicode, RFC 5198, qui impose la normalisation NFC.)

À l'heure actuelle, les Internet-Drafts sont signés, les outils doivent encore être adaptés aux nouvelles règles de ce RFC, mais elles sont simples et ça ne devrait pas être trop dur. Pour vérifier les signatures, la procédure (qui est documentée) consiste d'abord à installer le logiciel de canonicalisation :

% wget  https://www.ietf.org/id-info/canon.c    
% make canon
    

Puis à télécharger les certificats racine :

% wget  https://www.ietf.org/id-info/verifybundle.pem
    

Vous pouvez examiner ce groupe de certificats avec :

% openssl crl2pkcs7 -nocrl -certfile verifybundle.pem  | openssl pkcs7 -print_certs -text
    

Téléchargez ensuite des Internet-Drafts par exemple en https://www.ietf.org/id/ :

% wget      https://www.ietf.org/id/draft-ietf-isis-sr-yang-03.txt 
% wget         https://www.ietf.org/id/draft-ietf-isis-sr-yang-03.txt.p7s

On doit ensuite canonicaliser l'Internet-Draft :

% ./canon draft-ietf-isis-sr-yang-03.txt draft-ietf-isis-sr-yang-03.txt.canon

On peut alors vérifier les signatures :

% openssl cms -binary -verify -CAfile verifybundle.pem -content draft-ietf-isis-sr-yang-03.txt.canon -inform DER -in draft-ietf-isis-sr-yang-03.txt.p7s -out /dev/null
Verification successful

Si vous avez à la place :

Verification failure
139818719196416:error:2E09A09E:CMS routines:CMS_SignerInfo_verify_content:verification failure:../crypto/cms/cms_sd.c:819:
139818719196416:error:2E09D06D:CMS routines:CMS_verify:content verify error:../crypto/cms/cms_smime.c:393:
 

c'est sans doute que vous avez oublié l'option -binary.

Si vous trouvez la procédure compliquée, il y a un script qui automatise tout ça idsigcheck :

% ./idsigcheck --setup      
% ./idsigcheck draft-ietf-isis-sr-yang-03.txt
    

Si ça vous fait bad interpreter: /bin/bash^M, il faut recoder les sauts de ligne :

% dos2unix idsigcheck 
dos2unix: converting file idsigcheck to Unix format...
    

Ce script appelle OpenSSL mais pas avec les bonnes options à l'heure actuelle, vous risquez donc d'avoir la même erreur que ci-dessus.


Téléchargez le RFC 8358


L'article seul

RFC 8354: Use Cases for IPv6 Source Packet Routing in Networking (SPRING)

Date de publication du RFC : Mars 2018
Auteur(s) du RFC : J. Brzozowski, J. Leddy (Comcast), C. Filsfils, R. Maglione, M. Townsley (Cisco)
Pour information
Réalisé dans le cadre du groupe de travail IETF spring
Première rédaction de cet article le 29 mars 2018


Le sigle SPRING signifie « Source Packet Routing In NetworkinG ». C'est quoi, le routage par la source (source routing) ? Normalement, la transmission de paquets IP se fait uniquement en fonction de l'adresse de destination, chaque routeur sur le trajet prenant sa décision indépendemment des autres, et sans que l'émetteur original du paquet n'ait son mot à dire. L'idée du routage par la source est de permettre à cet émetteur d'indiquer par où il souhaite que son paquet passe. L'idée est ancienne, et resurgit de temps en temps sur l'Internet. Ce nouveau RFC décrit les cas où une solution de routage par la source serait utile.

L'idée date des débuts de l'Internet. Par exemple, la norme IPv4, le RFC 791, spécifie, dans sa section 3.1, deux mécanismes de routage par la source, « Loose Source Routing » et « Strict Source Routing ». Mais ces mécanismes sont peu déployés et vous n'avez guère de chance, si vous mettez ces options dans un paquet IP, de voir un effet. En effet, le routage par la source est dangereux, il permet des attaques variées, et il complique beaucoup le travail des routeurs. Le but du projet SPRING, dont c'est le deuxième RFC, est de faire mieux. Le cahier des charges du projet est dans le RFC 7855.

L'architecture technique de SPRING est dans un document pas encore publié, draft-ietf-spring-segment-routing. Ce segment routing est déjà mis en œuvre dans le noyau Linux depuis la version 4.14 (cf. le site du projet). Notre nouveau RFC 8354 ne contient, lui, que les scénarios d'usage. (Certains étaient déjà dans la section 3 du RFC 7855.) Seul IPv6 est pris en compte.

D'abord, le cas du SOHO connecté à plusieurs fournisseurs d'accès. Comme chacun de ces fournisseurs n'acceptera que des paquets dont l'adresse IP source est dans un préfixe qu'il a alloué au client, il est essentiel de pouvoir router en fonction de la source, afin d'envoyer les paquets ayant une adresse source du FAI A vers le FAI A et seulement celui-ci. Voici par exemple comment faire sur Linux, quand on veut envoyer les paquets ayant l'adresse IP source 2001:db8:dc2:45:216:3eff:fe4b:8c5b vers un routeur différent de l'habituel (le RFC 3178 peut être une bonne lecture, quoique daté, notamment sa section 5) :

#!/bin/sh

DEVICE=eth1
SERVICE=2001:db8:dc2:45:216:3eff:fe4b:8c5b
TABLE=CustomTable
ROUTER=2001:db8:dc2:45:1

echo 200 ${TABLE} >> /etc/iproute2/rt_tables
ip -6 rule add from ${SERVICE} table ${TABLE}
ip -6 route add default via ${ROUTER} dev ${DEVICE} table ${TABLE}
ip -6 route flush cache
    

Notez que la décision correcte peut être prise par la machine terminale, comme dans l'exemple ci-dessus, ou bien par un routeur situé plus loin sur le trajet (dans le projet SPRING, la source n'est pas forcément la machine terminale initiale).

Outre le fait que le FAI B rejetterait probablement les paquets ayant une adresse source qui n'est pas à lui (RFC 3704), il peut y avoir d'autres raisons pour envoyer les paquets sur une interface de sortie particulière :

  • Elle est plus rapide,
  • Elle est moins chère (pensez à un réseau connecté en filaire mais également à un réseau mobile avec forfait « illimité » ce qui, en langage telco, veut dire ayant des limites),
  • Dans le cas du télétravailleur, il peut être souhaitable de faire passer les paquets de la machine personnelle par un FAI payé par le télétravailleur et ceux de la machine de bureau par un FAI payé par l'employeur.

Autre cas où un routage par la source peut être utile, le FAI peut s'en servir pour servir certains utilisateurs ou certains usages dans des conditions différentes, par exemple avec des prix à la tête du client. Cela viole sans doute la neutralité du réseau mais c'est peut-être un scénario qui en tentera certains (sections 2.2 et 2.5 du RFC). Cela concerne le réseau d'accès (de M. Michu au FAI) et aussi le cœur de réseau ; « l'opérateur peut vouloir configurer un chemin spécial pour les applications sensibles à la latence ».

De plus haute technologie est le scénario présenté dans la section 2.3. Ici, il s'agit d'un centre de données entièrement IPv6. Cela simplifie considérablement la gestion du réseau, et cela permet d'avoir autant d'adresses qu'on veut, sans se soucier de la pénurie d'adresses IPv4. Certains opérateurs travaillent déjà à de telles configurations. Dans ce cas, le routage par la source serait un outil puissant pour, par exemple, isoler différents types de trafic et les acheminer sur des chemins spécifiques.

Si le routage par la source, une très vieille idée, n'a jamais vraiment pris, c'est en grande partie pour des raisons de sécurité. La section 4 rappelle les risques associés, qui avaient mené à l'abandon de la solution « Type 0 Routing Header » (cf. RFC 5095).


Téléchargez le RFC 8354


L'article seul

RFC 8352: Energy-Efficient Features of Internet of Things Protocols

Date de publication du RFC : Avril 2018
Auteur(s) du RFC : C. Gomez (UPC), M. Kovatsch (ETH Zurich), H. Tian (China Academy of Telecommunication Research), Z. Cao (Huawei Technologies)
Pour information
Réalisé dans le cadre du groupe de travail IETF lwig
Première rédaction de cet article le 7 avril 2018


Les objets contraints, engins connectés ayant peu de capacités (un capteur de température dans la nature, par exemple), ont en général une réserve d'énergie électrique très limitée, fournie par des piles ou batteries à la capacité réduite. L'usage des protocoles de communication traditionnels, souvent très bavards, épuiserait rapidement ces réserves. Ce RFC étudie les mécanismes utilisables pour limiter la consommation électrique, et faire ainsi durer ces objets plus longtemps.

La plupart des protocoles de couche 2 utilisés pour ces « objets contraints » disposent de fonctions pour diminuer la consommation électrique. Notre RFC décrit ces fonctions, et explique comment les protocoles de couches supérieures, comme IP peuvent en tirer profit. Si vous ne connaissez pas le monde des objets contraints, et les problèmes posés par leur connexion à l'Internet, je recommande fortement la lecture des RFC 6574, RFC 7228, et RFC 7102. Le but des techniques présentées dans ce RFC est bien de faire durer plus longtemps la batterie, ce n'est pas un but écologique. Si l'objet est alimenté en permanence (une télévision connectée, ou bien un grille-pain connecté), ce n'est pas considéré comme problème.

Des tas de travaux ont déjà eu lieu sur ce problème de la diminution de la consommation électrique de ces objets. Il y a eu moins d'efforts sur celle des protocoles réseau, et ce sont ces efforts que résume ce RFC. Les protocoles traditionnels étaient conçus pour des machines alimentées en permanence et pour qui la consommation électrique n'était pas un problème. Diffuser très fréquemment une information, même inutile, n'était pas perçu comme du gaspillage, contrairement à ce qui se passe avec l'Internet des Objets. (Voir le RFC 7772 pour une solution à un tel problème.)

L'IETF a déjà développé un certain nombre de protocoles qui sont spécifiques à cet « Internet des Objets ». Ce sont par exemple 6LoWPAN (RFC 6282, RFC 6775 et RFC 4944), ou bien le protocole de routage RPL (RFC 6550) ou encore l'alternative à HTTP CoAP (RFC 7252). En gros, l'application fait tourner du CoAP sur IPv6 qui est lui-même au-dessus de 6LoWPAN, couche d'adaptation d'IP à des protocoles comme IEEE 802.15.4. Mais l'angle « économiser l'énergie » n'était pas toujours clairement mis en avant, ce que corrige notre nouveau RFC.

Qu'est ce qui coûte cher à un engin connecté (cher en terme d'énergie, la ressource rare) ? En général, faire travailler le CPU est bien moins coûteux que de faire travailler la radio, ce qui justifie une compression énergique. Et, question réseau, la réception n'est pas forcément moins coûteuse que la transmission. L'étude Powertrace est une bonne source de mesures dans ce domaine mais on peut également lire « Measuring Power Consumption of CC2530 With Z-Stack ». Le RFC contient aussi (section 2) des chiffres typiques, mesurés sur Contiki et publiés dans « The ContikiMAC Radio Duty Cycling Protocol » :

  • Écouter pendant une seconde : 63 000 μJ,
  • Réception d'un paquet : 178 μJ pour un paquet diffusé et 222 μJ autrement,
  • Transmission d'un paquet : de 120 à 1 790 μJ selon les cas.

Une technique courante quand on veut réduire la consommation électrique lors de la transmission est de réduire la puissance d'émission. Mais cela réduit la portée, donc peut nécessiter d'introduire du routage, ce qui augmenterait la consommation.

On a vu que la simple écoute pouvait consommer beaucoup d'énergie. Il faut donc trouver des techniques pour qu'on puisse couper l'écoute (éteindre complètement la radio), tout en gardant la possibilité de parler à l'objet si nécessaire. Sans ce duty-cycling (couper la partie radio de l'objet, cf. RFC 7228, section 4.3), la batterie ne durerait que quelques jours, voire quelques heures, alors que certains objets doivent pouvoir fonctionner sans entretien pendant des années. Comment couper la réception tout en étant capable de recevoir des communications ? Il existe trois grandes techniques de RDC (Radio Duty-Cycling, section 3 de notre RFC) :

  • Échantillonage : on n'est pas en réception la plupart du temps mais on écoute le canal à intervalles réguliers. Les objets qui veulent me parler doivent donc émettre un signal pendant une durée plus longue que la période d'échantillonage.
  • Transmissions aux moments prévus. On annonce les moments où on est prêt à recevoir. Cela veut dire que les objets qui veulent parler avec moi doivent le faire au bon moment.
  • Écouter quand on transmet. Dans ce cas, ceux qui ont envie de me parler doivent attendre que j'émette d'abord.

Dans les trois cas, la latence va évidemment en souffrir. On doit faire un compromis entre réduire la consommation électrique et augmenter la latence. On va également diminuer la capacité du canal puisqu'il ne pourra pas être utilisé en permanence. Ce n'est pas forcément trop grave, un capteur a peu à transmettre, en général.

Il existe plein de méthodes pour gagner quelques μJ, comme le regroupement de plusieurs paquets en un seul (il y a un coût fixe par paquet, indépendamment de leur taille). Normalement, le RDC est quelque part dans la couche 2 et les protocoles IETF, situés plus haut, ne devraient pas avoir à s'en soucier. Mais une réduction sérieuse de la consommation électrique nécessite que tout le monde participe, et que les couches hautes fassent un effort, et connaissent ce que font les couches basses, pour s'adapter.

La section 3.6 de notre RFC détaille quelques services que fournissent les protocoles de couche 2 de la famille IEEE 802.11. Une station (un objet ou un ordinateur) peut indiquer au point d'accès (AP, pour access point) qu'il va s'endormir. L'AP peut alors mémoriser les trames qui lui étaient destinées, pour les envoyer plus tard. IEEE 802.11v va plus loin en incluant des mécanismes comme le proxy ARP (répondre aux requêtes ARP à la place de l'objet endormi).

Bluetooth a un ensemble de services pour la faible consommation, nommés Bluetooth LE (pour Low Energy). 6LoWPAN peut d'ailleurs en tirer profit (cf. RFC 7668).

IEEE 802.15.4 a aussi des solutions. Il permet par exemple d'avoir deux types de réseaux, avec ou sans annonces périodiques (beacons). Dans un réseau avec annonces, des machines nommés les coordinateurs envoient régulièrement des annonces qui indiquent quand on peut émettre et quand on ne peut pas, ces dernières périodes étant le bon moment pour s'endormir afin de diminuer la consommation : on est sûr de ne rien rater. Les durées de ces périodes sont configurables par l'administrateur réseaux. Dans les réseaux sans annonces, les différentes machines connectées n'ont pas d'informations et transmettent quand elles veulent, en suivant CSMA/CA.

Enfin, DECT a aussi un mode à basse consommation, DECT ULE. Elle est également utilisable avec 6LoWPAN (RFC 8105). DECT est très asymétrique, il y a le FP (Fixed Part, la base), et le PP (Portable Part, l'objet). DECT ULE permet au FP de prévenir quand il parlera (et le PP peut dormir jusque là). À noter que si la « sieste » est trop longue (plus de dix secondes), le PP devra refaire une séquence de synchronisation avec le FP, qui peut être coûteuse en énergie. Il faut donc bien calculer son coup : si on s'endort souvent pour des périodes d'un peu plus de dix secondes, le bilan global sera sans doute négatif.

La section 4 de notre RFC couvre justement ce que devrait faire IP, en supposant qu'on utilisera 6LoWPAN. Il y a trois services importants de 6LoWPAN pour la consommation électrique :

  • 6LoWPAN a un mécanisme de fragmentation et de réassemblage. IPv6 impose que les liens aient une MTU d'au moins 1 280 octets. Si ce n'est pas le cas, il faut un mécanisme de fragmentation sous IP. Mais la fragmentation n'est pas gratuite et il vaut mieux que les applications s'abstiennent de faire des paquets trop gros (un cas que traite CoAP, dans le RFC 7959).
  • 6LoWPAN sait que le processeur consomme moins d'énergie que la partie radio de l'objet connecté, et qu'il est donc rentable de faire des calculs qui diminuent la quantité de données à envoyer, notamment la compression. Par exemple, 6LoWPAN permet de diminuer sérieusement la taille de l'en-tête de paquet IPv6 (normalement 40 octets), en comptant sur le fait que les paquets successifs d'un même flot se ressemblent.
  • 6LoWPAN a un mécanisme de découverte du voisin optimisé pour les réseaux contraints, nommé 6LoWPAN-ND.

Il y a aussi des optimisations qui ne changent pas le protocole. Par exemple, Contiki ne suit pas, dans sa mise en œuvre, une stricte séparation des couches, afin de pouvoir optimiser l'activité réseau.

De même, les protocoles de routage doivent tenir compte des contraintes de consommation électrique (section 5 du RFC). Le protocole « officiel » de l'IETF pour le routage dans les réseaux contraints est RPL (RFC 6550). L'étude Powertrace déjà citée étudie également la consommation de RPL et montre qu'il est en effet assez efficace, si le réseau est stable (s'il ne l'est pas, on consommera évidemment du courant à envoyer et recevoir les mises à jours des routes). On peut adapter les paramètres de RPL, via l'algorithme Trickle (RFC 6206, rien à voir avec le protocole de transfert de fichiers de BitNet), pour le rendre encore plus économe, mais au prix d'une convergence plus lente lorsque le réseau change.

À noter que le RFC ne parle que de RPL, et pas des autres protocoles de routage utilisables par les objets, comme Babel (RFC 6126).

Et les applications ? La section 6 du RFC leur donne du boulot, à elles aussi. Bien sûr, il est recommandé d'utiliser CoAP (RFC 7252) plutôt que HTTP, entre autre en raison de son en-tête plus court, et de taille fixe. D'autre part, CoAP a un mode de pure observation, où le client indique son intérêt pour certaines ressources, que le serveur lui enverra automatiquement par la suite, lorsqu'elles changeront, économisant ainsi une requête. Comme HTTP, CoAP peut utiliser des relais qui mémorisent la ressource demandée, ce qui permet de récupérer des ressources sans réveiller le serveur, si l'information était dans le cache. D'autres protocoles étaient à l'étude, reprenant les principes de CoAP, pour mieux gérer les serveurs endormis. (Mais la plupart de ces projets semblent… endormis à l'heure actuelle.)

Enfin, la section 7 de notre RFC résume les points importants :

  • Il ne faut pas hésiter à violer le modèle en couches, en accédant à des informations des couches basses, elles peuvent sérieusement aider à faire des économies.
  • Il faut comprimer.
  • Il faut se méfier de la diffusion, qui consomme davantage.
  • Et il faut s'endormir souvent, pour économiser l'énergie (la méthode Gaston Lagaffe, même si le RFC ne cite pas ce pionnier).

Téléchargez le RFC 8352


L'article seul

RFC 8336: The ORIGIN HTTP/2 Frame

Date de publication du RFC : Mars 2018
Auteur(s) du RFC : M. Nottingham, E. Nygren (Akamai)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 22 mars 2018


Le concept d'origine est crucial pour la sécurité de HTTP. L'idée est d'empêcher du contenu actif (code JavaScript, par exemple) d'interagir avec des serveurs autres que ceux de l'origine, de l'endroit où on a chargé ce contenu actif. Dans la version 1 de HTTP, cela ne posait pas (trop) de problèmes. Mais la version 2 de HTTP permet d'avoir, dans une même connexion HTTP vers un serveur donné, accès à des ressources d'origines différentes (par exemple parce qu'hébergées sur des Virtual Hosts différents). Ce nouveau RFC ajoute donc au protocole HTTP/2 un nouveau type de trame, ORIGIN, qui permet de spécifier les origines utilisées dans une connexion.

L'origine est un concept ancien, mais sa description formelle n'est venue au'avec le RFC 6454, dont la lecture est fortement recommandée, avant de lire ce nouveau RFC 8336. Son application à HTTP/2, normalisé dans le RFC 7540, a posé quelques problèmes (sections 9.1.1 et 9.1.2 du RFC 7540). En effet, avec HTTP/2, des origines différentes peuvent coexister sur la même connexion HTTP. Si le serveur ne peut pas produire une réponse, par exemple parce qu'il sépare le traitement des requêtes entre des machines différentes, il va envoyer un code de retour 421, indiquant à un client HTTP de re-tenter, avec une connexion différente. Pour lui faire gagner du temps, notre nouveau RFC 8336 va indiquer préalablement les origines acceptables sur cette connexion. Le client n'aura donc pas à essayer, il saura d'avance si ça marchera ou pas. Cette méthode évite également au client HTTP de se faire des nœuds au cerveau pour déterminer si une requête pour une origine différente a des chances de réussir ou pas, processus compliqué, et qui ne marche pas toujours.

Ce n'est pas clair ? Voici un exemple concret. Le client, un navigateur Web, vient de démarrer et on lui demande de se connecter à https://www.toto.example/. Il établit une connexion TCP, puis lance TLS, et enfin fait du HTTP/2. Dans la phase d'établissement de la connexion TLS, il a récupéré un certificat qui liste des noms possibles (subjectAltName), www.toto.example mais aussi foobar.example. Et, justement, quelques secondes plus tard, l'utilisateur demande à visiter https://foobar.example/ToU/TLDR/. Un point central de HTTP/2 est la réutilisation des connexions, pour diminuer la latence, due entre autres à l'établissement de connexion, qui peut être long avec TCP et, surtout TLS. Notre navigateur va donc se dire « chic, je garde la même connexion puisque c'est la même adresse IP et que ce serveur m'a dit qu'il gérait aussi foobar.example, c'était dans son certificat » (et la section 9.1.1 du RFC 7540 le lui permet explicitement). Mais patatras, l'adresse IP est en fait celle d'un répartiteur de charge qui envoie les requêtes pour www.toto.example et foobar.example à des machines différentes. La machine qui gère foobar.example va alors renvoyer 421 Misdirected Request au navigateur qui sera fort marri, et aura perdu du temps pour rien. Alors qu'avec la trame ORIGIN de notre RFC 8336, le serveur de www.toto.example aurait dès le début envoyé une trame ORIGIN disant « sur cette connexion, c'est www.toto.example et rien d'autre ». Le navigateur aurait alors été prévenu.

La section 2 du RFC décrit en détail ce nouveau type de trame (RFC 7540, section 4, pour le concept de trame). Le type de la trame est 12 (cf. le registre des types), et elle contient une liste d'origines, chacune sous forme d'un doublet longueur-valeur. Une origine est identifiée par un nom de domaine (RFC 6454, sections 3 et 8). Il n'y a pas de limite de taille à la liste, programmeurs, faites attention aux débordements de tableau. Un nom de la liste ne peut pas inclure de jokers (donc, pas d'origine *.example.com, donc attention si vous avez des certificats utilisant des jokers). Ce type de trames doit être envoyée sur le ruisseau HTTP/2 de numéro 0 (celui de contrôle).

Comme toutes les trames d'un type inconnu du récepteur, elles sont ignorées par le destinataire. Donc, en pratique, le serveur peut envoyer ces trames sans inquiétude, le client HTTP trop vieux pour les connaitre les ignorera. Ces trames ORIGIN n'ont de sens qu'en cas de liaison directe, les relais doivent les ignorer, et ne pas les transmettre.

Au démarrage, le client HTTP/2 a un jeu d'origines qui est déterminé par les anciennes règles (section 9.1.1 du RFC 7540). S'il reçoit une trame ORIGIN, celle-ci remplace complètement ce jeu, sauf pour la première origine vue (le serveur auquel on s'est connecté, identifié par son adresse IP et, si on utilise HTTPS, par le nom indiqué dans l'extension TLS SNI, cf. RFC 6066) qui, elle, reste toujours en place. Ensuite, les éventuelles réponses 421 (Misdirected request) supprimeront des entrées du jeu d'origines.

Notez bien que la trame ORIGIN ne fait qu'indiquer qu'on peut utiliser cette connexion HTTP/2 pour cette origine. Elle n'authentifie pas le serveur. Pour cela, il faut toujours compter sur le certificat (cf. section 4 du RFC).

En parlant de sécurité, notez que le RFC 7540, section 9.1.1 obligeait le client HTTP/2 à vérifier le DNS et le nom dans le certificat, avant d'ajouter une origine. Notre nouveau RFC est plus laxiste, on ne vérifie que le certificat quand on reçoit une nouvelle origine dans une trame ORIGIN envoyée sur HTTPS (cela avait suscité des réactions diverses lors de la discussion à l'IETF). Cela veut dire qu'un méchant qui a pu avoir un certificat valable pour un nom, via une des nombreuses AC du magasin, n'a plus besoin de faire une attaque de l'Homme du Milieu (avec, par exemple, un détournement DNS). Il lui suffit, lorsqu'un autre nom qu'il contrôle est visité, d'envoyer une trame ORIGIN et de présenter le certificat. Pour éviter cela, le RFC conseille au client de vérifier le certificat plus soigneusement, par exemple avec les journaux publics du RFC 6962, ou bien avec une réponse OCSP (RFC 6960 montrant que le certificat n'a pas été révoqué, en espérant qu'un certificat « pirate » sera détecté et révoqué…)

Les développeurs regarderont avec intérêt l'annexe B, qui donne des conseils pratiques. Par exemple, si un serveur a une très longue liste d'origines possibles, il n'est pas forcément bon de l'envoyer dès le début de la connexion, au moment où la latence est critique. Il vaut mieux envoyer une liste réduite, et attendre un moment où la connexion est tranquille pour envoyer la liste complète. (La liste des origines, dans une trame ORIGIN, ne s'ajoute pas aux origines précédentes, elle les remplace. Pour retirer une origine, on envoie une nouvelle liste, sans cette origine, ou bien on compte sur les 421. Ce point avait suscité beaucoup de discussions au sein du groupe de travail.)

Pour l'instant, la gestion de ce nouveau type de trames ne semble se trouver que dans Firefox, et n'est dans aucun serveur, mais des programmeurs ont annoncé qu'ils allaient s'y mettre.


Téléchargez le RFC 8336


L'article seul

RFC 8335: PROBE: A Utility For Probing Interfaces

Date de publication du RFC : Février 2018
Auteur(s) du RFC : R. Bonica (Juniper), R. Thomas (Juniper), J. Linkova (Google), C. Lenart (Verizon), M. Boucadair (Orange)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF intarea
Première rédaction de cet article le 23 février 2018


Pour tester qu'une machine est bien joignable, vous utilisez ping ou, plus rigoureusement, vous envoyez un message ICMP de type echo, auquel la machine visée va répondre avec un message ICMP echo reply. Ce test convient souvent mais il a plusieurs limites. L'une des limites de ce test est qu'il ne teste qu'une seule interface réseau de la machine, celle par laquelle vous lui parlez (deux interfaces, dans certains cas de routage asymétrique). Si la machine visée est un gros routeur avec plein d'interfaces réseau, le test ne vous dira pas si toutes fonctionnent. D'où cette extension aux messages ICMP permettant de spécifier l'interface qu'on veut vérifier.

A priori, ce RFC ne s'intéresse qu'aux routeurs, les serveurs n'ayant souvent qu'une seule interface réseau. La nouvelle technique, nommée PROBE, n'a pas de vocation générale, contrairement à ping, et concernera surtout les administrateurs réseau. D'autant plus que, comme elle est assez indiscrète, elle ne sera a priori pas ouverte au public. Notez qu'elle permet non seulement de tester une autre interface du routeur, mais également une interface d'une machine directement connectée au routeur. Les scénarios d'usage proposés sont exposés dans la section 5, une liste non limitative de cas où ping ne suffit pas :

  • Interface réseau non numérotée (pas d'adresse, ce qui est relativement courant sur les routeurs),
  • Interface réseau numérotée de manière purement locale (par exemple adresse IPv6 link-local),
  • Absence de route vers l'interface testée (si on veut tester l'interface d'un routeur qui fait face à un point d'échange, et que le préfixe du point d'échange n'est pas annoncé par le protocole de routage, ce qui est fréquent).

En théorie, SNMP pourrait servir au moins partiellement à ces tests mais, en pratique, c'est compliqué.

ping, la technique classique, est très sommairement décrit dans le RFC 2151, section 3.2, mais sans indiquer comment il fonctionne. La méthodologie est simple : la machine de test envoie un message ICMP Echo (type 8 en IPv4 et 128 en IPv6) à la machine visée (l'amer). L'amer répond avec un Echo Reply (type 0 en IPv4 et 129 en IPv6). La réception de cet Echo Reply indique que la liaison marche bien dans les deux sens. La non-réception indique d'un problème s'est produit, mais on n'en sait pas plus (notamment, on ne sait pas si le problème était à l'aller ou bien au retour). Ici, on voit le test effectué par une sonde Atlas sur l'amer 2605:4500:2:245b::42 (l'un des serveurs hébergeant ce blog), vu par tshark :

13.013422 2a02:1811:c13:1902:1ad6:c7ff:fe2a:6ac → 2605:4500:2:245b::42 ICMPv6 126 Echo (ping) request id=0x0545, seq=1, hop limit=56
13.013500 2605:4500:2:245b::42 → 2a02:1811:c13:1902:1ad6:c7ff:fe2a:6ac ICMPv6 126 Echo (ping) reply id=0x0545, seq=1, hop limit=64 (request in 11)
    

ICMP est normalisé dans les RFC 792 pour IPv4 et RFC 4443 pour IPv6. L'exemple ci-dessus montre un test classique, avec une requête et une réponse.

Notre RFC parle d'« interface testée » (probed interface) et d'« interface testante » (probing interface). Dans l'exemple ci-dessus, l'interface Ethernet de l'Atlas était la testante et celle du serveur était la testée. Le succès du test montre que les deux interfaces sont actives et peuvent se parler.

Au contraire de ping, PROBE va envoyer le message, non pas à l'interface testée mais à une interface « relais » (proxy). Celle-ci répondra si l'interface testée fonctionne bien (état oper-status, cf. RFC 7223). Si l'interface testée n'est pas sur le nœud qui sert de relais, ce dernier détermine l'état de cette interface en regardant la table ARP (RFC 826) ou NDP (RFC 4861). Aucun test actif n'est effectué, l'interface est considérée comme active si on lui a parlé récemment (et donc si l'adresse IP est dans un cache). PROBE utilise, comme ping, ICMP. Il se sert des messages ICMP structurés du RFC 4884. Une des parties du message structuré sert à identifier l'interface testée.

L'extension à ICMP Extended Echo est décrite en section 2 du RFC. Le type de la requête est 42 pour IPv4 et 160 pour IPv6 (enregistré à l'IANA, pour IPv4 et IPv6). Parmi les champs qu'elle comprend (les deux premiers existent aussi pour l'ICMP Echo traditionnel) :

  • Un identificateur qui peut servir à faire correspondre une réponse à une requête (ICMP, comme IP, n'a pas de connexion ou de session, chaque paquet est indépendant), 0x0545 dans l'exemple vu plus haut avec tshark,
  • Un numéro de séquence, qui peut indiquer les paquets successifs d'un même test, 1 dans l'exemple vu plus haut avec tshark
  • Un bit nommé L (local) qui indique si l'interface testée est sur le nœud visé par le test ou non,
  • Une structure qui indique l'interface testée.

Cette structure suit la forme décrite dans la section 7 du RFC 4884. Elle contient un objet d'identification de l'interface. L'interface qu'on teste peut être désignée par son adresse IP (si elle n'est pas locale - bit L à zéro, c'est la seule méthode acceptée), son nom ou son index. Notez que l'adresse IP identifiant l'adresse testée n'est pas forcément de la même famille que celle du message ICMP. On peut envoyer en IPv4 un message ICMP demandant à la machine distante de tester une interface IPv6.

Plus précisément, l'objet d'identification de l'interface est composé, comme tous les objets du RFC 4884, d'un en-tête et d'une charge utile. L'en-tête contient les champs :

  • Un numéro de classe, 3, stocké à l'IANA,
  • Un numéro indiquant comment l'interface testée est désignée (1 = par nom, cf. RFC 7223, 2 = par index, le même qu'en SNMP, voir aussi le RFC 7223, section 3, le concept de if-index, et enfin 3 = par adresse),
  • La longueur des données.

L'adresse est représentée elle-même par une structure à trois champs, la famille (4 pour IPv4 et 6 pour IPv6), la longueur et la valeur de l'adresse. Notez que le RFC 5837 a un mécanisme de description de l'interface, portant le numéro de classe 2, et utilisé dans un contexte proche.

La réponse à ces requêtes a le type 43 en IPv4 et 161 en IPv6 (section 3 du RFC). Elle comprend :

  • Un code (il valait toujours 0 pour la requête) qui indique le résultat du test : 0 est un succès, 1 signale que la requête était malformée, 2 que l'interface à tester n'existe pas, 3 qu'il n'y a pas de telle entrée dans la table des interfaces, et 4 que plusieurs interfaces correspondent à la demande (liste complète dans un registre IANA),
  • Un identificateur, copié depuis la requête,
  • Un numéro de séquence, copié depuis la requête,
  • Un état qui donne des détails en cas de code 0 (autrement, pas besoin de détails) : si l'interface testée n'est pas locale, l'état vaut 2 si l'entrée dans le cache ARP ou NDP est active, 1 si elle est incomplète (ce qui indique typiquement que l'interface testée n'a pas répondu), 3 si elle n'est plus à jour, etc,
  • Un bit A (active) qui est mis à un si l'interface testée est locale et fonctionne,
  • Un bit nommé 4 (pour IPv4) qui indique si IPv4 tourne sur l'interface testée,
  • Un bit nommé 6 (pour IPv6) qui indique si IPv6 tourne sur l'interface testée.

La section 4 du RFC détaille le traitement que doit faire la machine qui reçoit l'ICMP Extended Echo. D'abord, elle doit jeter le paquet (sans répondre) si ICMP Extended Echo n'est pas explicitement activé (rappelez-vous que ce service est assez indiscret, cf. section 8 du RFC) ou bien si l'adresse IP de la machine testante n'est pas autorisée (même remarque). Si les tests sont passés et que la requête est acceptée, la machine réceptrice fabrique une réponse : le code est mis à 1 si la requête est anormale (pas de partie structurée par exemple), 2 si l'interface testée n'existe pas, 3 si elle n'est pas locale et n'apparait pas dans les tables (caches) ARP ou NDP. Si on trouve l'interface, on la teste et on remplit les bits A, 4, 6 et l'état, en fonction de ce qu'on trouve sur l'interface testée.

Reste la question de la sécurité (section 8 du RFC). Comme beaucoup de mécanismes, PROBE peut être utilisé pour le bien (l'administrateur réseaux qui détermine l'état d'une interface d'un routeur dont il s'occupe), mais aussi pour le mal (chercher à récolter des informations sur un réseau avant une attaque, par exemple, d'autant plus que les noms d'interfaces dans les routeurs peuvent être assez parlants, révélant le type de réseau, le modèle de routeur…) Le RFC exige donc que le mécanisme ICMP Extended Echo ne soit pas activé par défaut, et soit configurable (liste blanche d'adresses IP autorisées, permission - ou non - de tester des interfaces non locales, protection des différents réseaux les uns contre les autres, si on y accueille des clients différents…) Et, bien sûr, il faut pouvoir limiter le nombre de messages.

Ne comptez pas utilise PROBE tout de suite. Il n'existe apparemment pas de mise en œuvre de ce mécanisme publiée. Juniper en a réalisé une mais elle n'apparait encore dans aucune version de JunOS.


Téléchargez le RFC 8335


L'article seul

RFC 8334: Launch Phase Mapping for the Extensible Provisioning Protocol (EPP)

Date de publication du RFC : Mars 2018
Auteur(s) du RFC : J. Gould (VeriSign), W. Tan (Cloud Registry), G. Brown (CentralNic)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 5 mars 2018


Les registres de noms de domaine ont parfois des périodes d'enregistrement spéciales, par exemple lors de la phase de lancement d'un nouveau domaine d'enregistrement, ou bien lorsque les règles d'enregistrement changent. Pendant ces périodes, les conditions d'enregistrement ne sont pas les mêmes que pendant les périodes « standards ». Les registres qui utilisent le protocole EPP pour l'enregistrement peuvent alors utiliser les extensions EPP de ce nouveau RFC pour gérer ces périodes spéciales.

Un exemple de période spéciale est l'ouverture d'un tout nouveau TLD à l'enregistrement. Un autre exemple est une libéralisation de l'enregistrement, passant par exemple de vérifications a priori strictes à un modèle plus ouvert. Dans les deux cas, on peut voir des conflits se faire jour, par exemple entre le titulaire le plus rapide à enregistrer un nom, et un détenteur de propriété intellectuelle qui voudrait reprendre le nom. Les périodes spéciales sont donc définies par des privilèges particuliers pour certains utilisateurs, permettant par exemple aux titulaires d'une marque déposée d'avoir un avantage pour le nom de domaine correspondant à cette marque. La période spéciale est qualifiée de « phase de lancement » (launch phase). Les extensions à EPP décrites dans ce nouveau RFC permettent de mettre en œuvre ces privilèges.

La classe (mapping) décrivant les domaines en EPP figure dans le RFC 5731. Elle est prévue pour le fonctionnement standard du registre, sans intégrer les périodes spéciales. Par exemple, en fonctionnement standard, une fois que quelqu'un a enregistré un nom, c'est fini, personne d'autre ne peut le faire. Mais dans les phases de lancement, il arrive qu'on accepte plusieurs candidatures pour un même nom, qui sera ensuite attribué en fonction de divers critères (y compris parfois une mise aux enchères). Ou bien il peut y avoir des vérifications supplémentaires pendant une phase de lancement. Par exemple, certaines phases peuvent être réservées aux titulaires de propriété intellectuelle, et cela est vérifié via un organisme de validation, comme la TMCH (RFC 7848).

D'où ce RFC qui étend la classe domain du RFC 5731. La section 2 du RFC décrit les nouveaux attributs et éléments des domaines, la section 3 la façon de les utiliser dans les commandes EPP et la section 4 donne le schéma XML. Voyons d'abord les nouveaux éléments et attributs.

D'abord, comme il peut y avoir plusieurs candidatures pour un même nom, il faut un moyen de les distinguer. C'est le but de l'identificateur de candidature (application identifier). Lorsque le serveur EPP reçoit une commande <domain:create> pour un nom, il attribue un identificateur de candidature, qu'il renvoie au client, dans un élément <launch:applicationID>, tout en indiquant que le domaine est en état pendingCreate (RFC 5731, section 2.3) puisque le domaine n'a pas encore été créé. Au passage, launch dans <launch:applicationID> est une abréviation pour l'espace de noms XML urn:ietf:params:xml:ns:launch-1.0. Un processeur XML correct ne doit évidemment pas tenir compte de l'abréviation (qui peut être ce qu'on veut) mais uniquement de l'espace de noms associé. Cet espace est désormais enregistré à l'IANA (cf. RFC 3688).

Autre nouveauté, comme un serveur peut utiliser plusieurs organismes de validation d'une marque déposée, il existe désormais un attribut validatorID qui indique l'organisme. Par défaut, c'est la TMCH (identificateur tmch). On pourra utiliser cet attribut lorsqu'on indiquera un identificateur de marque, par exemple lorsqu'on se sert de l'élément <mark:mark> du RFC 7848.

Les périodes spéciales ont souvent plusieurs phases, et notre RFC en définit plusieurs (dans une ouverture réelle, toutes ne sont pas forcément utilisées), qui seront utilisées dans l'élément <launch:phase> :

  • Lever de soleil (sunrise), phase où les titulaires de marques (le RFC ne mentionne pas d'autres cas, comme le nom de l'entreprise ou d'une ville) peuvent seuls soumettre des candidatures, la marque étant validée par exemple via la TMCH,
  • Prétentions (claims), où on peut enregistrer si on n'a pas de marque, mais on reçoit alors une notice disant qu'une marque similaire existe (elle est décrite plus en détail dans l'Internet-Draft draft-ietf-regext-tmch-func-spec), et on peut alors renoncer ou continuer (si on est d'humeur à affronter les avocats de la propriété intellectuelle), en annonçant, si on continue « oui, j'ai vu, j'y vais quand même »,
  • Ruée (landrush), phase immédiatement après l'ouverture, quand tout le monde et son chien peuvent se précipiter pour enregistrer,
  • État ouvert (open), une fois qu'on a atteint le régime de croisière.

La section 2 définit aussi les états d'une candidature. Notamment :

  • pendingValidation (validation en attente),
  • validated (c'est bon, mais voyez plus loin),
  • invalid (raté, vous n'avez pas de droits sur ce nom),
  • pendingAllocation (une fois qu'on est validé, tout n'est pas fini, il peut y avoir plusieurs candidatures, avec un mécanisme de sélection, par exemple fondé sur une enchère),
  • allocated (c'est vraiment bon),
  • rejected (c'est fichu…)

Les changements d'état ne sont pas forcément synchrones. Parfois, il faut attendre une validation manuelle, par exemple. Dans cas, il faut notifier le client EPP, ce qui se fait avec le mécanisme des messages asynchrones (poll message) du RFC 5730, section 2.9.2.3.

Comme toutes les extensions EPP, elle n'est utilisée par le client que si le serveur l'indique à l'ouverture de la session, en listant les espaces de noms XML des extensions qu'il accepte, par exemple :


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
   <epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
      <greeting><svID>EPP beautiful server for .example</svID>
                <svDate>2018-02-20T15:37:20.0Z</svDate>
                <svcMenu><version>1.0</version><lang>en</lang>
                <objURI>urn:ietf:params:xml:ns:domain-1.0</objURI>
                <objURI>urn:ietf:params:xml:ns:contact-1.0</objURI>
                <svcExtension>
                <extURI>urn:ietf:params:xml:ns:rgp-1.0</extURI>
                <extURI>urn:ietf:params:xml:ns:secDNS-1.1</extURI>
                <extURI>urn:ietf:params:xml:ns:launch-1.0</extURI>
                </svcExtension>
                </svcMenu>
        </greeting>
   </epp>

    

Maintenant qu'on a défini les données, la section 3 du RFC explique comment les utiliser. (Dans tous les exemples ci-dessous, C: identifie ce qui est envoyé par le client EPP et S: ce que le serveur répond.) Par exemple, la commande EPP <check> (RFC 5730, section 2.9.2.1) sert à vérifier si on peut enregistrer un objet (ici, un nom de domaine). Elle prend ici des éléments supplémentaires, par exemple pour tester si un nom correspond à une marque. Ici, on demande si une marque existe (notez l'extension <launch:check>) :


C:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
C:  <command>
C:   <check>
C:    <domain:check
C:     xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
C:      <domain:name>domain1.example</domain:name>
C:    </domain:check>
C:   </check>
C:   <extension>
C:    <launch:check
C:     xmlns:launch="urn:ietf:params:xml:ns:launch-1.0"
C:     type="trademark"/>
C:   </extension>
C:  </command>
C:</epp>

    

Et on a la réponse (oui, la marque existe dans la TMCH) :


S:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
S:  <response>
S:    <result code="1000">
S:     <msg>Command completed successfully</msg>
S:    </result>
S:    <extension>
S:     <launch:chkData
S:      xmlns:launch="urn:ietf:params:xml:ns:launch-1.0">
S:      <launch:cd>
S:        <launch:name exists="1">domain1.example</launch:name>
S:        <launch:claimKey validatorID="tmch">
S:        2013041500/2/6/9/rJ1NrDO92vDsAzf7EQzgjX4R0000000001
S:        </launch:claimKey>
S:      </launch:cd>
S:     </launch:chkData>
S:    </extension>
S:  </response>
S:</epp>

    

Avec la commande EPP <info>, qui sert à récupérer des informations sur un nom, on voit ici qu'un nom est en attente (pendingCreate), et on a l'affichage de la phase actuelle du lancement, dans l'élément <launch:phase> :


C:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
C:  <command>
C:   <info>
C:    <domain:info
C:     xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
C:      <domain:name>domain.example</domain:name>
C:    </domain:info>
C:   </info>
C:   <extension>
C:    <launch:info
C:     xmlns:launch="urn:ietf:params:xml:ns:launch-1.0"
C:       includeMark="true">
C:      <launch:phase>sunrise</launch:phase>
C:    </launch:info>
C:   </extension>
C:  </command>
C:</epp>
    

Et le résultat, avec entre autre l'identificateur de candidature :


S:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
S:  <response>
S:    <result code="1000">
S:      <msg>Command completed successfully</msg>
S:    </result>
S:    <resData>
S:      <domain:infData
S:       xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
S:        <domain:name>domain.example</domain:name>
S:        <domain:status s="pendingCreate"/>
S:        <domain:registrant>jd1234</domain:registrant>
S:        <domain:contact type="admin">sh8013</domain:contact>
S:        <domain:crDate>2012-04-03T22:00:00.0Z</domain:crDate>
   ...
S:      </domain:infData>
S:    </resData>
S:    <extension>
S:      <launch:infData
S:       xmlns:launch="urn:ietf:params:xml:ns:launch-1.0">
S:        <launch:phase>sunrise</launch:phase>
S:          <launch:applicationID>abc123</launch:applicationID>
S:          <launch:status s="pendingValidation"/>
S:          <mark:mark
S:            xmlns:mark="urn:ietf:params:xml:ns:mark-1.0">
S:             ...
S:         </mark:mark>
S:      </launch:infData>
S:    </extension>
S:  </response>
S:</epp>

    

C'est bien joli d'avoir des informations mais, maintenant, on voudrait créer des noms de domaine. La commande EPP <create> (RFC 5730, section 2.9.3.1) sert à cela. Selon la phase de lancement, il faut lui passer des extensions différentes. Pendant le lever de soleil (sunrise), il faut indiquer la marque déposée sur laquelle on s'appuie, dans <launch:codeMark> (il y a d'autres moyens de l'indiquer, cf. section 2.6) :


C:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
C:  <command>
C:    <create>
C:      <domain:create
C:       xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
C:        <domain:name>domain.example</domain:name>
C:        <domain:registrant>jd1234</domain:registrant>
   ...
C:      </domain:create>
C:    </create>
C:    <extension>
C:      <launch:create
C:       xmlns:launch="urn:ietf:params:xml:ns:launch-1.0">
C:        <launch:phase>sunrise</launch:phase>
C:        <launch:codeMark>
C:          <launch:code validatorID="sample1">
C:            49FD46E6C4B45C55D4AC</launch:code>
C:        </launch:codeMark>
C:      </launch:create>
C:    </extension>
C:  </command>
C:</epp>

    

On reçoit une réponse qui dit que le domaine n'est pas encore créé, mais on a un identificateur de candidature (un numéro de ticket, quoi) en <launch:applicationID>. Notez le code de retour 1001 (j'ai compris mais je ne vais pas le faire tout de suite) et non pas 1000, comme ce serait le cas en régime de croisière :


S:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
S:  <response>
S:    <result code="1001">
S:      <msg>Command completed successfully; action pending</msg>
S:    </result>
S:    <resData>
S:      <domain:creData
S:         xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
S:       <domain:name>domain.example</domain:name>
S:       <domain:crDate>2010-08-10T15:38:26.623854Z</domain:crDate>
S:      </domain:creData>
S:    </resData>
S:    <extension>
S:      <launch:creData
S:        xmlns:launch="urn:ietf:params:xml:ns:launch-1.0">
S:        <launch:phase>sunrise</launch:phase>
S:        <launch:applicationID>2393-9323-E08C-03B1
S:        </launch:applicationID>
S:      </launch:creData>
S:    </extension>
S:  </response>
S:</epp>

    

De même, des extensions permettent de créer un domaine pendant la phase où il faut indiquer qu'on a vu les prétentions qu'avait un titulaire de marque sur ce nom. Le RFC décrit aussi l'extension à utiliser dans la phase de ruée (landrush), mais j'avoue n'avoir pas compris son usage (puisque, pendant la ruée, les règles habituelles s'appliquent).

On peut également retirer une candidature, avec la commande EPP <delete> qui, en mode standard, sert à supprimer un domaine. Il faut alors indiquer l'identifiant de la candidature qu'on retire :


C:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
C:  <command>
C:   <delete>
C:    <domain:delete
C:     xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
C:      <domain:name>domain.example</domain:name>
C:    </domain:delete>
C:   </delete>
C:   <extension>
C:    <launch:delete
C:     xmlns:launch="urn:ietf:params:xml:ns:launch-1.0">
C:      <launch:phase>sunrise</launch:phase>
C:      <launch:applicationID>abc123</launch:applicationID>
C:    </launch:delete>
C:   </extension>
C:  </command>
C:</epp>

    

Et les messages non sollicités (poll), envoyés de manière asynchrone par le serveur ? Voici un exemple, où le serveur indique que la candidature a été jugée valide (le mécanisme par lequel on passe d'un état à un autre dépend de la politique du serveur) :


S:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
S:  <response>
S:    <result code="1301">
S:      <msg>Command completed successfully; ack to dequeue</msg>
S:    </result>
S:    <msgQ count="5" id="12345">
S:      <qDate>2013-04-04T22:01:00.0Z</qDate>
S:      <msg>Application pendingAllocation.</msg>
S:    </msgQ>
S:    <resData>
S:      <domain:infData
S:       xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
S:        <domain:name>domain.example</domain:name>
S:        ...
S:      </domain:infData>
S:    </resData>
S:    <extension>
S:      <launch:infData
S:       xmlns:launch="urn:ietf:params:xml:ns:launch-1.0">
S:        <launch:phase>sunrise</launch:phase>
S:        <launch:applicationID>abc123</launch:applicationID>
S:        <launch:status s="pendingAllocation"/>
S:      </launch:infData>
S:    </extension>
S:  </response>
S:</epp>

    

Voilà, vous savez l'essentiel, si vous voulez tous les détails, il faudra lire la section 3 complète, ainsi que la section 4, qui contient le schéma XML des extensions pour les phases de lancement. Comme toutes les extensions à EPP, celle de ce RFC est désormais dans le registre des extensions EPP, décrit dans le RFC 7451.

Notez que ce RFC ne fournit pas de moyen pour indiquer au client EPP quelle est la politique d'enregistrement pendant la période spéciale. Cela doit être fait par un mécanisme externe (page Web du registre, par exemple).

Quelles sont les mises en œuvre de ce RFC ? L'extension pour les phases de lancement est ancienne (première description en 2011) et de nombreux registres offrent désormais cette possibilité. C'est d'autant plus vrai que l'ICANN impose aux registres de ses nouveaux TLD de gérer les phases de lancement avec cette extension. Ainsi :

  • Le kit de développement de clients EPP de Verisign a cette extension, sous une licence libre.
  • Logiquement, le système d'enregistrement de Verisign, utilisé notamment pour .com et .net (mais également pour bien d'autres TLD) gère cette extension (code non libre et non public, cette fois).
  • Le serveur REngin (non libre), utilisé pour .za a aussi cette extension.
  • Le serveur de CentralNIC, pareil.
  • Le client EPP distribué par Neustar est sous licence libre et sait gérer les phases de lancement.
  • Le serveur (non libre) de SIDN (qui gère le domaine national .nl) fait partie de ceux qui ont mis en œuvre ce RFC.
  • Et côté client, il y a le logiciel libre Net::DRI (extension ajoutée dans une scission nommée tdw, pas dans le logiciel originel de Patrick Mevzek), cf. LaunchPhase.pm.

Téléchargez le RFC 8334


L'article seul

RFC 8332: Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol

Date de publication du RFC : Mars 2018
Auteur(s) du RFC : D. Bider (Bitvise)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF curdle
Première rédaction de cet article le 21 mars 2018


Les RFC normalisant le protocole SSH mentionnaient la possibilité pour le serveur d'avoir une clé publique RSA, mais uniquement avec l'algorithme de condensation SHA-1. Celui-ci a subi plusieurs attaques et n'est plus du tout conseillé aujourd'hui. Ce nouveau RFC est la description officielle de l'utilisation de RSA avec SHA-2 dans SSH.

Le RFC officiel du protocole SSH, le RFC 4253, définit l'algorithme ssh-rsa comme utilisant the SHA-1 hash pour la signature et la vérification. Ce n'est plus cohérent avec ce qu'on sait aujourd'hui des faiblesses de SHA-1. Par exemple, le NIST (dont les règles s'imposent aux organismes gouvernementaux états-uniens), interdit SHA-1 (voir aussi la section 5.1 du RFC).

(Petit point au passage, dans SSH, le format de la clé ne désigne que la clé elle-même, alors que l'algorithme désigne un format de clé et des procédures de signature et de vérification, qui impliquent un algorithme de condensation. Ainsi, ssh-keygen -t rsa va générer une clé RSA, indépendamment de l'algorithme qui sera utilisé lors d'une session SSH. Le registre IANA indique désormais séparement le format et l'algorithme.)

Notre nouveau RFC définit donc deux nouveaux algorithmes :

  • rsa-sha2-256, qui utilise SHA-256 pour la condensation, et dont la mise en œuvre est recommandée pour tout programme ayant SSH,
  • et rsa-sha2-512, qui utilise SHA-512 et dont la mise en œuvre est facultative.

Les deux algorithmes sont maintenant dans le registre IANA. Le format, lui, ne change pas, et est toujours qualifié de ssh-rsa (RFC 4253, section 3.) Il n'est donc pas nécessaire de changer ses clés RSA. (Si vous utilisez RSA : SSH permet d'autres algorithmes de cryptographie asymétrique, voir également la section 5.1 du RFC.)

Les deux « nouveaux » (ils sont déjà présents dans plusieurs programmes SSH) algorithmes peuvent être utilisés aussi bien pour l'authentification du client que pour celle du serveur. Ici, par exemple, un serveur OpenSSH version 7.6p1 annonce les algorithme qu'il connait (section 3.1 de notre RFC), affichés par un client OpenSSH utilisant l'option -v :


debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,ssh-rsa,rsa-sha2-256,rsa-sha2-512,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521>

    

On y voit la présence des deux nouveaux algorithmes. ssh-rsa est celui du RFC 4253, avec SHA-1 (non explicitement indiqué).

En parlant d'OpenSSH, la version 7.2 avait une bogue (corrigée en 7.2p2), qui faisait que la signature était étiquetée ssh-rsa quand elle aurait dû l'être avec rsa-sha2-256 ou rsa-sha2-512. Pour compenser cette bogue, le RFC autorise les programmes à accepter ces signatures quand même.

Autre petit piège pratique (et qui a suscité les discussions les plus vives dans le groupe de travail), certains serveurs SSH « punissent » les clients qui essaient de s'authentifier avec des algorithmes que le serveur ne sait pas gèrer, pour diminuer l'efficacité d'éventuelles attaques par repli. Pour éviter d'être pénalisé (serveur qui raccroche brutalement, voire qui vous met en liste noire), le RFC recommande que les serveurs déploient le protocole qui permet de négocier des extensions, protocole normalisé dans le RFC 8308. (L'extension intéressante est server-sig-algs.) Le client prudent peut éviter d'essayer les nouveaux algorithmes utilisant SHA-2, si le serveur n'annonce pas cette extension. L'ancien algorithme, utilisant SHA-1, devrait normalement être abandonné au fur et à mesure que tout le monde migre.

Les nouveaux algorithmes de ce RFC sont présents dans :

Vous pouvez aussi regarder le tableau de comparaison des versions de SSH. Voici encore un ssh -v vers un serveur dont la clé de machine est RSA et qui utilise l'algorithme RSA-avec-SHA512 :

...
debug1: kex: host key algorithm: rsa-sha2-512
...
debug1: Server host key: ssh-rsa SHA256:yiol3GPr1dgVo/SNXPvtFqftLw4UF+nL+ECa1yXAtG0
...
   

Par comparaison avec les SSH récents, voici un ssh -v OpenSSH vers un serveur un peu vieux :

    

debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521>

On voit que les nouveaux algorithmes manquent (pour RSA ; SHA-2 est utilisable pour ECDSA). Et pour sa clé de machine :

...
debug1: kex: host key algorithm: ssh-rsa
...
debug1: Server host key: ssh-rsa SHA256:a6cLkwFRGuEorbmN0oRjvKrXELhIVXdgHRCcbQOM2w8
  

Le serveur utilise le vieil algorithme, ssh-rsa, ce qui veut dire SHA-1 (le SHA256 qui apparait a été généré par le client).


Téléchargez le RFC 8332


L'article seul

RFC 8326: Graceful BGP Session Shutdown

Date de publication du RFC : Mars 2018
Auteur(s) du RFC : P. Francois, B. Decraene (Orange), C. Pelsser (Strasbourg University), K. Patel (Arrcus), C. Filsfils (Cisco Systems)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF grow
Première rédaction de cet article le 7 mars 2018


Voici une nouvelle communauté BGP, GRACEFUL_SHUTDOWN, qui va permettre d'annoncer une route à son pair BGP, tout en l'avertissant que le lien par lequel elle passe va être bientôt coupé pour une maintenance prévue. Le pair pourra alors automatiquement limiter l'usage de cette route et chercher tout de suite des alternatives. Cela évitera les pertes de paquets qui se produisent quand on arrête un lien ou un routeur.

Le protocole BGP (RFC 4271) qui assure le routage entre les AS qui composent l'Internet permet aux routeurs d'échanger des informations entre eux « pour aller vers 2001:db8:bc9::/48, passe donc par moi ». Avec ces informations, chaque routeur calcule les routes à suivre pour chaque destination. Une fois que c'est fait, tout le monde se repose ? Non, parce qu'il y a tout le temps des changements. Certains peuvent être imprévus et accidentels (la fameuse pelleteuse), d'autres sont planifiés à l'avance : ce sont les opérations de maintenance « le 7 février à 2300 UTC, nous allons remplacer une line card du routeur, coupant toutes les sessions BGP de cette carte ». Voici par exemple un message reçu sur la liste de diffusion des opérateurs connectés au France-IX :

      
Date: Wed, 31 Jan 2018 15:57:34 +0000
From: Quelqu'un <quelquun@opérateur>
To: "paris@members.franceix.net" <paris@members.franceix.net>
Subject: [FranceIX members] [Paris] [OPÉRATEUR/ASXXXXX] - France-IX port  maintenance

Dear peers,

Tomorrow morning CEST, we will be conducting a maintenance that will
impact one of our connection to France-IX (IPs: x.y.z.t/2001:x:y:z::t).

All sessions will be shut down before and brought back up once the maintenance will be over.

Please note that our MAC address will change as the link will be migrated to a new router.

Cheers

    

Lorsqu'une telle opération est effectuée, le résultat est le même que pour une coupure imprévue : les sessions BGP sont coupées, les routeurs retirent les routes apprises via ces sessions, et vont chercher d'autres routes dans les annonces qu'ils ont reçues. Ils propagent ensuite ces changements à leurs voisins, jusqu'à ce que tout l'Internet soit au courant. Le problème est que cela prend du temps (quelques secondes au moins, des dizaines de secondes, parfois, à moins que les routeurs n'utilisent le RFC 7911 mais ce n'est pas toujours le cas). Et pendant ce temps, les paquets continuent à arriver à des routeurs qui ne savent plus les traiter (section 3 du RFC). Ces paquets seront jetés, et devront être réémis (pour le cas de TCP). Ce n'est pas satisfaisant. Bien sûr, quand la coupure est imprévue, il n'y a pas le choix. Mais quand elle est planifiée, on devrait pouvoir faire mieux, avertir les routeurs qu'ils devraient cesser d'utiliser cette route. C'est justement ce que permet la nouvelle communauté GRACEFUL_SHUTDOWN. (Les communautés BGP sont décrites dans le RFC 1997.) Elle s'utilise avant la coupure, indiquant aux pairs qu'ils devraient commencer le recalcul des routes, mais qu'ils peuvent continuer à utiliser les anciennes routes pendant ce temps. (Notez qu'un cahier des charges avait été établi pour ce problème, le RFC 6198. Et que ce projet d'une communauté pour les arrêts planifiés est ancien, au moins dix ans.)

Ce RFC décrit donc deux choses, la nouvelle communauté normalisée, GRACEFUL_SHUTDOWN (section 5), et la procédure à utiliser pour s'en servir proprement (section 4). L'idée est que les routes qui vont bientôt être coupées pour maintenance restent utilisées, mais avec une préférence locale très faible (la valeur 0 est recommandée, la plus petite valeur possible). La notion de préférence locale est décrite dans le RFC 4271, section 5.1.5. Comme son nom l'indique, elle est locale à un AS, et représente sa préférence (décidée unilatéralement) pour une route plutôt que pour une autre. Lors du choix d'une route par BGP, c'est le premier critère consulté.

Pour mettre en œuvre cette idée, chaque routeur au bord des AS (ASBR, pour Autonomous System Border Router) doit avoir une règle qui, lorsqu'une annonce de route arrive avec la communauté GRACEFUL_SHUTDOWN, applique une préférence locale de 0. Notez que cela peut se faire avec les routeurs actuels, aucun code nouveau n'est nécessaire, ce RFC ne décrit qu'une procédure. Une fois que cette règle est en place, tout le reste sera automatique, chez les pairs de l'AS qui coupe un lien ou un routeur.

Et l'AS qui procéde à une opération de maintenance, que doit-il faire ? Dans l'ordre :

  • Sur la·es session·s BGP qui va·ont être coupée·s, appliquer la communauté GRACEFUL_SHUTDOWN aux routes qu'on annonce (outbound policy),
  • Sur la·es session·s BGP qui va·ont être coupée·s, appliquer la communauté GRACEFUL_SHUTDOWN aux routes qu'on reçoit (inbound policy), et mettre leur préférence locale à zéro,
  • Attendre patiemment que tout le monde ait convergé (propagation des annonces),
  • Procéder à l'opération de maintenance, qui va couper BGP (et c'est plus joli si on utilise le RFC 8203).

J'ai dit plus haut qu'il n'était pas nécessaire de modifier le logiciel des routeurs BGP mais évidemment tout est plus simple s'ils connaissent la communauté GRACEFUL_SHUTDOWN et simplifient ainsi la tâche de l'administrateur réseaux. Cette communauté est « bien connue » (elle n'est pas spécifique à un AS), décrite dans la section 5 du RFC, enregistrée à l'IANA et sa valeur est 0xFFFF0000 (qui peut aussi s'écrire 65535:0, dans la notation habituelle des communautés).

La section 6 du RFC fait le tour de la sécurité de ce système. Comme il permet d'influencer le routage chez les pairs (on annonce une route avec la communauté GRACEFUL_SHUTDOWN et paf, le pair met une très faible préférence à ces routes), il ouvre la porte à de l'ingénierie du trafic pas toujours bienveillante. Il est donc prudent de regarder ce qu'annoncent ses pairs, et d'engueuler ou de dépairer ceux et celles qui abusent de ce mécanisme.

Pour les amateurs de solutions alternatives, l'annexe A explique les autres techniques qui auraient pu être utilisées lors de la réception des routes marquées avec GRACEFUL_SHUTDOWN. Au lieu d'influencer la préférence locale, on aurait par exemple pu utiliser le MED (multi-exit discriminator, RFC 4271, section 5.1.4) mais il n'est considéré par les pairs qu'après d'autres critères, et il ne garantit donc pas que le lien bientôt coupé ne sera plus utilisé.

L'annexe B donne des exemples de configuration pour différents types de routeurs. (Configurations pour l'AS qui reçoit la notification d'un arrêt proche, pas pour ceux qui émettent.) Ainsi, pour IOS XR :

   !    65535:0 = 0xFFFF0000 
   community-set comm-graceful-shutdown
     65535:0
   end-set

   route-policy AS64497-ebgp-inbound
     ! Règles appliquées aux annonces reçues du pair, l'AS 64497. Bien
     ! sûr, en vrai, il y aurait plein d'autres règles, par exemple de filtrage.
     if community matches-any comm-graceful-shutdown then
       set local-preference 0
     endif
     ! On a appliqué la règle du RFC : mettre la plus faible
     ! préférence locale possible  
   end-policy

   ! La configuration de la session BGP avec le pair
   router bgp 64496
    neighbor 2001:db8:1:2::1
     remote-as 64497
     address-family ipv6 unicast
      send-community-ebgp
      route-policy AS64497-ebgp-inbound in
    

Pour BIRD, cela sera :

   # (65535, 0) = 0xFFFF0000 
   function honor_graceful_shutdown() {
       if (65535, 0) ~ bgp_community then {
           bgp_local_pref = 0;
       }
   }
   filter AS64497_ebgp_inbound
   {
           # Règles appliquées aux annonces reçues du pair, l'AS 64497. Bien
           # sûr, en vrai, il y aurait plein d'autres règles, par
	   # exemple de filtrage.
           honor_graceful_shutdown();
   }
   protocol bgp peer_64497_1 {
       neighbor 2001:db8:1:2::1 as 64497;
       local as 64496;
       import keep filtered;
       import filter AS64497_ebgp_inbound;
   }
    

Et sur OpenBGPD (on voit qu'il connait GRACEFUL_SHUTDOWN, il n'y a pas besoin de donner sa valeur) :

   AS 64496
   router-id 192.0.2.1
   neighbor 2001:db8:1:2::1 {
           remote-as 64497
   }
   # Règles appliquées aux annonces reçues du pair, l'AS 64497. Bien
   # sûr, en vrai, il y aurait plein d'autres règles, par exemple de filtrage.
   match from any community GRACEFUL_SHUTDOWN set { localpref 0 }      
    

Enfin, l'annexe C du RFC décrit quelques détails supplémentaires, par exemple pour IBGP (BGP interne à un AS).

Notez que ce nouveau RFC est prévu pour le cas où la transmission des paquets (forwarding plane) est affectée. Si c'est uniquement la session BGP (control plane) qui est touchée, la solution du RFC 4724, Graceful Restart, est plus appropriée.


Téléchargez le RFC 8326


L'article seul

RFC 8324: DNS Privacy, Authorization, Special Uses, Encoding, Characters, Matching, and Root Structure: Time for Another Look?

Date de publication du RFC : Février 2018
Auteur(s) du RFC : J. Klensin
Pour information
Première rédaction de cet article le 28 février 2018


Le DNS est une infrastructure essentielle de l'Internet. S'il est en panne, rien ne marche (sauf si vous faites partie de la minorité qui fait uniquement des ping -n et des traceroute -n). S'il est lent, tout rame. Comme le DNS, heureusement, marche très bien, et s'est montré efficace, fiable et rapide, il souffre aujourd'hui de la malédiction des techniques à succès : on essaie de charger la barque, de lui faire faire plein de choses pour lesquelles il n'était pas prévu. D'un côté, c'est un signe de succès. De l'autre, c'est parfois fragilisant. Dans ce RFC individuel (qui exprime juste le point de vue d'un individu, et n'est pas du tout une norme IETF), John Klensin, qui ne participe plus activement au développement du DNS depuis des années, revient sur certaines de ces choses qu'on essaie de faire faire au DNS et se demande si ce n'est pas trop, et à partir de quel point il faudrait arrêter de « perfectionner » le DNS et plutôt passer à « autre chose » (« quand le seul outil qu'on a est un marteau, tous les problèmes ressemblent à des clous… »). Une bonne lecture pour celleszetceux qui ne veulent pas seulement faire marcher le DNS mais aussi se demander « pourquoi c'est comme ça ? » et « est-ce que ça pourrait être différent ? »

Améliorer le système petit à petit ou bien le remplacer complètement ? C'est une question que se posent régulièrement les ingénieurs, à propos d'un logiciel, d'un langage de programmation, d'un protocole réseau. À l'extrême, il y a l'ultra-conservateur qui ne voit que des inconvénients aux solutions radicales, à l'autre il y a l'ultra-optimiste qui en a marre des rustines et qui voudrait jeter le vieux système, pour le remplacer par un système forcément meilleur, car plus récent. Entre les deux, beaucoup d'informaticiens hésitent. L'ultra-conservateur oublie que les rustines successives ont transformé l'ancien système en un monstre ingérable et difficile à maintenir, l'ultra-optimiste croit naïvement qu'un système nouveau, rationnellement conçu (par lui…) sera à coup sûr plus efficace et moins bogué. Mais les deux camps, et tout celleszetceux qui sont entre les deux peuvent tirer profit de ce RFC, pour approfondir leur réflexion.

Klensin note d'abord que le DNS est vieux. La première réflexion à ce sujet était le RFC 799 en 1981, et le premier RFC décrivant le DNS est le RFC 882, en novembre 1983. (Paul Mockapetris a raconté le développement du DNS dans « Development of the Domain Name System », j'ai fait un résumé des articles d'histoire du DNS.) Le DNS remplaçait l'ancien système fondé sur un fichier centralisé de noms de machines (RFC 810, RFC 952, et peut-être aussi RFC 953). Tout n'est pas écrit : le DNS n'a pas aujourd'hui une spécification unique et à jour, aux RFC de base (RFC 1034 et RFC 1035, il faut ajouter des dizaines de RFC qui complètent ou modifient ces deux-ci, ainsi que pas mal de culture orale. (Un exemple de cette difficulté était que, pendant le développement du RFC 7816, son auteur s'est aperçu que personne ne se souvenait pourquoi les résolveurs envoyaient le FQDN complet dans les requêtes.) Plusieurs techniques ont même été supprimées comme les requêtes inverses (RFC 3425) ou comme les types d'enregistrement WKS, MD, MF et MG.

D'autres auraient dû être supprimées, car inutilisables en pratique, comme les classes (que prétendait utiliser le projet Net4D), et qui ont fait l'objet de l'Internet-Draft « The DNS Is Not Classy: DNS Classes Considered Useless », malheureusement jamais adopté dans une IETF parfois paralysée par la règle du consensus.

D'autres évolutions ont eu lieu : le DNS original ne proposait aucun mécanisme d'options, tous les clients et tous les serveurs avaient exactement les mêmes capacités. Cela a changé avec l'introduction d'EDNS, dans le RFC 2671 en 1999.

Beaucoup d'articles ont été écrits sur les systèmes de nommage. (Le RFC recommande l'article de V. Cerf, « Desirable Properties of Internet Identifiers », ou bien le livre « Signposts in Cyberspace: The Domain Name System and Internet Navigation ». Je me permets de rajouter mes articles, « Inventer un meilleur système de nommage : pas si facile », « Un DNS en pair-à-pair ? » et « Mon premier nom Namecoin enregistré ».)

Pourquoi est-ce que les gens ne sont pas contents du DNS actuel et veulent le changer (section 4 du RFC, la plus longue du RFC) ? Il y a des tas de raisons. Certaines, dit l'auteur, peuvent mener à des évolutions raisonnables du DNS actuel. Certaines nécessiteraient un protocole complètement nouveau, incompatible avec le DNS. D'autres enfin seraient irréalistes, quel que soit le système utilisé. La section 4 les passe en revue (rappelez-vous que ce RFC est une initiative individuelle, pas une opinion consensuelle à l'IETF).

Premier problème, les requêtes « multi-types ». À l'heure actuelle, une requête DNS est essentiellement composée d'un nom (QNAME, Query Name) et d'un type (QTYPE, Query Type, par exemple AAAA pour une adresse IP, TLSA pour une clé publique, etc). Or, on aurait parfois besoin de plusieurs types. L'exemple classique est celui d'une machine double-pile (IPv6 et le vieil IPv4), qui ne sait pas quelle version d'IP est acceptée en face et qui demande donc l'adresse IPv6 et l'adresse IPv4 du pair. Il n'y a actuellement pas de solution pour ce problème, il faut faire deux requêtes DNS. (Et, non, ANY ne résout pas ce problème, notamment en raison de l'interaction avec les caches : que doit faire un cache qui ne connait qu'une seule des deux adresses ?)

Deuxième problème, la sensibilité à la casse. La norme originale prévoyait des requêtes insensibles à la casse (RFC 1034, section 3.1), ce qui semble logique aux utilisateurs de l'alphabet latin. Mais c'est plutôt une source d'ennuis pour les autres écritures (et c'est une des raisons pour lesquelles accepter l'Unicode dans les noms de domaine nécessite des méthodes particulières). Avec ASCII, l'insensibilité à la casse est facile (juste un bit à changer pour passer de majuscule en minuscule et réciproquement) mais ce n'est pas le cas pour le reste d'Unicode. En outre, il n'est pas toujours évident de connaitre la correspondance majuscule-minuscule (cf. les débats entre germanophones sur la majuscule de ß). Actuellement, les noms de domaine en ASCII sont insensibles à la casse et ceux dans le reste du jeu de caractères Unicode sont forcément en minuscules (cf. RFC 5890), libre à l'application de mettre ses propres règles d'insensibilité à la casse si elle veut, lorsque l'utilisateur utilise un nom en majuscules comme RÉUSSIR-EN.FR. On referait le DNS en partant de zéro, peut-être adopterait-on UTF-8 comme encodage obligatoire, avec normalisation NFC dans les serveurs de noms, mais c'est trop tard pour le faire.

En parlant d'IDN, d'ailleurs, ce sujet a été à l'origine de nombreuses discussions, incluant pas mal de malentendus (pour lesquels, à mon humble avis, l'auteur de RFC a une sérieuse responsabilité). Unicode a une particularité que n'a pas ASCII : le même caractère peut être représenté de plusieurs façons. L'exemple classique est le É qui peut être représenté par un point de code, U+00C9 (LATIN CAPITAL LETTER E WITH ACUTE), ou par deux, U+0045 (LATIN CAPITAL LETTER E) et U+0301 (COMBINING ACUTE ACCENT). Je parle bien de la représentation en points de code, pas de celle en bits sur le réseau, qui est une autre affaire ; notez que la plupart des gens qui s'expriment à propos d'Unicode sur les forums ne connaissent pas Unicode. La normalisation Unicode vise justement à n'avoir qu'une forme (celle à un point de code si on utilise NFC) mais elle ne traite pas tous les cas gênants. Par exemple, dans certains cas, la fonction de changement de casse dépend de la langue (que le DNS ne connait évidemment pas). Le cas le plus célèbre est celui du i sans point U+0131, qui a une règle spécifique en turc. Il ne sert à rien de râler contre les langues humaines (elles sont comme ça, point), ou contre Unicode (dont la complexité ne fait que refléter celles des langues humaines et de leurs écritures). Le point important est qu'on n'arrivera pas à faire en sorte que le DNS se comporte comme M. Toutlemonde s'y attend, sauf si on se limite à un M. Toutlemonde étatsunien (et encore).

Les IDN ont souvent été accusés, y compris dans ce RFC, de permettre, ou en tout cas de faciliter, le hameçonnage par la confusion possible entre deux caractères visuellement proches. En fait, le problème n'est pas spécifique aux IDN (regardez google.com et goog1e.com) et les études montrent que les utilisateurs ne vérifient pas les noms, de toute façon. Bref, il s'agit de simple propagande de la part de ceux qui n'ont jamais vraiment accepté Unicode.

Les IDN nous amènent à un problème proche, celui des synonymes. Les noms de domaine color.example et colour.example sont différents alors que, pour tout anglophone, color et colour sont « équivalents ». J'ai mis le mot « équivalent » entre guillemets car sa définition même est floue. Est-ce que « Saint-Martin » est équivalent à « St-Martin » ? Et est-ce que « Dupont » est équivalent à « Dupond » ? Sans même aller chercher des exemples comme l'équivalence entre sinogrammes simplifiés et sinogrammes traditionnels, on voit que l'équivalence est un concept difficile à cerner. Souvent, M. Michu s'agace « je tape st-quentin.fr, pourquoi est-ce que ça n'est pas la même chose que saint-quentin-en-yvelines.fr ? » Fondamentalement, la réponse est que le DNS ne gère pas les requêtes approximatives, et qu'il n'est pas évident que tout le monde soit d'accord sur l'équivalence de deux noms. Les humains se débrouillent avec des requêtes floues car ils ont un contexte. Si on est dans les Yvelines, je sais que « St-Quentin » est celui-ci alors que, si on est dans l'Aisne, mon interlocuteur parle probablement de celui-là. Mais le DNS n'a pas ce contexte.

Plusieurs RFC ont été écrit à ce sujet, RFC 3743, RFC 4290, RFC 6927 ou RFC 7940, sans résultats convaincants. Le DNS a bien sûr des mécanismes permettant de dire que deux noms sont équivalents, comme les alias (enregistrements CNAME) ou comme les DNAME du RFC 6672. Mais :

  • Aucun d'entre eux n'a exactement la sémantique que les utilisateurs voudraient (d'où des propositions régulières d'un nouveau mécanisme comme les BNAME),
  • Et ils supposent que quelqu'un ait configuré les correspondances, ce qui suscitera des conflits, et ne satisfera jamais tout le monde.

Même écrire un cahier des charges des « variantes » n'a jamais été possible. (C'est également un sujet sur lequel j'avais écrit un article.)

Passons maintenant aux questions de protection de la vie privée. L'auteur du RFC note que la question suscite davantage de préoccupations aujourd'hui mais ne rappelle pas que ces préoccupations ne sont pas irrationnnelles, elles viennent en grande partie de la révélation de programmes de surveillance massive comme MoreCowBell. Et il « oublie » d'ailleurs de citer le RFC 7626, qui décrit en détail le problème de la vie privée lors de l'utilisation du DNS.

J'ai parlé plus haut du problème des classes dans le DNS, ce paramètre supplémentaire des enregistrements DNS (un enregistrement est identifié par trois choses, le nom, la classe et le type). L'idée au début (RFC 1034, section 3.6) était de gérer depuis le DNS plusieurs protocoles très différents (IP, bien sûr, mais aussi CHAOS et d'autres futurs), à l'époque où le débat faisait rage entre partisans d'un réseau à protocole unique (le futur Internet) et ceux et celles qui préféraient un catenet, fondé sur l'interconnexion de réseaux techniquement différents. Mais, aujourd'hui, la seule classe qui sert réellement est IN (Internet) et, en pratique, il y a peu de chances que les autres soient jamais utilisées. Il a parfois été suggéré d'utiliser les classes pour partitionner l'espace de noms (une classe IN pour l'ICANN et créer une classe UN afin de la donner à l'UIT pour qu'elle puisse jouer à la gouvernance ?) mais le fait que les classes aient été très mal normalisées laisse peu d'espoir. (Est-ce que IN example, CH example et UN example sont la même zone ? Ont-ils les mêmes serveurs de noms ? Cela n'a jamais été précisé.)

Une particularité du DNS qui déroute souvent les nouveaux administrateurs système est le fait que les données ne soient que faiblement synchronisées : à un moment donné, il est parfaitement normal que plusieurs valeurs coexistent dans l'Internet. Cela est dû à plusieurs choix, notamment :

  • Celui d'avoir plusieurs serveurs faisant autorité pour une zone, et sans mécanisme assurant leur synchronisation forte. Lorsque le serveur maître (ou primaire) est mis à jour, il sert immédiatement les nouvelles données, sans attendre que les esclaves (ou secondaires) se mettent à jour.
  • Et le choix d'utiliser intensivement les caches, la mémoire des résolveurs. Si un serveur faisant autorité sert un enregistrement avec un TTL de 7 200 secondes (deux heures) et que, cinq minutes après qu'un résolveur ait récupéré cet enregistrement, le serveur faisant autorité modifie l'enregistrement, les clients du résolveur verront encore l'ancienne valeur pendant 7 200 - 300 = 6 900 secondes.

Cela a donné naissance à la légende de la propagation du DNS et aux chiffres fantaisistes qui accompagnent cette légende comme « il faut 24 h pour que le DNS se propage ».

Ces choix ont assuré le succès du DNS, en lui permettant de passer à l'échelle, vers un Internet bien plus grand que prévu à l'origine. Un modèle à synchronisation forte aurait été plus compliqué, plus fragile et moins performant.

Mais tout choix en ingéniérie a des bonnes conséquences et des mauvaises : la synchronisation faible empêche d'utiliser le DNS pour des données changeant souvent. Des perfectionnements ont eu lieu (comme la notification non sollicitée du RFC 1996, qui permet aux serveurs secondaires d'être au courant rapidement d'un changement, mais qui ne marche que dans le cas où on connait tous les secondaires) mais n'ont pas fondamentalement changé le tableau. Bien sûr, les serveurs faisant autorité qui désireraient une réjuvénation plus rapide peuvent toujours abaisser le TTL mais, en dessous d'une certaine valeur (typiquement 30 à 60 minutes), les TTL trop bas sont parfois ignorés.

Un autre point où les demandes de beaucoup d'utilisateurs rentrent en friction avec les concepts du DNS est celui des noms privés, des noms qui n'existeraient qu'à l'intérieur d'une organisation particulière, et qui ne nécessiteraient pas d'enregistrement auprès d'un tiers. La bonne méthode pour avoir des noms privés est d'utiliser un sous-domaine d'un domaine qu'on a enregistré (aujourd'hui, tout le monde peut avoir son domaine assez facilement, voir gratuitement), et de le déléguer à des serveurs de noms qui ne sont accessibles qu'en interne. Si on est l'association Example et qu'on est titulaire de example.org, on crée priv.example.org et on y met ensuite les noms « privés » (je mets privé entre guillemets car, en pratique, comme le montrent les statistiques des serveurs de noms publics, de tels noms fuitent souvent à l'extérieur, par exemple quand un ordinateur portable passe du réseau interne à celui d'un FAI public).

Il faut noter que beaucoup d'organisations, au lieu d'utiliser la bonne méthode citée ci-dessus, repèrent un TLD actuellement inutilisé (.home, .lan, .private…) et s'en servent. C'est une très mauvaise idée, car, un jour, ces TLD seront peut-être délégués, avec les risques de confusion que cela entrainera (cf. le cas de .box et celui de .dev).

Les administrateurs système demandent souvent « mais quel est le TLD réservé pour les usages internes » et sont surpris d'apprendre qu'il n'en existe pas. C'est en partie pour de bonnes raisons (imaginez deux entreprises utilisant ce TLD et fusionnant… Ou simplement s'interconnectant via un VPN… Un problème qu'on voit souvent avec le RFC 1918.) Mais c'est aussi en partie parce que les tentatives d'en créer un se sont toujours enlisées dans les sables de la bureaucratie (personne n'a envie de passer dix ans de sa vie professionnelle à faire du lobbying auprès de l'ICANN pour réserver un tel TLD). La dernière tentative était celle de .internal mais elle n'a pas marché.

Il y a bien un registre des noms de domaines (pas uniquement des TLD) « à usage spécial », créé par le RFC 6761. Il a malheureusement été gelé par l'IESG et fait l'objet de contestations (RFC 8244). Aucun des noms qu'il contient ne convient vraiment au besoin de ceux qui voudraient des noms de domaine internes (à part .test qui devrait logiquement être utilisé pour les bancs de test, de développement, etc). Le RFC note qu'un des principaux problèmes d'un tel registre est qu'il est impossible de garder à jour tous les résolveurs de la planète quand ce registre est modifié. On ne peut donc pas garantir qu'un nouveau TLD réservé sera bien traité de manière spéciale par tous les résolveurs.

Une caractéristique du DNS qui a suscité beaucoup de débats, pas toujours bien informés et pas toujours honnêtes, est l'existence de la racine du DNS, et des serveurs qui la servent. Lors de la mise au point du DNS, la question s'était déjà posée, certains faisant remarquer que cette racine allait focaliser les problèmes, aussi bien techniques que politiques. L'expérience a montré qu'en fait la racine marchait bien, mais cela n'a pas évité les polémiques. Le RFC note que le sujet est très chaud : qui doit gérer un serveur racine ? Où faut-il les placer physiquement ? Si l'anycast a largement résolu la seconde question (RFC 7094), la première reste ouverte. Le RFC n'en parle pas mais, si la liste des onze (ou douze, ça dépend comment on compte) organisations qui gèrent un serveur racine n'a pas évolué depuis vingt ans, ce n'est pas pour des raisons techniques, ni parce qu'aucune organisation n'est capable de faire mieux que les gérants actuels, mais tout simplement parce qu'il n'existe aucun processus pour supprimer ou ajouter un serveur racine. Comme pour les membres permanents du Conseil de Sécurité de l'ONU, on en reste au statu quo, aussi inacceptable soit-il, simplement parce qu'on ne sait pas faire autrement.

Le problème de la gestion de la racine n'est pas uniquement celui de la gestion des serveurs racine. Le contenu de la zone racine est tout aussi discuté. Si les serveurs racine sont les imprimeurs du DNS, le gérant de la zone racine en est l'éditeur. Par exemple, combien faut-il de TLD ? Si quelqu'un veut créer .pizza, faut-il le permettre ? Et .xxx ? Et .vin, que le gouvernement français avait vigoureusement combattu ? Ou encore .home, déjà largement utilisé informellement dans beaucoup de réseaux locaux, mais pour lequel il y avait trois candidatures à l'ICANN (rejetées peu de temps avant la publication du RFC). Ces questions, qui se prêtent bien aux jeux politiciens, occupent actuellement un bon bout des réunions ICANN.

La base technique à ces discussions est qu'il n'y a qu'une seule racine (RFC 2826). Son contrôle va donc forcément susciter des conflits. Un autre système de nommage que le DNS, si on le concevait de nos jours, pourrait éviter le problème en évitant ces points de contrôle. Les techniques à base de chaînes de blocs comme Namecoin sont évidemment des candidates possibles. Outre les problèmes pratiques (avec Namecoin, quand on perd sa clé privée, on perd son domaine), la question de fond est « quelle gouvernance souhaite-t-on ? »

La question de la sémantique dans les noms de domaines est également délicate. L'auteur affirme que les noms de domaines sont (ou en tout cas devraient être) de purs identificateurs techniques, sans sémantique. Cela permet de justifier les limites des noms (RFC 1034, section 3.5) : s'ils sont de purs identificateurs techniques, il n'est pas nécessaire de permettre les IDN, par exemple. On peut se contenter des lettres ASCII, des chiffres et du tiret, la règle dite LDH, qui vient du RFC 952. Cette règle « Letters-Digits-Hyphen » a été une première fois remise en cause vers 1986 lorsque 3Com a voulu son nom de domaine 3com.com (à l'époque, un nom devait commencer par une lettre, ce qui a été changé par la norme actuelle, RFC 1123). Mais cela laisse d'autres marques sans nom de domaine adapté, par exemple C&A ne peut pas avoir c&a.fr. Sans parler des cas de ceux et celles qui n'utilisent pas l'alphabet latin.

L'argument de Klensin est que ce n'est pas grave : on demande juste aux noms de domaine d'être des identificateurs uniques et non ambigus. Qu'ils ne soient pas très « conviviaux » n'est pas un problème. Inutile de dire que ce point de vue personnel ne fait pas l'unanimité.

Bien sûr, il y a aussi un aspect technique. Si on voulait, dit l'auteur ironiquement, permettre l'utilisation de la langue naturelle dans les noms de domaine, il faudrait aussi supprimer la limite de 63 caractères par composant (255 caractères pour le nom complet). Il est certain qu'il est difficile d'avoir des identificateurs qui soient à la fois utiles pour les programmes (simples, non ambigus) et pour les humains.

Le DNS n'est pas figé, et a évolué depuis ses débuts. Notamment, beaucoup de nouveaux types (RRTYPE, pour Resource Record Type) ont été créés avec le temps (cf. RFC 6895). Ce sont, par exemple :

  • NAPTR (RFC 3403), système que je trouve fort compliqué et qui n'a pas eu de succès,
  • URI (RFC 7553) qui permet de stocker des URI dans le DNS (par exemple, dig +short URI 78100.cp.bortzmeyer.fr va vous donner un URI OpenStreetMap correspondant au code postal 78100),
  • SRV (RFC 2782), qui généralise le vieux MX en permettant une indirection depuis le nom de domaine vers un nom de serveur (hélas, HTTP est le seul protocole Internet qui, stupidement, ne l'utilise pas).

Une observation à partir de l'étude du déploiement de tous les nouveaux types d'enregistrement est que ça se passe mal : pare-feux débiles qui bloquent les types qu'ils ne connaissent pas, interfaces de gestion du contenu des zones qui ne sont jamais mises à jour (bien des hébergeurs DNS ne permettent pas d'éditer URI ou TLSA, voir simplement SRV), bibliothèques qui ne permettent pas de manipuler ces types… Cela a entrainé bien des concepteurs de protocole à utiliser le type « fourre-tout » TXT. Le RFC 5507 explique ses avantages et (nombreux) inconvénients. (Le RFC 6686 raconte comment le type générique TXT a vaincu le type spécifique SPF.)

Aujourd'hui, tout le monde et son chien a un nom de domaine. Des noms se créent en quantité industrielle, ce qui est facilité par l'automatisation des procédures, et le choix de certains registres de faire des promotions commerciales. Il n'est pas exagéré de dire que, surtout dans les nouveaux TLD ICANN, la majorité des noms sont créés à des fins malveillantes. Il est donc important de pouvoir évaluer la réputation d'un nom : si mail.enlargeyourzob.xyz veut m'envoyer du courrier, puis-je utiliser la réputation de ce domaine (ce qu'il a fait précédemment) pour décider de rejeter le message ou pas ? Et si un utilisateur clique sur http://www.bitcoinspaschers.town/, le navigateur Web doit-il l'avertir que ce domaine a mauvaise réputation ? Le RFC, souvent nostalgique, rappelle que le modèle original du DNS, formalisé dans le RFC 1591, était que chaque administrateur de zone était compétent, responsable et honnête. Aujourd'hui, chacune de ces qualités est rare et leur combinaison est encore plus rare. L'auteur du RFC regrette que les registres ne soient pas davantage comptables du contenu des zones qu'ils gèrent, ce qui est un point de vue personnel et très contestable : pour un TLD qui est un service public, ce serait une violation du principe de neutralité.

Bref, en pratique, il est clair aujourd'hui qu'on trouve de tout dans le DNS. Il serait donc souhaitable qu'on puisse trier le bon grain de l'ivraie mais cela présuppose qu'on connaisse les frontières administratives. Elles ne coïncident pas forcément avec les frontières techniques (.fr et gouv.fr sont actuellement dans la même zone alors que le premier est sous la responsabilité de l'AFNIC et le second sous celle du gouvernement français). Rien dans le DNS ne les indique (le point dans un nom de domaine indique une frontière de domaine, pas forcément une frontière de zone, encore moins une frontière de responsabilité). Beaucoup de légendes circulent à ce sujet, par exemple beaucoup de gens croient à tort que tout ce qui se trouve avant les deux derniers composants d'un nom est sous la même autorité que le nom de deuxième niveau (cf. mon article sur l'analyse d'un nom). Il n'y a pas actuellement de mécanisme standard et sérieux pour déterminer les frontières de responsabilité dans un nom de domaine. Plusieurs efforts avaient été tentés à l'IETF mais ont toujours échoué. La moins mauvaise solution, aujourd'hui, est la Public Suffix List.

Beaucoup plus technique, parmi les problèmes du DNS, est celui de la taille des paquets. Car la taille compte. Il y a très très longtemps, la taille d'une réponse DNS était limitée à 512 octets. Cette limite a été supprimée en 1999 avec le RFC 2671 (c'est d'ailleurs une excellente question pour un entretien d'embauche lorsque le candidat a mis « DNS » dans la liste de ses compétences : « quelle est la taille maximale d'une réponse DNS ? »). En théorie, les réponses peuvent désormais être plus grandes (la plupart des serveurs sont configurés pour 4 096 octets) mais on se heurte à une autre limite : la MTU de 1 500 octets tend à devenir une valeur sacrée, et les réponses plus grandes que cette taille ont du mal à passer, par exemple parce qu'un pare-feu idiot bloque les fragments IP, ou parce qu'un pare-feu tout aussi crétin bloque l'ICMP, empêchant les messages Packet Too Big de passer (cf. RFC 7872).

Bref, on ne peut plus trop compter sur la fragmentation, et les serveurs limitent parfois leur réponse en UDP (TCP n'a pas de problème) à moins de 1 500 octets pour cela.

Une section entière du RFC, la 5, est consacrée au problème de la requête inverse, c'est-à-dire comment trouver un nom de domaine en connaissant le contenu d'un enregistrement lié à ce nom. La norme historique prévoyait une requête spécifique pour cela, IQUERY (RFC 1035, sections 4.1.1 et 6.4). Mais elle n'a jamais réellement marché (essentiellement parce qu'elle suppose qu'on connaisse déjà le serveur faisant autorité… pour un nom de domaine qu'on ne connait pas encore) et a été retirée dans le RFC 3425. Pour permettre quand même des « requêtes inverses » pour le cas le plus demandé, la résolution d'une adresse IP en un nom de domaine, un truc spécifique a été développé, l'« arbre inverse » in-addr.arpa (puis plus tard ip6.arpa pour IPv6). Ce truc n'utilise pas les IQUERY mais des requêtes normales, avec le type PTR. Ainsi, l'option -x de dig permet à la fois de fabriquer le nom en in-addr.arpa ou ip6.arpa et de faire une requête de type PTR pour lui :

% dig -x 2001:678:c::1
...
;; ANSWER SECTION:
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.c.0.0.0.8.7.6.0.1.0.0.2.ip6.arpa. 172796 IN PTR	d.nic.fr.
  

Cette technique pose des problèmes dans le cas de préfixes IPv4 ne tenant pas sur une frontière d'octets (cas traité par le RFC 2317). Globalement, c'est toujours resté un truc, parfois utile, souvent surestimé, mais ne fournissant jamais un service général. Une tentative avait été faite à l'IETF pour décrire ce truc et son utilisation, mais elle n'a jamais abouti (draft-ietf-dnsop-reverse-mapping-considerations), la question étant trop sensible (même écrire « des gens trouvent que les enregistrements PTR sont utiles, d'autres personnes ne sont pas d'accord », même une phrase comme cela ne rencontrait pas de consensus).

Enfin, la section 7 du RFC est consacrée à un problème difficile : le DNS ne permet pas de recherche floue. Il faut connaitre le nom exact pour avoir les données. Beaucoup d'utilisateurs voudraient quelque chose qui ressemble davantage à un moteur de recherche. Ils n'ont pas d'idée très précise sur comment ça devrait fonctionner mais ils voudraient pouvoir taper « st quentin » et que ça arrive sur www.saint-quentin-en-yvelines.fr. Le fait qu'il existe plusieurs villes nommées Saint-Quentin ne les arrête pas ; ils voudraient que ça marche « tout seul ». Le DNS a un objectif très différent : fournir une réponse exacte à une question non ambigüe.

Peut-être aurait-on pu développer un service de recherche floue au dessus du DNS. Il y a eu quelques réflexions à ce sujet (comme le projet IRNSS ou comme le RFC 2345) mais ce n'est jamais allé très loin. En pratique, les moteurs de recherche jouent ce rôle, sans que l'utilisateur comprenne la différence. Peu d'entre eux savent qu'un nom de domaine, ce n'est pas comme un terme tapé dans un moteur de recherche. On voit aussi bien des gens taper un nom de domaine dans la boîte de saisie du terme de recherche, que des gens utiliser le moteur de recherche comme fournisseurs d'identificateurs (« pour voir notre site Web, tapez "trucmachin" dans Google »). Le dernier clou dans le cercueil de la compréhension de la différence entre identificateur et moteur de recherche a été planté quand les navigateurs ont commencé à fusionner barre d'adresses et boite de saisie de la recherche.


Téléchargez le RFC 8324


L'article seul

RFC 8318: IAB, IESG, and IAOC Selection, Confirmation, and Recall Process: IAOC Advisor for the Nominating Committee

Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : S. Dawkins (Wonder Hamster)
Première rédaction de cet article le 1 février 2018


Un petit RFC bureaucratique pour traiter un manque du RFC 7437, qui ne disait pas que l'IAOC devait envoyer quelqu'un au NomCom, le comité de nomination.

En effet, le NomCom, le comité chargé d'étudier les personnes qui pourront occuper des postes dans divers organismes de l'IETF (cf. RFC 7437), ce NomCom s'occupe entre autres des postes à l'IAOC (RFC 4071), mais ne disait pas que l'IAOC pouvait envoyer un membre au NomCom, comme le faisaient les autres organes. A priori, ce n'était pas grave puisque le NomCom pouvait toujours ajouter qui il voulait comme observateur mais, en 2017, le NomCom a travaillé sur un poste à l'IAOC sans avoir de représentant de l'IAOC à ses réunions.

Ce n'est pas la fin du monde mais cela a justifié ce RFC, qui ajoute juste aux règles existantes que le NomCom devrait veiller à avoir quelqu'un de l'IAOC, ou qui connait l'IAOC.

Les discussions qui ont eu lieu autour de ce minuscule changement sont résumées dans l'annexe A. Par exemple, après un long débat, ce « représentant » de l'IAOC sera qualifié d'« advisor » et pas de « liaison » (en gros, un « liaison » a un boulot plus formel et plus sérieux.)


Téléchargez le RFC 8318


L'article seul

RFC 8315: Cancel-Locks in Netnews articles

Date de publication du RFC : Février 2018
Auteur(s) du RFC : M. Bäuerle (STZ Elektronik)
Chemin des normes
Première rédaction de cet article le 14 février 2018


Cela peut sembler étonnant, mais le service des News fonctionne toujours. Et il est régulièrement perfectionné. Ce nouveau RFC normalise une extension au format des articles qui permettra de sécuriser un petit peu l'opération d'annulation d'articles.

Une fois qu'un article est lancé sur un réseau social décentralisé, comme Usenet (RFC 5537), que faire si on le regrette ? Dans un système centralisé comme Twitter, c'est simple, on s'authentifie, on le supprime et plus personne ne le voit. Mais dans un réseau décentralisé, il faut encore propager la demande de suppression (d'annulation, sur les News). Et cela pose évidemment des questions de sécurité : il ne faut pas permettre aux méchants de fabriquer trop facilement une demande d'annulation. Notre RFC propose donc une mesure de sécurité, l'en-tête Cancel-Lock:.

Cette mesure de sécurité est simple, et ne fournit certainement pas une sécurité de niveau militaire. Pour la comprendre, il faut revenir au mécanisme d'annulation d'un article d'Usenet. Un article de contrôle est un article comme les autres, envoyé sur Usenet, mais il ne vise pas à être lu par les humains, mais à être interprété par le logiciel. Un exemple d'article de contrôle est l'article de contrôle d'annulation, défini dans le RFC 5337, section 5.3. Comme son nom l'indique, il demande la suppression d'un article, identifié par son Message ID. Au début d'Usenet, ces messages de contrôle n'avaient aucune forme d'authentification. On a donc vu apparaitre des faux messages de contrôle, par exemple à des fins de censure (supprimer un article qu'on n'aimait pas). Notre nouveau RFC propose donc qu'un logiciel proche de la source du message mette un en-tête Cancel-Lock: qui indique la clé qui aura le droit d'annuler le message.

Évidemment, ce Cancel-Lock: n'apporte pas beaucoup de sécurité, entre autre parce qu'un serveur peut toujours le retirer et mettre le sien, avant de redistribuer (c'est évidemment explicitement interdit par le RFC mais il y a des méchants). Mais cela ne change de toute façon pas grand'chose à la situation actuelle, un serveur peut toujours jeter un article, de toute façon. Si on veut quand même une solution de sécurité « sérieuse », il faut utiliser PGP, comme mentionné en passant par le RFC 5537 (mais jamais normalisé dans un RFC).

La section 2 du RFC décrit en détail le mécanisme de sécurité. La valeur de l'en-tête Cancel-Lock: est l'encodage en base64 d'une condensation d'une clé secrète (en fait, on devrait plutôt l'appeler mot de passe). Pour authentifier une annulation, le message de contrôle comportera un autre en-tête, Cancel-Key:, qui révélera la clé (qui ne devra donc être utilisée qu'une fois).

Voici un exemple. On indique explicitement l'algorithme de condensation (ici, SHA-256, la liste est dans un registre IANA). D'abord, le message original aura :

Cancel-Lock: sha256:s/pmK/3grrz++29ce2/mQydzJuc7iqHn1nqcJiQTPMc=
    

Et voici le message de contrôle, authentifié :

Cancel-Key: sha256:qv1VXHYiCGjkX/N1nhfYKcAeUn8bCVhrWhoKuBSnpMA=
    

La section 3 du RFC détaille comment on utilise ces en-têtes. Le Cancel-Lock: peut être mis par l'auteur originel de l'article, ou bien par un intermédiaire (par exemple le modérateur qui a approuvé l'article). Plusieurs Cancel-Lock: peuvent donc être présents. Notez qu'il n'y a aucun moyen de savoir si le Cancel-Lock: est « authentique ». Ce mécanisme est une solution de sécurité faible.

Pour annuler un message, on envoie un message de contrôle avec un Cancel-Key: correspondant à un des Cancel-Lock:. Les serveurs recevant ce message de contrôle condenseront la clé (le mot de passe) et vérifieront s'ils retombent bien sur le condensat contenu dans un des Cancel-Lock:.

La section 4 donne les détails sur le choix de la clé (du mot de passe). Évidemment, elle doit être difficile à deviner, donc plutôt choisie par un algorithme pseudo-aléatoire (et pas "azerty123"). Et elle doit être à usage unique puisque, une fois révélée par un Cancel-Key:, elle n'est plus secrète. L'algorithme recommandé par le RFC est d'utiliser un HMAC (RFC 2104) d'un secret et de la concaténation du Message ID du message avec le nom de l'utilisateur. Comme cela, générer un Cancel-Key: pour un message donné peut se faire avec juste le message, sans avoir besoin de mémoriser les clés. Voici un exemple, tiré de la section 5, et utilisant OpenSSL. Le secret est frobnicateZ32. Le message est le <12345@example.net> et l'utilisateur est stephane :

      
% printf "%s" "<12345@example.net>stephane" \
        | openssl dgst -sha256 -hmac "frobnicateZ32" -binary \
        | openssl enc -base64
f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=

    

Voilà, nous avons notre clé, f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=. Pour le condensat, nous nous servirons de SHA-256 :


% printf "%s" "f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=" \
        | openssl dgst -sha256 -binary \
        | openssl enc -base64
RBJ8ZsgqBnW/tYT/qu1JcXK8SA2O9g+qJLDzRY5h1cg=	
   
    

Nous pouvons alors former le Cancel-Lock: :

Cancel-Lock: sha256:RBJ8ZsgqBnW/tYT/qu1JcXK8SA2O9g+qJLDzRY5h1cg=
    

Et, si nous voulons annuler ce message, le Cancel-Key: dans le message de contrôle d'annulation aura l'air :

Control: cancel <12345@example.net>
Cancel-Key: sha256:f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=
    

Pour vérifier ce message de contrôle, le logiciel calculera le condensat de la clé et vérifiera s'il retombe bien sur RBJ8ZsgqBnW/tYT/qu1JcXK8SA2O9g+qJLDzRY5h1cg=.

Enfin, la section 7 du RFC détaille la sécurité de ce mécanisme. Cette sécurité est plutôt faible :

  • Aucune protection de l'intégrité. Un intermédiaire a pu modifier, ajouter ou supprimer le Control-Lock:. Si cela vous défrise, vous devez utiliser PGP.
  • Lors d'une annulation, la clé est visible par tous, donc elle peut être copiée et utilisée dans un message de contrôle de remplacement (au lieu de l'annulation). Mais, de toute façon, un attaquant peut aussi bien faire un nouveau message faux (Usenet ne vérifie pas grand'chose).
  • Avant ce RFC, ce mécanisme était déjà largement utilisé, depuis longtemps, et souvent en utilisant SHA-1 comme fonction de condensation. Contrairement à ce qu'on lit parfois, SHA-1 n'est pas complètement cassé : il n'y a pas encore eu de publication d'une attaque pré-image pour SHA-1 (seulement des collisions). Néanmoins, SHA-1 ne peut plus être considéré comme sûr, d'autant plus que Usenet évolue très lentement : un logiciel fait aujourd'hui va rester en production longtemps et, avec le temps, d'autres attaques contre SHA-1 apparaitront. D'où la recommandation du RFC de n'utiliser que SHA-2.

Les deux nouveaux en-têtes ont été ajoutés au registre des en-têtes.

À noter que, comme il n'y a pas de groupe de travail IETF sur les News, ce RFC a été surtout discuté… sur les News, en l'occurrence les groupes news.software.nntp et de.comm.software.newsserver. Comme ce RFC décrit une technique ancienne, il y a déjà de nombreuses mises en œuvre comme la bibliothèque canlock (paquetage Debian libcanlock2), dans le serveur INN, ou les clients News Gnus (regardez cet article sur son usage), flnews, slrn ou tin. Vous pouvez aussi lire l'article de Brad Templeton comparant Cancel-Lock: aux signatures.

Merci à Julien Élie pour sa relecture.


Téléchargez le RFC 8315


L'article seul

RFC 8314: Cleartext Considered Obsolete: Use of TLS for Email Submission and Access

Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : K. Moore (Windrock), C. Newman (Oracle)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF uta
Première rédaction de cet article le 31 janvier 2018


Ce RFC s'inscrit dans le cadre des efforts de sécurisation de l'Internet contre la surveillance massive, efforts notablement accélérés depuis les révélations Snowden. Désormais, l'usage de texte en clair (non chiffré) pour le courrier électronique est officiellement abandonné : POP, IMAP et SMTP doivent être chiffrés.

Plus précisément, ce nouveau RFC du groupe de travail UTA vise l'accès au courrier (POPRFC 1939 et IMAPRFC 3501), ainsi que la soumission de messages par SMTP - RFC 6409 (entre le MUA et le premier MTA). Les messages de ces protocoles doivent désormais être systématiquement chiffrés avec TLSRFC 5246. Le but est d'assurer la confidentialité des échanges. Le cas de la transmission de messages entre MTA est couvert par des RFC précédents, RFC 3207 et RFC 7672.

Pour résumer les recommandations concrètes de ce RFC :

  • TLS 1.2 au minimum (on croise encore souvent SSL malgré le RFC 7568).
  • Migrer le plus vite possible vers le tout-chiffré si ce n'est pas déjà fait.
  • Utiliser le « TLS implicite » sur un port dédié, où on démarre TLS immédiatement (par opposition au vieux système STARTTLS, où on demandait explicitement le démarrage de la cryptographie, cf. sections 2 et 3 du RFC).

Ce RFC ne traite pas le cas du chiffrement de bout en bout, par exemple avec PGP (RFC 4880 et RFC 3156). Ce chiffrement de bout en bout est certainement la meilleure solution mais il est insuffisamment déployé aujourd'hui. En attendant qu'il se généralise, il faut bien faire ce qu'on peut pour protéger les communications. En outre, PGP ne protège pas certaines métadonnées comme les en-têtes (From:, Subject:, etc), alors qu'un chiffrement TLS du transport le fait. Bref, on a besoin des deux.

La section 3 du RFC rappelle ce qu'est le « TLS implicite », qui est désormais recommandé. Le client TLS se connecte à un serveur TLS sur un port dédié, où tout se fait en TLS, et il démarre la négociation TLS immédiatement. Le TLS implicite s'oppose au « TLS explicite » qui était l'approche initiale pour le courrier. Avec le TLS explicite (RFC 2595 et RFC 3207), le serveur devait annoncer sa capacité à faire du TLS :

% telnet smtpagt1.ext.cnamts.fr. smtp
Trying 93.174.145.55...
Connected to smtpagt1.ext.cnamts.fr.
Escape character is '^]'.
220 smtpagt1.ext.cnamts.fr ESMTP CNAMTS (ain1)
EHLO mail.example.com
250-smtpagt1.ext.cnamts.fr Hello mail.example.com [192.0.2.187], pleased to meet you
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-8BITMIME
250 STARTTLS
    

Et le client devait dire qu'il allait démarrer la session TLS avec la commande STARTTLS. L'inconvénient principal de STARTTLS est qu'il est vulnérable à l'attaque « SSL stripping » où un attaquant actif modifie la communication avant que TLS ne démarre, pour faire croire que le partenaire ne sait pas faire de TLS. (Et il y a aussi la vulnérabilité CERT #555316.) Bien sûr, les serveurs peuvent se protéger en refusant les connexions sans STARTTLS mais peu le font. L'approche STARTTLS était conçue pour gérer un monde où certains logiciels savaient faire du TLS et d'autres pas, mais, à l'heure où la recommandation est de faire du TLS systématiquement, elle n'a plus guère d'utilité. (La question est discutée plus en détail dans l'annexe A. Notez qu'un des auteurs de notre nouveau RFC, Chris Newman, était l'un des auteurs du RFC 2595, qui introduisait l'idée de STARTTLS.)

Avec le TLS implicite, cela donne :

% openssl s_client -connect mail.example.com:465   
...
subject=/CN=mail.example.com
issuer=/O=CAcert Inc./OU=http://www.CAcert.org/CN=CAcert Class 3 Root
...
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
...
220 mail.example.com ESMTP Postfix
EHLO toto
250-mail.example.com
250-AUTH DIGEST-MD5 NTLM CRAM-MD5
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250 SMTPUTF8
    

Donc, concrètement, pour POP, cela veut dire établir la connexion sur le port du TLS implicite, le 995 (et non pas sur le port 110, prévu pour le texte en clair), lancer TLS et authentifier avec le RFC 7817. Puis on fait du POP classique. Pour IMAP, c'est le port 993. Dans les deux cas, cette recommandation de notre RFC ne sera pas trop dure à suivre, le TLS implicite est déjà courant pour ces deux protocoles.

Pour la soumission SMTP (RFC 6409), c'est le port 465 (utilisé auparavant pour du SMTP classique, non-soumission, cf. le registre IANA et les section 7.3 et annexe A du RFC qui justifient cette réaffectation). Le mécanisme avec STARTTLS sur le port 587 (TLS explicite) est très répandu, contrairement à ce qui se passe pour POP et IMAP. La transition sera donc plus longue, et il faudra donc maintenir les deux services, TLS implicite et explicite, pendant un certain temps.

Voici les serveurs IMAP (pour récupérer le courrier) et SMTP (pour soumettre du courrier) conformes aux recommandations de ce RFC (TLS implicite), tels qu'affichés par Thunderbird : thunderbird-servers-ports.png

Et voici la configuration du MTA Postfix pour accepter du TLS implicite sur le port 465 :

submissions inet n       -       -       -       -       smtpd
   -o syslog_name=postfix/submissions
   -o smtpd_tls_wrappermode=yes
   -o smtpd_sasl_auth_enable=yes
   -o smtpd_client_restrictions=permit_sasl_authenticated,reject
   -o smtpd_etrn_restrictions=reject
   -o smtpd_sasl_authenticated_header=yes
    

(À mettre dans le master.cf.) Cette configuration active TLS et exige une authentification du client (ce qui est normal pour soumettre du courrier.) Pensez aussi à vérifier que le port est bien défini dans /etc/services (smtps 465/tcp ssmtp submissions).

La section 4 du RFC fournit des détails sur l'utilisation de TLS, du côté des fournisseurs de service de courrier. Le point essentiel est « chiffrement partout, tout le temps » (section 4.1). L'objectif ne pourra pas forcément être atteint immédiatement par tout le monde mais il faut commencer. Par exemple, on peut interdire tout accès en texte clair à certains utilisateurs, puis généraliser à tous. Et, dans ce cas, le message envoyé doit être indépendant de si le mot de passe était valide ou pas (pour ne pas donner d'indication à un éventuel écoutant). Le RFC conseille aussi de traiter les accès avec les vieilles versions de TLS (ou, pire, avec SSL) de la même façon que les accès en clair. Une fois l'accès en clair coupé, il est recommandé de forcer un changement de mot de passe, si l'ancien avait été utilisé en clair.

Et les autres points ? Les fournisseurs de services de courrier électronique doivent annoncer les services POP, IMAP et soumission SMTP en utilisant les enregistrements SRV du RFC 6186, afin de faciliter la tâche des MUA. Un nouvel enregistrement SRV arrive avec ce RFC, d'ailleurs, le _submissions._tcp, pour la soumission SMTP sur TLS. Le RFC demande que ces enregistrements SRV (ainsi que les enregistrements MX utilisés pour le courrier entrant, donc a priori pas le sujet de ce RFC) soient signés avec DNSSEC. Et, à propos du DNS, le RFC demande également la publication d'enregistrements TLSA (RFC 6698).

La cryptographie, c'est bien, mais il est également souhaitable de signaler qu'elle a été utilisée, et dans quelles conditions, pour informer les utilisateurs de la sécurité dont ils ont pu bénéficier. Par exemple, le RFC demande que les algorithmes de cryptographie utilisées soient mis dans l'en-tête Received: du courrier (cf. aussi le RFC 3848), via une clause tls (dans registre IANA). Notez que beaucoup de serveurs SMTP le font déjà, avec une syntaxe différente :

      
Received: from mail4.protonmail.ch (mail4.protonmail.ch [185.70.40.27])
        (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
        (No client certificate requested)
        by mail.bortzmeyer.org (Postfix) with ESMTPS id 38DAF31D16
        for <stephane@bortzmeyer.org>; Sat, 20 Jan 2018 16:56:59 +0100 (CET)

      

La section 5 décrit les exigences pour l'autre côté, le client, cette fois, et non plus le serveur de la section 4. D'abord, comme les MUA sont en contact direct avec l'utilisateur humain, ils doivent lui indiquer clairement le niveau de confidentialité qui a été obtenu (par exemple par une jolie icône, différente si on a utilisé TLS ou pas). Notez que cette indication de la confidentialité est un des points du projet Caliopen. Gmail, par exemple, peut afficher ces informations et cela donne :gmail-tls.png

Comment est-ce qu'un MUA doit trouver s·on·es serveur·s, au fait ? La méthode recommandée est d'utiliser les enregistements SRV du RFC 6186. Aux _imap._tcp et _pop3._tcp du RFC précédent s'ajoute _submissions._tcp pour le SMTP de soumission d'un message, avec TLS implicite. Attention, le but étant la confidentialité, le MUA ne devrait pas utiliser les serveurs annoncés via les SRV s'ils ne satisfont pas à des exigences minimales de sécurité TLS. Le MUA raisonnable devrait vérifier que le SRV est signé avec DNSSEC, ou, sinon, que le serveur indiqué est dans le même domaine que le domaine de l'adresse de courrier. La section 5.2 donne d'autres idées de choses à vérifier (validation du certificat du serveur suivant le RFC 7817, de la version de TLS…) D'une manière générale, le MUA ne doit pas envoyer d'informations sensibles comme un mot de passe si la session n'est pas sûre. La tentation pourrait être d'afficher à l'utilisateur un dialogue du genre « Nous n'avons pas réussi à établir une connexion TLS satisfaisante car la version de TLS n'est que la 1.1 et le groupe Diffie-Hellman ne fait que 512 bits, voulez-vous continuer quand même ? » Mais le RFC s'oppose à cette approche, faisant remarquer qu'il est alors trop facile pour l'utilisateur de cliquer OK et de prendre ainsi des risques qu'il ne maitrise pas.

Autre question de sécurité délicate, l'épinglage du certificat. Il s'agit de garder un certificat qui a marché autrefois, même s'il ne marche plus, par exemple parce qu'expiré. (Ce n'est donc pas le même épinglage que celui qui est proposé pour HTTP par le RFC 7469.) Le RFC autorise cette pratique, ce qui est du bon sens : un certificat expiré, ce n'est pas la même chose qu'un certificat faux. Et ces certificats expirés sont fréquents car, hélas, bien des administrateurs système ne supervisent pas l'expiration des certificats. Voici la configuration d'Icinga pour superviser un service de soumission via SMTP :

      
apply Service "submissions" {
  import "generic-service"

  check_command = "ssmtp"
  vars.ssmtp_port = 465
  assign where (host.address || host.address6) && host.vars.submissions
  vars.ssmtp_certificate_age = "7,3"
}

    

Et, une fois ce service défini, on peut ajouter à la définition d'un serveur vars.submissions = true et il sera alors supervisé : icinga-submissions.png

Notre RFC recommande également aux auteurs de MUA de faire en sorte que les utilisateurs soient informés du niveau de sécurité de la communication entre le client et le serveur. Tâche délicate, comme souvent quand on veut communiquer avec les humains. Il ne faut pas faire de fausses promesses (« votre connection est cryptée avec des techniques military-grade, vous êtes en parfaite sécurité ») tout en donnant quand même des informations, en insistant non pas sur la technique (« votre connexion utilise ECDHE-RSA-AES256-GCM-SHA384, je vous mets un A+ ») mais sur les conséquences (« Ce serveur de courrier ne propose pas de chiffrement de la communication, des dizaines d'employés de la NSA, de votre FAI, et de la Fsociety sont en train de lire le message où vous parlez de ce que vous avez fait hier soir. »).

Voilà, vous avez l'essentiel de ce RFC. Quelques détails, maintenant. D'abord, l'interaction de ces règles avec les antivirus et antispam. Il y a plusieurs façons de connecter un serveur de messagerie à un logiciel antivirus et·ou antispam (par exemple l'interface Milter, très répandue). Parfois, c'est via SMTP, avec l'antivirus et·ou antispam qui se place sur le trajet, intercepte les messages et les analyse avant de les retransmettre. C'est en général une mauvaise idée (RFC 2979). Dès qu'il y a du TLS dans la communication, c'est encore pire. Puisque le but de TLS est de garantir l'authenticité et l'intégrité de la communication, tout « intercepteur » va forcément être très sale. (Et les logiciels qui font cela sont d'abominables daubes.)

Ah, et un avertissement lié à la vie privée (section 8 du RFC) : si on présente un certificat client, on révèle son identité à tout écoutant. Le futur TLS 1.3 aidera peut-être à limiter ce risque mais, pour l'instant, attention à l'authentification par certificat client.

Si vous aimez connaitre les raisons d'un choix technique, l'annexe A couvre en détail les avantages et inconvénients respectifs du TLS implicite sur un port séparé et du TLS explicite sur le port habituel. La section 7 du RFC 2595 donnait des arguments en faveur du TLS explicite. Certaines des critiques que ce vieux RFC exprimait contre le TLS implicite n'ont plus de sens aujourd'hui (le RFC 6186 permet que le choix soit désormais fait par le logiciel et plus par l'utilisateur, et les algorithmes export sont normalement abandonnés). D'autres critiques ont toujours été mauvaises (par exemple, celle qui dit que le choix entre TLS et pas de TLS est binaire : un MUA peut essayer les deux.) Outre la sécurité, un avantage du port séparé pour TLS est qu'il permet de déployer des frontaux - répartiteurs de charge, par exemple - TLS génériques.

Et pour terminer, d'autres exemples de configuration. D'abord avec mutt, par Keltounet :

      smtp_url=smtps://USER@SERVER:465
    

Et le courrier est bien envoyé en TLS au port de soumission à TLS implicite.

Avec Dovecot, pour indiquer les protocoles utilisés dans la session TLS, Shaft suggère, dans conf.d/10-logging.conf :

login_log_format_elements = user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k
    

(Notez le %k à la fin, qui n'est pas dans le fichier de configuration d'exemple.)

Et pour OpenSMTPd ? n05f3ra1u propose :

pki smtp.monserveur.example key "/etc/letsencrypt/live/monserveur.example/privkey.pem"
pki smtp.monserveur.example certificate "/etc/letsencrypt/live/monserveur.example/cert.pem"
...
listen on eno1 port 465 hostname smtp.monserveur.example smtps pki smtp.monserveur.example auth mask-source
    

Téléchargez le RFC 8314


L'article seul

RFC 8312: CUBIC for Fast Long-Distance Networks

Date de publication du RFC : Février 2018
Auteur(s) du RFC : I. Rhee (NCSU), L. Xu (UNL), S. Ha (Colorado), A. Zimmermann, L. Eggert (NetApp), R. Scheffenegger
Pour information
Réalisé dans le cadre du groupe de travail IETF tcpm
Première rédaction de cet article le 8 février 2018


Longtemps après sa mise au point et son déploiement massif sur Linux, voici la description officielle de l'algorithme CUBIC, un algorithme de contrôle de la congestion dans TCP.

CUBIC doit son nom au fait que la fonction de calcul de la fenêtre d'envoi des données est une fonction cubique (elle a un terme du troisième degré) et non pas linéaire. CUBIC est l'algorithme utilisé par défaut dans Linux depuis pas mal d'années :

% sudo sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = cubic
    

CUBIC est plus énergique lorsqu'il s'agit d'agrandir la fenêtre d'envoi de données, lorsque le réseau a une grande capacité mais un RTT important. Dans le cas de ces réseaux « éléphants » (terme issu de la prononciation en anglais de LFN, Long and Fat Network, voir RFC 7323, section 1.1), le RTT élevé fait que l'émetteur TCP met du temps à recevoir les accusés de réception, et donc à comprendre que tout va bien et qu'il peut envoyer davantage de données, pour remplir le long tuyau. CUBIC permet d'accélérer cette phase.

Notez que CUBIC ne contrôle que l'émetteur, le récepteur est inchangé. Cela facilite le déploiement : un émetteur CUBIC peut parfaitement communiquer avec un récepteur traditionnel.

Avant de lire la suite du RFC, il est recommandé de (re)lire le RFC 5681, la bible sur le contrôle de congestion TCP, et notamment sur cette notion de fenêtre d'envoi (ou fenêtre de congestion).

TCP (RFC 793) a évidemment une mission difficile. L'intérêt de l'émetteur est d'envoyer le plus de données le plus vite possible. Mais à condition qu'elles arrivent sinon, s'il y a de la congestion, les données seront perdues et il faudra recommencer (ré-émettre). Et on n'est pas tout seul sur le réseau : il faut aussi tenir compte des autres, et chercher à partager équitablement l'Internet. L'algorithme doit donc être énergique (chercher à utiliser les ressources au maximum) mais pas bourrin (il ne faut pas dépasser le maximum), tout en étant juste (on n'est pas dans la startup nation, il ne faut pas écraser les autres, mais partager avec eux).

Le problème des éléphants, des réseaux à fort BDP, est connu depuis longtemps (article de T. Kelly, « Scalable TCP: Improving Performance in HighSpeed Wide Area Networks », et RFC 3649.) Dans ces réseaux, TCP tend à être trop prudent, à ouvrir sa fenêtre (les données qu'on peut envoyer tout de suite) trop lentement. Cette prudence l'honore, mais peut mener à des réseaux qui ne sont pas utilisés à fond. L'article de Ha, S., Kim, Y., Le, L., Rhee, I., et L. Xu, « A Step toward Realistic Performance Evaluation of High-Speed TCP Variants » expose ce problème. Il touche toutes les variantes de TCP depuis le TCP Reno décrit dans le RFC 5681 : le New Reno des RFC 6582 et RFC 6675, et même des protocoles non-TCP mais ayant le même algorithme, comme UDP (TFRC, RFC 5348) ou SCTP (RFC 4960).

CUBIC a été originellement documenté dans l'article de S. Ha, Injong Rhee, et Lisong Xu, « CUBIC: A New TCP-Friendly High-Speed TCP Variant », en 2008. Sur Linux, il a remplacé BIC pour les réseaux à haut BDP.

La section 3 du RFC rappelle les principes de conception de CUBIC, notamment :

  • Utilisation de la partie concave (la fenêtre s'agrandit rapidement au début puis plus lentement ensuite) et de la partie convexe de la fonction, et pas seulement la partie convexe (on ouvre la fenêtre calmement puis on accélère), comme l'ont tenté la plupart des propositions alternatives. Si vous avez du mal avec les termes concave et convexe, la figure 2 de cet article de comparaison de CUBIC et BIC illustre bien, graphiquement, ces concepts. La courbe est d'abord concave, puis convexe.
  • Comportement identique à celui de ses prédécesseurs pour les liaisons à faible RTT (ou faible BDP). Les algorithmes TCP traditionnels n'ont en effet pas de problème dans ce secteur (cf. section 4.2, et Floyd, S., Handley, M., et J. Padhye, « A Comparison of Equation-Based and AIMD Congestion Control »). « Si ce n'est pas cassé, ne le réparez pas. » CUBIC ne se différencie donc des autres algorithmes que pour les réseaux à RTT élevé, ce que rappelle le titre de notre RFC.
  • Juste partage de la capacité entre des flots ayant des RTT différents.
  • CUBIC mène à un agrandissement plus rapide de la fenêtre d'envoi, mais également à une réduction moins rapide lorsqu'il détecte de la congestion (paramètre « beta_cubic », le facteur de réduction de la fenêtre, voir aussi le RFC 3649.)

La section 4 du RFC spécifie précisement l'algorithme, après beaucoup de discussion avec les développeurs du noyau Linux (puisque le code a été écrit avant le RFC). Cette section est à lire si vous voulez comprendre tous les détails. Notez l'importance du point d'inflexion entre la partie concave et la partie convexe de la courbe qui décrit la fonction de changement de taille de la fenêtre. Ce point d'inflexion est mis à la valeur de la fenêtre d'envoi juste avant la dernière fois où TCP avait détecté de la congestion.

Notez que Linux met en outre en œuvre l'algorithme HyStart, décrit dans « Taming the Elephants: New TCP Slow Start ». Hystart mesure le RTT entre émetteur et récepteur pour détecter (par l'augmentation du RTT) un début de congestion avant que des pertes de paquets se produisent. (Merci à Lucas Nussbaum pour l'information.)

La section 5 analyse le comportement CUBIC selon les critères de comparaison des algorithmes de contrôle de la congestion décrits dans le RFC 5033.

Pour finir, voici une intéressante comparaison des algorithmes de contrôle de congestion.


Téléchargez le RFC 8312


L'article seul

RFC 8310: Usage Profiles for DNS over TLS and DNS over DTLS

Date de publication du RFC : Mars 2018
Auteur(s) du RFC : S. Dickinson (Sinodun), D. Gillmor (ACLU), T. Reddy (McAfee)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dprive
Première rédaction de cet article le 22 mars 2018


Afin de mieux protéger la vie privée des utilisateurs du DNS, le RFC 7858 normalise comment faire du DNS sur TLS, le chiffrement empêchant la lecture des requêtes et réponses DNS. Mais le chiffrement sans authentification n'a qu'un intérêt limité. Notamment, il ne protège pas contre un attaquant actif, qui joue les hommes du milieu. Le RFC 7858 ne proposait qu'un seul mécanisme d'authentification, peu pratique, les clés publiques configurées statiquement dans le client DNS. Ce nouveau RFC décrit d'autres mécanismes d'authentification, classés selon deux profils, un Strict (sécurité maximum) et un Opportuniste (disponibilité maximum).

Le problème de la protection de la vie privée quand on utilise le DNS est décrit dans le RFC 7626. Les solutions, comme toujours quand il s'agit de vie privée, se répartissent en deux catégories, la minimisation des données (RFC 7816) et le chiffrement (RFC 7858 et RFC 8094). Le chiffrement protège bien contre un attaquant purement passif. Mais si celui qui veut écouter les échanges DNS est capable de lancer des attaques actives (ARP spoofing, par exemple), le chiffrement ne suffit pas, il faut le doubler d'une authentification du serveur. Par exemple, en mars 2014, en Turquie, l'attaquant (le gouvernement) a pu détourner le trafic avec Google Public DNS. Même si Google Public DNS avait permis le chiffrement, il n'aurait pas servi, lors de cette attaque active. Sans l'authentification, on risque de parler en chiffré… à l'attaquant.

Le problème de l'authentification, c'est que si elle échoue, que faut-il faire ? Renoncer à envoyer des requêtes DNS ? Cela revient à se couper d'Internet. Notre nouveau RFC, considérant qu'il n'y a pas une solution qui conviendra à tous les cas, propose deux profils :

  • Le profil strict privilégie la confidentialité. Si on ne peut pas chiffrer et authentifier, on renonce.
  • Le profil opportuniste privilégie le fonctionnement du DNS. Si on ne peut pas authentifier, tant pis, on chiffre sans authentifier, mais, au moins, on a du DNS.

Notez bien qu'un profil spécifie des propriétés, une fin et pas un moyen. Un profil est défini par ces propriétés, qu'on implémente ensuite avec des mécanismes, décrits en section 6. Le RFC 7858 spécifiait déjà, dans sa section 4.2, un mécanisme d'authentification, fondé sur la connaissance préalable, par le client DNS, de la clé publique du serveur (SPKI, pour Subject Public Key Info, une solution analogue à celle du RFC 7469 pour HTTP). Mais beaucoup de détails manquaient. Ce nouveau RFC 8310 :

  • Décrit comment le client est censé obtenir les informations nécessaires à l'authentification,
  • Quelles lettres de créance peut présenter le serveur pour s'authentifier,
  • Et comment le client peut les vérifier.

À propos de ces « informations nécessaires à l'authentification », il est temps d'introduire un acronyme important (section 2 du RFC), ADN (Authentication Domain Name), le nom du serveur qu'on veut authentifier. Par exemple, pour Quad9, ce sera dns.quad9.net. Autres termes importants :

  • Ensemble de clés (SPKI pinset), un ensemble de clés cryptographiques (ou plutôt de condensats de clés),
  • Identificateur de référence (reference identifier), l'identificateur utilisé pour les vérifications (cf. RFC 6125).
  • Lettres de créance (credentials), les informations du serveur qui lui servent à s'authentifier, certificat PKIX, enregistrement TLSA (RFC 6698) ou ensemble de clés.

Note importante, la section 3 du RFC pointe le fait que les mécanismes d'authentification présentés ici ne traitent que l'authentification d'un résolveur DNS par son client. L'éventuelle future authentification d'un serveur faisant autorité par le résolveur est hors-sujet, tout comme l'authentification du client DNS par le serveur. Sont également exclus les idées d'authentifier le nom de l'organisation qui gère le serveur ou son pays : on authentifie uniquement l'ADN, « cette machine est bien dns-resolver.yeti.eu.org », c'est tout.

La section 5 présente les deux profils normalisés. Un profil est une politique choisie par le client DNS, exprimant ses exigences, et les compromis qu'il est éventuellement prêt à accepter si nécessaire. Un profil n'est pas un mécanisme d'authentification, ceux-ci sont dans la section suivante, la section 6. La section 5 présente deux profils :

  • Le profil strict, où le client exige chiffrement avec le serveur DNS et authentification du serveur. Si cela ne peut pas être fait, le profil strict renonce à utiliser le DNS, plutôt qu'à prendre des risques d'être surveillé. Le choix de ce profil protège à la fois contre les attaques passives et contre les attaques actives.
  • Le profil opportuniste, où le client tente de chiffrer et d'authentifier, mais est prêt à continuer quand même si ça échoue (cf. RFC 7435, sur ce concept de sécurité opportuniste). Si le serveur DNS permet le chiffrement, l'utilisateur de ce profil est protégé contre une attaque passive, mais pas contre une attaque active. Si le serveur ne permet pas le chiffrement, l'utilisateur risque même de voir son trafic DNS passer en clair.

Les discussions à l'IETF (par exemple pendant la réunion de Séoul en novembre 2016) avaient été vives, notamment sur l'utilité d'avoir un profil strict, qui peut mener à ne plus avoir de résolution DNS du tout, ce qui n'encouragerait pas son usage.

Une petite nuance s'impose ici : pour les deux profils, il faudra, dans certains cas, effectuer une requête DNS au tout début pour trouver certaines informations nécessaires pour se connecter au serveur DNS (par exemple son adresse IP si on n'a que son nom). Cette « méta-requête » peut se faire en clair, et non protégée contre l'écoute, même dans le cas du profil strict. Autrement, le déploiement de ce profil serait très difficile (il faudrait avoir toutes les informations stockées en dur dans le client).

Le profil strict peut être complètement inutilisable si les requêtes DNS sont interceptées et redirigées, par exemple dans le cas d'un portail captif. Dans ce cas, la seule solution est d'avoir un mode « connexion » pendant lequel on n'essaie pas de protéger la confidentialité des requêtes DNS, le temps de passer le portail captif, avant de passer en mode « accès Internet ». C'est ce que fait DNSSEC-trigger (cf. section 6.6).

Le tableau 1 du RFC résume les possibilités de ces deux profils, face à un attaquant actif et à un passif. Dans tous les cas, le profil strict donnera une connexion DNS chiffrée et authentifiée, ou bien pas de connexion du tout. Dans le meilleur cas, le profil opportuniste donnera la même chose que le strict (connexion chiffrée et authentifiée). Si le serveur est mal configuré ou si le client n'a pas les informations nécessaires pour authentifier, ou encore si un Homme du Milieu intervient, la session ne sera que chiffrée, et, dans le pire des cas, le profil opportuniste donnera une connexion en clair. On aurait pu envisager d'autres profils (par exemple un qui impose le chiffrement, mais pas l'authentification) mais le groupe de travail à l'IETF a estimé qu'ils n'auraient pas eu un grand intérêt, pour la complexité qu'ils auraient apporté.

Un mot sur la détection des problèmes. Le profil opportuniste peut permettre la détection d'un problème, même si le client continue ensuite malgré les risques. Par exemple, si un résolveur DNS acceptait DNS-sur-TLS avant, que le client avait enregistré cette information, mais que, ce matin, les connexions vers le port 853 sont refusées, avec le profil opportuniste, le client va quand même continuer sur le port 53 (en clair, donc) mais peut noter qu'il y a un problème. Il peut même (mais ce n'est pas une obligation) prévenir l'utilisateur. Cette possibilité de détection est le « D » dans le tableau 1, et est détaillée dans la section 6.5. La détection permet d'éventuellement prévenir l'utilisateur d'une attaque potentielle, mais elle est aussi utile pour le déboguage.

Évidemment, seul le profil strict protège réellement l'utilisateur contre l'écoute et toute mise en œuvre de DNS-sur-TLS devrait donc permettre au moins ce profil. Le profil opportuniste est là par réalisme : parfois, il vaut mieux une connexion DNS écoutée que pas de DNS du tout.

Les deux profils vont nécessiter un peu de configuration (le nom ou l'adresse du résolveur) mais le profil strict en nécessite davantage (par exemple la clé du résolveur).

Maintenant qu'on a bien décrit les profils, quels sont les mécanismes d'authentification disponibles ? La section 6 les décrit rapidement, et le tableau 2 les résume, il y en a six en tout, caractérisés par l'information de configuration nécessaire côté client (ils seront détaillés en section 8) :

  • Adresse IP du résolveur + clé du résolveur (SPKI, Subject Public Key Info). C'est celui qui était présenté dans la section 4.2 du RFC 7858, et qui est illustré dans mon article sur la supervision de résolveurs DNS-sur-TLS. Ce mécanisme est pénible à gérer (il faut par exemple tenir compte des éventuels changements de clé) mais c'est celui qui minimise la fuite d'information : l'éventuel surveillant n'apprendra que l'ADN (dans le SNI de la connexion TLS). En prime, il permet d'utiliser les clés nues du RFC 7250 (cf. section 9 du RFC).
  • ADN (nom de domaine du résolveur DNS-sur-TLS) et adresse IP du résolveur. On peut alors authentifier avec le certificat PKIX, comme on le fait souvent avec TLS (cf. section 8 du RFC, RFC 5280 et RFC 6125). L'identificateur à vérifier est l'ADN, qui doit se trouver dans le certificat, comme subjectAltName.
  • ADN seul. La configuration est plus simple et plus stable mais les méta-requêtes (obtenir l'adresse IP du résolveur à partir de son ADN) ne sont pas protégées et peuvent être écoutées. Si on n'utilise pas DNSSEC, on peut même se faire détourner vers un faux serveur (la vérification du certificat le détecterait, si on pouvait faire confiance à toutes les AC situées dans le magasin).
  • DHCP. Aucune configuration (c'est bien le but de DHCP) mais deux problèmes bloquants : il n'existe actuellement pas d'option DHCP pour transmettre cette information (même pas de projet) et DHCP lui-même n'est pas sûr.
  • DANE (RFC 6698). On peut authentifier le certificat du résolveur, non pas avec le fragile système des AC X.509, mais avec DANE. L'enregistrement TLSA devra être en _853._tcp.ADN. Cela nécessite un client capable de faire de la validation DNSSEC (à l'heure actuelle, le résolveur sur la machine cliente est en général un logiciel minimal, incapable de valider). Et les requêtes DANE (demande de l'enregistrement TLSA) peuvent passer en clair.
  • DANE avec une extension TLS. Cette extension (actuellement non encore décrite dans un RFC, cf. le projet draft-ietf-tls-dnssec-chain-extension) permet au serveur DNS-sur-TLS d'envoyer les enregistrements DNS et DNSSEC dans la session TLS elle-même. Plus besoin de méta-requêtes et donc plus de fuites d'information.

Cela fait beaucoup de mécanismes d'authentification ! Comment se combinent-ils ? Lesquels essayer et que faire s'ils donnent des résultats différents ? La méthode recommandée, si on a un ADN et une clé, est de tester les deux et d'exiger que les deux fonctionnent.

On a parlé à plusieurs reprises de l'ADN (Authentication Domain Name). Mais comment on obtient son ADN ? La section 7 du RFC détaille les sources d'ADN. La première est évidemment le cas où l'ADN est configuré manuellement. On pourrait imaginer, sur Unix, un /etc/resolv.conf avec une nouvelle syntaxe :

nameserver 2001:db8:53::1 adn resolver.example.net
	

Ici, on a configuré manuellement l'adresse IP et le nom (l'ADN) du résolveur. Cela convient au cas de résolveurs publics comme Quad9. Mais on pourrait imaginer des cas où seul l'ADN serait configuré quelque part, le résolveur dans /etc/resolv.conf étant rempli par DHCP, et n'étant utilisé que pour les méta-requêtes. Par exemple un (mythique, pour l'instant) /etc/tls-resolver.conf :

adn resolver.example.net
# IP address will be found via the "DHCP" DNS resolver, and checked
# with DNSSEC and/or TLS authentication
	

Troisième possibilité, l'ADN et l'adresse IP pourraient être découverts dynamiquement. Il n'existe à l'heure actuelle aucune méthode normalisée pour cela. Si on veut utiliser le profil strict, cette future méthode normalisée devra être raisonnablement sécurisée, ce qui n'est typiquement pas le cas de DHCP. On peut toujours normaliser une nouvelle option DHCP pour indiquer l'ADN mais elle ne serait, dans l'état actuel des choses, utilisable qu'avec le profil opportuniste. Bon, si vous voulez vous lancer dans ce travail, lisez bien la section 8 du RFC 7227 et la section 23 du RFC 3315 avant.

La section 11 du RFC décrit les mesures à mettre en œuvre contre deux attaques qui pourraient affaiblir la confidentialité, même si on chiffre. La première est l'analyse des tailles des requêtes et des réponses. L'accès au DNS étant public, un espion peut facilement récolter l'information sur la taille des réponses et, puisque TLS ne fait rien pour dissimuler cette taille, déduire les questions à partir des tailles. La solution recommandée contre l'attaque est le remplissage, décrit dans le RFC 7830.

Seconde attaque possible, un résolveur peut inclure l'adresse IP de son client dans ses requêtes au serveur faisant autorité (RFC 7871). Cela ne révèle pas le contenu des requêtes et des réponses, mais c'est quand même dommage pour la vie privée. Le client DNS-sur-TLS doit donc penser à mettre l'option indiquant qu'il ne veut pas qu'on fasse cela (RFC 7871, section 7.1.2).

Enfin, l'annexe A de notre RFC rappelle les dures réalités de l'Internet d'aujourd'hui : même si votre résolveur favori permet DNS-sur-TLS (c'est le cas par exemple de Quad9), le port 853 peut être bloqué par un pare-feu fasciste. Le client DNS-sur-TLS a donc intérêt à mémoriser quels résolveurs permettent DNS-sur-TLS, et depuis quels réseaux.

Pour l'instant, les nouveaux mécanismes d'authentification, et la possibilité de configurer le profil souhaité, ne semblent pas encore présents dans les logiciels, il va falloir patienter (ou programmer soi-même).

Merci à Willem Toorop pour son aide.


Téléchargez le RFC 8310


L'article seul

RFC 8308: Extension Negotiation in the Secure Shell (SSH) Protocol

Date de publication du RFC : Mars 2018
Auteur(s) du RFC : D. Bider (Bitvise Limited)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF curdle
Première rédaction de cet article le 21 mars 2018
Dernière mise à jour le 28 mars 2018


Le protocole SSH n'avait pas de mécanisme propre pour négocier des extensions au protocole de base (comme celle du RFC 4335). En outre, une partie de la négociation, par exemple des algorithmes cryptographiques, se déroulait avant le début du chiffrement et n'était donc pas protégée. Ce nouveau RFC ajoute à SSH un mécanisme pour négocier les extensions après l'échange de clés et le début de la communication sécurisée, une fois que tout est confidentiel.

Un mécanisme de négociation pour un protocole cryptographique est toujours délicat (comme TLS s'en est aperçu à plusieurs reprises). Si on n'en a pas, le client et le serveur perdent beaucoup de temps à faire des essais/erreurs « tiens, il vient de déconnecter brusquement, c'est peut-être qu'il n'aime pas SHA-512, réessayons avec SHA-1 ». Et ces deux mécanismes (négociation explicite et essai/erreur) ouvrent une nouvelle voie d'attaque, celle des attaques par repli, où l'Homme du Milieu va essayer de forcer les deux parties à utiliser des algorithmes de qualité inférieure. (La seule protection serait de ne pas discuter, de choisir des algorithmes forts et de refuser tout repli. En sécurité, ça peut aider d'être obtus.)

Notez aussi que la méthode essais/erreurs a un danger spécifique, car bien des machines SSH mettent en œuvre des mécanismes de limitation du trafic, voire de mise en liste noire, si la machine en face fait trop d'essais, ceci afin d'empêcher des attaques par force brute. D'où l'intérêt d'un vrai mécanisme de négociation (qui peut en outre permettre de détecter certaines manipulations par l'Homme du Milieu).

Vue par tshark, voici le début d'une négociation SSH, le message SSH_MSG_KEXINIT, normalisé dans le RFC 4253, section 7.1 :

SSH Protocol
    SSH Version 2
        Packet Length: 1332
        Padding Length: 5
        Key Exchange
            Message Code: Key Exchange Init (20)
            Algorithms
                Cookie: 139228cb5cfbf6c97d6b74f6ae99453d
                kex_algorithms length: 196
                kex_algorithms string: curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,ext-info-c
                server_host_key_algorithms length: 290
                server_host_key_algorithms string [truncated]: ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ecdsa-s
                encryption_algorithms_client_to_server length: 150
                encryption_algorithms_client_to_server string: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,3des-cbc
                encryption_algorithms_server_to_client length: 150
                encryption_algorithms_server_to_client string: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,3des-cbc
                mac_algorithms_client_to_server length: 213
                mac_algorithms_client_to_server string [truncated]: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-2
                mac_algorithms_server_to_client length: 213
                mac_algorithms_server_to_client string [truncated]: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-2
                compression_algorithms_client_to_server length: 26
                compression_algorithms_client_to_server string: none,zlib@openssh.com,zlib
                compression_algorithms_server_to_client length: 26
                compression_algorithms_server_to_client string: none,zlib@openssh.com,zlib
                languages_client_to_server length: 0
                languages_client_to_server string: [Empty]
                languages_server_to_client length: 0
                languages_server_to_client string: [Empty]
                KEX First Packet Follows: 0
                Reserved: 00000000
            Padding String: 0000000000
    

Le message était en clair (c'est pour cela que tshark a pu le décoder). L'autre machine envoie un message équivalent. Suite à cet échange, les deux machines sauront quels algorithmes sont disponibles.

Notre RFC 8308 (section 2.1) ajoute à ce message le nouveau mécanisme d'extension. Pour préserver la compatibilité avec les anciennes mises en œuvre de SSH, et comme il n'y a pas de place « propre » disponible dans le message, le nouveau mécanisme se place dans la liste des algorithmes cryptographiques (champ kex_algorithms, celui qui commence par curve25519-sha256@libssh.org,ecdh-sha2-nistp256…). On ajoute à cette liste ext-info-c si on est client et ext-info-s si on est serveur. (Le nom est différent dans chaque direction, pour éviter que les deux parties ne se disent « cool, cet algorithme est commun, utilisons-le ».) Vous pouvez donc savoir si votre SSH gère le nouveau mécanisme en capturant ce premier paquet SSH et en examinant la liste des algorithmes d'échange de clé. (C'était le cas ici, avec OpenSSH 7.2. Vous l'aviez repéré ?)

Une fois qu'on a proposé à son pair d'utiliser le nouveau mécanisme de ce RFC 8308, on peut s'attendre, si le pair est d'accord, à recevoir un message SSH d'un nouveau type, SSH_MSG_EXT_INFO, qui sera, lui, chiffré. Il contient la liste des extensions gérées avec ce mécanisme. Notez qu'il peut être envoyé plusieurs fois, par exemple avant et après l'authentification du client, pour le cas d'un serveur timide qui ne voulait pas révéler sa liste d'extensions avant l'authentification. Ce nouveau type de message figure désormais dans le registre des types de message, avec le numéro 7.

La section 3 définit les quatre extensions actuelles (un registre accueillera les extensions futures) :

  • L'extension server-sig-algs donne la liste des algorithmes de signature acceptés par le serveur.
  • delay-compression indique les algorithmes de compression acceptés. Il y en a deux, un du client vers le serveur et un en sens inverse. Ils étaient déjà indiqués dans le message SSH_MSG_KEXINIT, dans les champs compression_algorithms_client_to_server et compression_algorithms_server_to_client mais, cette fois, ils sont transmis dans un canal sécurisé (confidentiel et authentifié).
  • no-flow-control, dont le nom indique bien la fonction.
  • elevation sert aux systèmes d'exploitation qui ont un mécanisme d'élévation des privilèges (c'est le cas de Windows, comme documenté dans le blog de Microsoft).

Les futures extensions, après ces quatre-là, nécessiteront un examen par l'IETF, via un RFC IETF (cf. RFC 8126, politique « IETF review »). Elle seront placées dans le registre des extensions.

Quelques petits problèmes de déploiement et d'incompatibilité ont été notés avec ce nouveau mécanisme d'extensions. Par exemple OpenSSH 7.3 et 7.4 gérait l'extension server-sig-algs mais n'envoyait pas la liste complète des algorithmes acceptés. Un client qui considérait cette liste comme ferme et définitive pouvait donc renoncer à utiliser certains algorithmes qui auraient pourtant marché. Voir ce message pour une explication détaillée.

Autre gag, les valeurs des extensions peuvent contenir des octets nuls et un logiciel maladroit qui les lirait comme si c'était des chaînes de caractères en C aurait des problèmes. C'est justement ce qui est arrivé à OpenSSH, jusqu'à la version 7.5 incluse, ce qui cassait brutalement la connexion. Le RFC conseille de tester la version du pair, et de ne pas utiliser les extensions en cause si on parle à un OpenSSH ≤ 7.5.

Aujourd'hui, ce mécanisme d'extension est mis en œuvre dans OpenSSH, Bitvise SSH, AsyncSSH et SmartFTP. (Cf. ce tableau de comparaison, mais qui n'est pas forcément à jour.)

Merci à Manuel Pégourié-Gonnard pour avoir détecté une erreur dans la première version.


Téléchargez le RFC 8308


L'article seul

RFC 8307: Well-Known URIs for the WebSocket Protocol

Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : C. Bormann (Universitaet Bremen TZI)
Chemin des normes
Première rédaction de cet article le 3 janvier 2018


Il existe une norme pour un préfixe de chemin dans un URI, préfixe nommée .well-known, et après lequel plusieurs noms sont normalisés, pour des ressources « bien connues », c'est-à-dire auxquelles on peut accéder sans lien qui y mène. Le RFC 5785 normalise ce .well-known. Il n'était prévu à l'origine que pour les plans http: et https:. Ce très court RFC l'étend aux plans ws: et wss:, ceux des Web sockets du RFC 6455.

Les gens de CoAP avaient déjà étendu l'usage de .well-known en permettant (RFC 7252) qu'il soit utilisé pour les plans coap: et coaps:.

Il existe un registre IANA des suffixes (les termes après .well-known). Ce registre est le même quel que soit le plan d'URI utilisé. Il ne change donc pas suite à la publication de ce RFC.


Téléchargez le RFC 8307


L'article seul

RFC 8305: Happy Eyeballs Version 2: Better Connectivity Using Concurrency

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 :

  • L'initiateur de la connexion utilise le DNS pour demander les enregistrements A (adresse IPv4) et AAAA (IPv6).
  • Il récupère 192.0.2.1 et 2001:db8::1.
  • Il tente IPv6 (sur Linux, l'ordre des essais est réglable dans /etc/gai.conf). L'initiateur envoie un paquet TCP SYN à 2001:db8::1.
  • Pas de réponse (connexion IPv6 incorrecte). L'initiateur réessaie, deux fois, trois fois, faisant ainsi perdre de nombreuses secondes.
  • L'initiateur renonce, il passe à IPv4 et envoie un paquet TCP SYN à 192.0.2.1.
  • Le répondeur envoie un 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 :

  • L'initiateur de la connexion utilise le DNS pour demander les enregistrements A (adresse IPv4) et AAAA (IPv6).
  • Il récupère 192.0.2.1 et 2001:db8::1. Il sait donc qu'il a plusieurs adresses, de famille différente.
  • Il tente IPv6 (l'algorithme du RFC est de toute façon facilement adaptable à des cas où IPv4 est prioritaire). L'initiateur envoie un paquet TCP SYN à 2001:db8::1, avec un très court délai de garde.
  • Pas de réponse quasi-immédiate ? L'initiateur passe à IPv4 rapidement. Il envoie un paquet TCP SYN à 192.0.2.1.
  • Le répondeur envoie un 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 :

  • La façon de faire les requêtes DNS (pour tenir compte des serveurs bogués qui ne répondent pas aux requêtes AAAA, cf. RFC 4074),
  • La gestion du cas où il y a plusieurs adresses IP de la même famille (v4 ou v6),
  • La bonne façon d'utiliser les souvenirs des connexions précédentes,
  • Et la méthode (dont je n'ai pas parlé ici) pour le cas des réseaux purement IPv6, mais utilisant le NAT64 du RFC 8305.

Téléchargez le RFC 8305


L'article seul

RFC 8304: Transport Features of the User Datagram Protocol (UDP) and Lightweight UDP (UDP-Lite)

Date de publication du RFC : Février 2018
Auteur(s) du RFC : G. Fairhurst, T. Jones (University of Aberdeen)
Pour information
Réalisé dans le cadre du groupe de travail IETF taps
Première rédaction de cet article le 8 février 2018


Le RFC 8303, du groupe de travail TAPS, décrit les fonctions et services rendus par les protocoles de transport aux applications. Ce RFC 8304 se focalise sur deux protocoles sans connexion, UDP et UDP-Lite. Quels services fournissent-ils ?

UDP est normalisé dans le RFC 768 et UDP-Lite dans le RFC 3828. Tous les deux permettent l'envoi et la réception de datagrammes, sans connexion préalable, sans garantie d'être prévenu en cas de perte du datagramme, sans garantie qu'ils soient distribués dans l'ordre. Une application qui utilise UDP va donc devoir connaitre les services exacts que ce protocole fournit. Aujourd'hui, de nombreuses applications utilisent UDP, l'une des plus connues étant sans doute le DNS, sans compter celles qui utilisent un autre protocole de transport mais qui l'encapsulent dans UDP pour passer les boitiers intermédiaires (cf. par exemple RFC 8261). UDP-Lite est très proche d'UDP et ajoute juste la possibilité de distribuer à l'application des paquets potentiellement endommagés. Pour utiliser UDP intelligement, les programmeurs ont tout intérêt à consulter le RFC 8085.

L'API la plus courante est l'API dite « socket », normalisée par POSIX, et dont la meilleure documentation est évidemment le livre de Stevens. Les applications peuvent envoyer des données avec send(), sendto() et sendmsg(), en recevoir avec recvfrom() et recvmsg(). Elles peuvent être dans l'obligation de configurer certaines informations de bas niveau comme la permission de fragmenter ou pas, alors que TCP gère cela tout seul. Des options existent dans l'API socket pour définir ces options.

Notre RFC 8304 suit la démarche du RFC 8303 pour la description d'un protocole de transport. Plus précisement, il suite la première passe, celle de description de ce que sait faire le protocole.

Donc, pour résumer (section 3 du RFC), UDP est décrit dans le RFC 768, et UDP-Lite dans le RFC 3828. Tous les deux fournissent un service de datagramme, non connecté, et qui préserve les frontières de message (contrairement à TCP, qui envoie un flot d'octets). Le RFC 768 donne quelques idées sur l'API d'UDP, il faut notamment que l'application puisse écouter sur un port donné, et puisse choisir le port source d'envoi.

UDP marche évidemment également sur IPv6, et une extension à l'API socket pour IPv6 est décrite dans le RFC 3493.

UDP est très basique : aucun contrôle de congestion (le RFC 8085 détaille ce point), pas de retransmission des paquets perdus, pas de notification des pertes, etc. Tout doit être fait par l'application. On voit que, dans la grande majorité des cas, il vaut mieux utiliser TCP. Certains développeurs utilisent UDP parce qu'ils ont lu quelque part que c'était « plus rapide » mais c'est presque toujours une mauvaise idée.

Notre RFC décrit ensuite les primitives UDP utilisables par les applications :

  • CONNECT est très différent de celui de TCP. C'est une opération purement locale (aucun paquet n'est envoyé sur le réseau), qui associe des ports source et destination à une prise (socket), c'est tout. Une fois cette opération faite, on pourra envoyer ou recevoir des paquets. Comme CONNECT, CLOSE n'a d'effet que local (pas de connexion, donc pas de fermeture de connexion).
  • SEND et RECEIVE, dont le nom indique bien ce qu'elles font.
  • SET_IP_OPTIONS qui permet d'indiquer des options IP, chose qui n'est en général pas nécessaire en TCP mais peut être utile en UDP.
  • SET_DF est important car il permet de contrôler la fragmentation des paquets. TCP n'en a pas besoin car la MSS est négociée au début de la connexion. Avec UDP, c'est le programmeur de l'application qui doit choisir s'il laissera le système d'exploitation et les routeurs (en IPv4 seulement, pour les routeurs) fragmenter, ce qui est risqué, car pas mal de pare-feux mal configurés bloquent les fragments, ou bien s'il refusera la fragmentation (DF = Don't Fragment) et devra donc faire attention à ne pas envoyer des paquets trop gros. (Voir aussi les RFC 1191 et RFC 8201 pour une troisième possibilité.)
  • SET_TTL (IPv4) et SET_IPV6_UNICAST_HOPS permettent de définir le TTL des paquets IP envoyés. (Le terme de TTL est objectivement incorrect, et est devenu hops en IPv6, mais c'est le plus utilisé.)
  • SET_DSCP permet de définir la qualité de service souhaitée (RFC 2474).
  • SET_ECN permet de dire qu'on va utiliser ECN (RFC 3168). Comme les précédentes, elle n'a pas de sens en TCP, où le protocole de transport gère cela seul.
  • Enfin, dernière primitive citée par le RFC, la demande de notification des erreurs. Elle sert à réclamer qu'on soit prévenu de l'arrivée des messages ICMP.

Certaines primitives ont été exclues de la liste car n'étant plus d'actualité (cf. le RFC 6633).

Avec ça, on n'a parlé que d'UDP et pas d'UDP-Lite. Rappelons qu'UDP-Lite (RFC 3828) est à 95 % de l'UDP avec un seul changement, la possibilité que la somme de contrôle ne couvre qu'une partie du paquet. Il faut donc une primtive SET_CHECKSUM_COVERAGE qui indique jusqu'à quel octet le paquet est couvert par la somme de contrôle.

Notez enfin que le RFC se concentre sur l'unicast mais, si cela vous intéresse, l'annexe A décrit le cas du multicast.


Téléchargez le RFC 8304


L'article seul

RFC 8303: On the Usage of Transport Features Provided by IETF Transport Protocols

Date de publication du RFC : Février 2018
Auteur(s) du RFC : M. Welzl (University of Oslo), M. Tuexen (Muenster Univ. of Appl. Sciences), N. Khademi (University of Oslo)
Pour information
Réalisé dans le cadre du groupe de travail IETF taps
Première rédaction de cet article le 8 février 2018


La famille de protocoles TCP/IP dispose d'un grand nombre de protocoles de transport. On connait bien sûr TCP mais il y en a d'autres comme UDP et SCTP et d'autres encore moins connus. Cette diversité peut compliquer la tâche des développeurs d'application, qui ne savent pas toujours bien lequel choisir. Et elle pose également problème pour les développements futurs à l'IETF, par exemple la réflexion en cours sur les API. On a besoin d'une description claire des services que chaque protocole fournit effectivement. C'est le but du groupe de travail TAPS dont voici le deuxième RFC : une description de haut niveau des services que fournit la couche transport. À faire lire à tous les développeurs d'applications réseau, à tous les participants à la normalisation, et aux étudiants en réseaux informatiques.

Ce RFC parle de TCP, MPTCP, SCTP, UDP et UDP-Lite, sous l'angle « quels services rendent-ils aux applications ? » (QUIC, pas encore normalisé, n'y figure pas. LEDBAT, qui n'est pas vraiment un protocole de transport, est par contre présent.) L'idée est de lister un ensemble d'opérations abstraites, qui pourront ensuite être exportées dans une API. J'en profite pour recommander la lecture du RFC 8095, premier RFC du groupe TAPS.

La section 1 de notre RFC décrit la terminologie employée. Il faut notamment distinguer trois termes :

  • La fonction (Transport Feature) qui désigne une fonction particulière que le protocole de transport va effectuer, par exemple la confidentialité, la fiabilité de la distribution des données, le découpage des données en messages, etc.
  • Le service (Transport Service) qui est un ensemble cohérent de fonctions. C'est ce que demande l'application.
  • Le protocole (Transport Protocol) qui est une réalisation concrète d'un ou plusieurs services.

Un exemple de programme qui permet d'essayer différents types de prises (sockets) est socket-types.c. Vous pouvez le compiler et le lancer en indiquant le type de service souhaité. STREAM va donner du TCP, SEQPACKET du SCTP, DATAGRAM de l'UDP :

% make socket-types
cc -Wall -Wextra     socket-types.c   -o socket-types

% ./socket-types -v -p 853 9.9.9.9 STREAM
9.9.9.9 is an IP address
"Connection" STREAM successful to 9.9.9.9

% ./socket-types -v -p 443 -h dns.bortzmeyer.org STREAM 
Address(es) of dns.bortzmeyer.org is(are): 2605:4500:2:245b::42 204.62.14.153 
"Connection" STREAM successful to 2605:4500:2:245b::42
256 response bytes read
    

À première vue, l'API sockets nous permet de sélectionner le service, indépendamment du protocole qui le fournira. Mais ce n'est pas vraiment le cas. Si on veut juste un flot de données, cela pourrait être fait avec TCP ou SCTP, mais seul TCP sera sélectionné si on demande STREAM. En fait, les noms de services dns l'appel à socket() sont toujours en correspondance univoque avec les protocoles de transport.

Autres termes importants :

  • Adresse (Transport Address) qui est un tuple regroupant adresse IP, nom de protocole et numéro de port. Le projet TAPS considère uniquement des protocoles ayant la notion de ports, et fournissant donc la fonction de démultiplexage (différencier les données destinées à telle ou telle application).
  • Primitive (Primitive), un moyen pour l'application de demander quelque chose au protocole de transport.
  • Événement (Event), généré par le protocole de transport, et transmis à l'application pour l'informer.

La section 2 décrit le problème général. Actuellement, les applications sont liées à un protocole, même quand elles ne le souhaitent pas. Si une application veut que ses octets soient distribués dans l'ordre, et sans pertes, plusieurs protocoles peuvent convenir, et il n'est pas forcément utile de se limiter à seulement TCP, par exemple. Pour avancer vers un moindre couplage entre application et protocole, ce RFC décrit les fonctions et les services, un futur RFC définira des services minimaux, un autre peut-être une API.

Le RFC essaie de s'en tenir au minimum, des options utiles mais pas indispensables (comme le fait pour l'application de demander à être prévenue de certains changements réseau, cf. RFC 6458) ne sont pas étudiées.

Le RFC procède en trois passes : la première (section 3 du RFC) analyse et explique ce que chaque protocole permet, la seconde (section 4) liste les primitives et événements possibles, la troisième (section 5) donne pour chaque fonction les protocoles qui la mettent en œuvre. Par exemple, la passe 1 note que TCP fournit à l'application un moyen de fermer une connexion en cours, la passe 2 liste une primitive CLOSE.TCP, la passe 3 note que TCP et SCTP (mais pas UDP) fournissent cette opération de fermeture (qui fait partie des fonctions de base d'un protocole orienté connexion, comme TCP). Bon, d'accord, cette méthode est un peu longue et elle rend le RFC pas amusant à lire mais cela permet de s'assurer que rien n'a été oublié.

Donc, section 3 du RFC, la passe 1. Si vous connaissez bien tous ces protocoles, vous pouvez sauter cette section. Commençons par le protocole le plus connu, TCP (RFC 793). Tout le monde sait que c'est un protocole à connexion, fournissant un transport fiable et ordonné des données. Notez que la section 3.8 de la norme TCP décrit, sinon une API, du moins l'interface (les primitives) offertes aux applications. Vous verrez que les noms ne correspondent pas du tout à celle de l'API sockets (OPEN au lieu de connect/listen, SEND au lieu de write, etc).

TCP permet donc à l'application d'ouvrir une connexion (activement - TCP se connecte à un TCP distant - ou passivement - TCP attend les connexions), d'envoyer des données, d'en recevoir, de fermer la connexion (gentiment - TCP attend encore les données de l'autre - ou méchamment - on part en claquant la porte). Il y a aussi des événements, par exemple la fermeture d'une connexion déclenchée par le pair, ou bien une expiration d'un délai de garde. TCP permet également de spécifier la qualité de service attendue (cf. RFC 2474, et la section 4.2.4.2 du RFC 1123). Comme envoyer un paquet IP par caractère tapé serait peu efficace, TCP permet, pour les sessions interactives, genre SSH, d'attendre un peu pour voir si d'autres caractères n'arrivent pas (algorithme de Nagle, section 4.2.3.4 du RFC 1123). Et il y a encore d'autres possibilités moins connues, comme le User Timeout du RFC 5482. Je vous passe des détails, n'hésitez pas à lire le RFC pour approfondir, TCP a d'autres possibilités à offrir aux applications.

Après TCP, notre RFC se penche sur MPTCP. MPTCP crée plusieurs connexions TCP vers le pair, utilisant des adresses IP différentes, et répartit le trafic sur ces différents chemins (cf. RFC 6182). Le modèle qu'il présente aux applications est très proche de celui de TCP. MPTCP est décrit dans les RFC 6824 et son API est dans le RFC 6897. Ses primitives sont logiquement celles de TCP, plus quelques extensions. Parmi les variations par rapport à TCP, la primitive d'ouverture de connexion a évidemment une option pour activer ou pas MPTCP (sinon, on fait du TCP normal). Les extensions permettent de mieux contrôler quelles adresses IP on va utiliser pour la machine locale.

SCTP est le principal concurrent de TCP sur le créneau « protocole de transfert avec fiabilité de la distribution des données ». Il est normalisé dans le RFC 4960. Du point de vue de l'application, SCTP a trois différences importantes par rapport à TCP :

  • Au lieu d'un flot continu d'octets, SCTP présent une suite de messages, avec des délimitations entre eux,
  • Il a un mode où la distribution fiable n'est pas garantie,
  • Et il permet de tirer profit du multi-homing.

Il a aussi une terminologie différente, par exemple on parle d'associations et plus de connexions.

Certaines primitives sont donc différentes : par exemple, Associate au lieu du Open TCP. D'autres sont identiques, comme Send et Receive. SCTP fournit beaucoup de primitives, bien plus que TCP.

Et UDP ? Ce protocole sans connexion, et sans fiabilité, ainsi que sa variante UDP-Lite, est analysé dans un RFC séparé, le RFC 8304.

Nous passons ensuite à LEDBAT (RFC 6817). Ce n'est pas vraiment un protocole de transport, mais plutôt un système de contrôle de la congestion bâti sur l'idée « décroissante » de « on n'utilise le réseau que si personne d'autre ne s'en sert ». LEDBAT est pour les protocoles économes, qui veulent transférer des grandes quantités de données sans gêner personne. Un flot LEDBAT cédera donc toujours la place à un flot TCP ou compatible (RFC 5681). N'étant pas un protocole de transport, LEDBAT peut, en théorie, être utilisé sur les protocoles existants comme TCP ou SCTP (mais les extensions nécessaires n'ont hélas jamais été spécifiées). Pour l'instant, les applications n'ont pas vraiment de moyen propre d'activer ou de désactiver LEDBAT.

Bien, maintenant, passons à la deuxième passe (section 4 du RFC). Après la description des protocoles lors de la première passe, cette deuxième passe liste explicitement les primitives, rapidement décrites en première passe, et les met dans différentes catégories (alors que la première passe classait par protocole). Notons que certaines primitives, ou certaines options sont exclues. Ainsi, TCP a un mécanisme pour les données urgentes (RFC 793, section 3.7). Il était utilisé pour telnet (cf. RFC 854) mais n'a jamais vraiment fonctionné de manière satisfaisante et le RFC 6093 l'a officiellement exclu pour les nouvelles applications de TCP. Ce paramètre n'apparait donc pas dans notre RFC, puisque celui-ci vise le futur.

La première catégorie de primitives concerne la gestion de connexion. On y trouve d'abord les primitives d'établissement de connexion :

  • CONNECT.TCP : établit une connexion TCP,
  • CONNECT.SCTP : idem pour SCTP,
  • CONNECT.MPTCP : établit une connexion MPTCP,
  • CONNECT.UDP : établit une « connexion » UDP sauf, que, UDP étant sans connexion, il s'agit cette fois d'une opération purement locale, sans communication avec une autre machine.

Cette catégorie comprend aussi les primitives permettant de signaler qu'on est prêt à recevoir une connexion, LISTEN.TCP, celles de maintenance de la connexion, qui sont bien plus variées, et celles permettant de mettre fin à la connexion (comme CLOSE.TCP et CLOSE.SCTP, ou comme le signal qu'un délai de garde a été dépassé, TIMEOUT.TCP).

Dans les primitives de maintenance, on trouve aussi bien les changements du délai de garde (CHANGE_TIMEOUT.TCP et SCTP), le débrayage de l'algorithme de Nagle, pour TCP et SCTP, l'ajout d'une adresse IP locale à l'ensemble des adresses utilisables (ADD_PATH.MPTCP et ADD_PATH.SCTP), la notification d'erreurs, l'activation ou la désactivation de la somme de contrôle (UDP seulement, la somme de contrôle étant obligatoire pour les autres)… La plupart sont spécifiques à SCTP, le protocole le plus riche.

La deuxième catégorie rassemble les primitives liées à la transmission de données. On y trouve donc SEND.TCP (idem pour SCTP et UDP), RECEIVE.TCP (avec ses équivalents SCTP et UDP), mais aussi les signalements d'échec comme SEND_FAILURE.UDP par exemple quand on a reçu un message ICMP d'erreur.

Venons-en maintenant à la troisième passe (section 5 du RFC). On va cette fois lister les primitives sous forme d'un concept de haut niveau, en notant les protocoles auxquels elles s'appliquent. Ainsi, pour la gestion des connexions, on aura de quoi se connecter : une primitive Connect, qui s'applique à plusieurs protocoles (TCP, SCTP et UDP, même si Connect ne se réalise pas de la même façon pour tous ces protocoles). Et de quoi écouter passivement : une primitive Listen. Pour la maintenance, on aura une primitive Change timeout, qui s'applique à TCP et SCTP, une Disable Nagle qui s'applique à TCP et SCTP, une Add path qui a un sens pour MPTCP et SCTP, une Disable checksum qui ne marche que pour UDP, etc.

Pour l'envoi de données, on ne fusionne pas les opérations d'envoi (SEND.PROTOCOL) de la passe précédente car elles ont une sémantique trop différente. Il y a Reliably transfer data pour TCP, et Reliably transfer a message pour SCTP. (Et bien sûr Unreliably transfer a message avec UDP.)

Voilà, tout ça a l'air un peu abstrait mais ça acquerra davantage de sens quand on passera aux futures descriptions d'API.

L'annexe B du RFC explique la méthodologie suivie pour l'élaboration de ce document : coller au texte des RFC, exclure les primitives optionnelles, puis les trois passes dont j'ai parlé plus tôt. C'est cette annexe, écrite avant le RFC, qui avait servi de guide aux auteurs des différentes parties (il fallait des spécialistes SCTP pour ne pas faire d'erreurs lors de la description de ce protocole complexe…)

Notez (surtout si vous avez lu le RFC 8095) que TLS n'a pas été inclus, pas parce que ce ne serait pas un vrai protocole de transport (ça se discute) mais car il aurait été plus difficile à traiter (il aurait fallu des experts en sécurité).


Téléchargez le RFC 8303


L'article seul

RFC 8300: Network Service Header (NSH)

Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : P. Quinn (Cisco), U. Elzur (Intel), C. Pignataro (Cisco)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sfc
Première rédaction de cet article le 14 janvier 2018


Ce Network Service Header est un mécanisme concret pour faire passer sur le réseau les paquets destinés à une SF (Service Function, voir RFC 7665 pour l'architecture et les définitions). On colle un NSH, stockant plusieurs métadonnées, au paquet à traiter, on encapsule ce paquet à traiter et on l'envoie au dispositif de traitement via un réseau overlay. Et on fait l'opération inverse au retour. L'encapsulation peut se faire dans IP (par exemple avec GRE) ou dans un autre protocole.

Les métadonnées mises dans le NSH sont le résultat d'un processus de classification où le réseau décide ce qu'on va faire au paquet. Par exemple, en cas de dDoS, le classificateur décide de faire passer tous les paquets ayant telle adresse source par un équipement de filtrage plus fin, et met donc cette décision dans le NSH (section 7.1). Le NSH contient les informations nécessaires pour le SFC (Service Function Chain, RFC 7665). Sa lecture est donc très utile pour l'opérateur du réseau (elle contient la liste des traitements choisis, et cette liste peut permettre de déduire des informations sur le trafic en cours) et c'est donc une information plutôt sensible (voir aussi le RFC 8165).

Le NSH ne s'utilise qu'à l'intérieur de votre propre réseau (il n'offre, par défaut, aucune authentification et aucune confidentialité, voir section 8 du RFC). C'est à l'opérateur de prendre les mesures nécessaires, par exemple en chiffrant tout son trafic interne. Cette limitation à un seul domaine permet également de régler le problème de la fragmentation, si ennuyeux dès qu'on encapsule, ajoutant donc des octets. Au sein d'un même réseau, on peut contrôler tous les équipements et donc s'assurer que la MTU sera suffisante, ou, sinon, que la fragmentation se passera bien (section 5 du RFC).

Tout le projet SFC (dont c'est le troisième RFC) repose sur une vision de l'Internet comme étant un ensemble de middleboxes tripotant les paquets au passage, plutôt qu'étant un ensemble de machines terminales se parlant suivant le principe de bout en bout. C'est un point important à noter pour comprendre les débats au sein de l'IETF.


Téléchargez le RFC 8300


L'article seul

RFC 8297: An HTTP Status Code for Indicating Hints

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.


Téléchargez le RFC 8297


L'article seul

RFC 8289: Controlled Delay Active Queue Management

Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : K. Nichols (Pollere), V. Jacobson, A. McGregor, J. Iyengar (Google)
Expérimental
Réalisé dans le cadre du groupe de travail IETF aqm
Première rédaction de cet article le 6 janvier 2018


Ah, la gestion des files d'attentes… Le cauchemar de plein d'étudiants en informatique. Et cela ne cesse pas quand ils deviennent ingénieurs et qu'il faut construire un routeur pour connecter des réseaux de capacités différentes, et qui aura donc besoin de files d'attente. Bref, dès qu'on n'a pas assez de ressources (et on n'en aura jamais assez), il faut optimiser ses files d'attente. Ce nouveau RFC décrit le mécanisme CoDel (mis en œuvre depuis un certain temps dans le noyau Linux) qui permet notamment de limiter le terrible, l'épouvantable bufferbloat.

L'algorithme naïf pour gérer une file d'attente est le suivant (on prend le cas simple d'un routeur qui n'a que deux interfaces et une seule file d'attente dans chaque direction) : les paquets arrivent au routeur et sont mis dans la file gérée en FIFO. Dès que des ressources suffisantes sont disponibles pour envoyer un paquet (dès que l'interface de sortie est libre), on envoie le paquet et on le retire donc de la file. Si un paquet arrive quand la file est pleine, on le jette : TCP détectera cette perte, réduira son rythme d'envoi, et réémettra les données manquantes.

Avec cet algorithme simpliste, il y a une décision importante à prendre, la taille de la file. Le trafic sur l'Internet est tout sauf constant : des périodes de grand calme succèdent à des pics de trafic impressionnants. Si la file d'attente est trop petite, on ne pourra pas écluser ces pics, et on jettera des paquets. Ça tombe bien, le prix des mémoires baisse, on va pouvoir mettre des files plus grandes, non ? Eh bien non car arrive le fameux bufferbloat. Si la file est trop grande, les paquets y séjourneront très longtemps, et on augmentera ainsi la latence, au grand dam des applications sensibles comme SSH ou les jeux en ligne. Bref, on est coincés, il n'y a pas de solution idéale. Pire, si on ne jette des paquets que lorsque la file est pleine, on risque de tomber dans le cas où l'équilibre se fait avec une file d'attente toujours pleine, et donc avec une mauvaise latence.

Bien sûr, il y a longtemps que les routeurs n'utilisent plus d'algorithme aussi naïf que celui présenté ici. Tout un travail a été fait sur l'AQM : voir par exemple les RFC 7567 et RFC 8033. Mais le problème de la file d'attente toujours pleine est toujours là. Sa première description est dans le RFC 896 en 1984. Plus récemment, on le trouve mentionné dans une présentation de Jim Gettys à l'IETF 80 (« Bufferbloat: Dark Buffers in the Internet ») développé dans un article dans Communications of the ACM (Gettys a beaucoup fait pour la prise de conscience des dangers du bufferbloat.).

Résoudre le problème de l'« obésité du tampon » (bufferbloat) en réduisant la taille des files d'attentes ne serait pas une solution : les tampons sont là pour une bonne raison, pour absorber les problèmes brefs, lorsque, temporairement, on reçoit davantage de paquets que ce que l'on peut transmettre. Le fait que des files plus petites ne sont pas une solution a déjà été exposé dans le RFC 2309, dans « A Rant on Queues » de Van Jacobson, dans le rapport « RED in a Different Light » et dans l'article fondateur de CoDel, « Controlling Queue Delay » (article complet sur Sci-Hub, cherchez 10.1145/2209249.2209264). Le problème n'est pas tant la taille de la file en soi (ce n'est pas la taille qui compte), mais si c'est une « bonne » file ou une « mauvaise » file (au passage, si vous ne connaissez pas la différence entre le bon chasseur et le mauvais chasseur, ne ratez pas l'indispensable sketch des Inconnus).

CoDel est donc une vieille idée. Elle veut répondre, entre autres, aux critères suivants (section 1 du RFC) :

  • Être « zéro-configuration » (parameterless), ce qui avait été un problème fréquent de solutions comme RED. (Mon opinion personnelle est que CoDel n'est pas réellement sans configuration, comme on le voit plus loin dans le RFC, mais il est certainement « configuration minimale ».) CoDel s'ajuste tout seul, une fois défini l'ordre de grandeur du RTT des paquets qui passeront par le routeur.
  • Capable de différencier le mauvais chasseur du bon chasseur, euh, pardon, la mauvaise file d'attente de la bonne.
  • Être simple à programmer, pour fonctionner aussi bien dans les processeurs limités des routeurs low cost que dans les ASIC (rapides, mais pas très souples) des routeurs haut de gamme.

Lorsque CoDel estime nécessaire de prendre des mesures (le trafic entre trop vite), il peut jeter les paquets, ou les marquer avec ECN (RFC 3168).

La section 2 de notre RFC définit la terminologie de CoDel. Parmi les termes importants :

  • Temps de passage (sojourn time) : le temps passé par le paquet dans la file d'attente. C'est la donnée de base de CoDel, qui va essayer de minimiser ce temps de passage.
  • File persistante (standing queue) : une file d'attente qui reste pleine trop longtemps, « trop » étant de l'ordre du RTT le plus élevé parmi les flots des paquets qui attendent dans la file.

Passons maintenant à une description de haut niveau de CoDel. Son but est de différencier la mauvaise file (qui ne fait qu'ajouter du retard d'acheminement des paquets) de la bonne. Une file d'attente se forme lorsqu'il y a un goulet d'étranglement, parce qu'un lien à forte capacité se déverse dans un lien à faible capacité, ou bien parce que plusieurs liens convergent vers un lien ayant la capacité de seulement l'un d'eux. Une notion importante à ce sujet est celle de BDP, en gros le nombre d'octets en transit pour une connexion donnée, lorsque le débit atteint 100 % de la capacité. Imaginons une connexion TCP dont la fenêtre d'envoi est de 25 paquets (je sais bien que les fenêtres TCP se calculent en octets, pas en paquets, mais c'est le RFC qui fait cet abus de langage, pas moi) et où le BDP est de 20 paquets. En régime permanent, il y aura 5 paquets dans la file d'attente. Si la fenêtre est de 30 paquets, ce seront 10 paquets qui occuperont en permanence la file d'attente, augmentant encore le délai, alors que le débit ne changera pas (20 paquets arriveront par RTT). Au contraire, si on réduit la fenêtre à 20 paquets, la file d'attente sera vide, le délai sera réduit au minimum, alors que le débit n'aura pas changé. Ce résultat contre-intuitif montre que bourrer la connexion de paquets n'est pas forcément le meilleur moyen d'aller « vite ». Et que la taille de la file ne renseigne pas sur le rythme d'envoi des données. Et enfin que les files pleines en permanence n'apportent que du délai. Dans le premier exemple, la file contenant cinq paquets tout le temps était clairement une mauvaise file. Un tampon d'entrée/sortie de 20 paquets est pourtant une bonne chose (pour absorber les variations brutales) mais, s'il ne se vide pas complètement ou presque en un RTT, c'est qu'il est mal utilisé. Répétons : Les bonnes files se vident vite.

CoDel comporte trois composants : un estimateur, un objectif et une boucle de rétroaction. La section 3 de notre RFC va les présenter successivement. Pour citer l'exposé de Van Jacobson à une réunion IETF, ces trois composants sont :

  • a) Measure what you’ve got
  • b) Decide what you want
  • c) If (a) isn’t (b), move it toward (b)

D'abord, l'estimateur. C'est la partie de CoDel qui observe la file d'attente et en déduit si elle est bonne ou mauvaise. Autrefois, la principale métrique était la taille de la file d'attente. Mais celle-ci peut varier très vite, le trafic Internet étant très irrégulier. CoDel préfère donc observer le temps de séjour dans la file d'attente. C'est une information d'autant plus essentielle qu'elle a un impact direct sur le vécu de l'utilisateur, via l'augmentation de la latence.

Bon, et une fois qu'on observe cette durée de séjour, comment en déduit-on que la file est bonne ou mauvaise ? Notre RFC recommande de considérer la durée de séjour minimale. Si elle est nulle (c'est-à-dire si au moins un paquet n'a pas attendu du tout dans la file, pendant la dernière période d'observation), alors il n'y a pas de file d'attente permanente, et la file est bonne.

Le RFC parle de « période d'observation ». Quelle doit être la longueur de cette période ? Au moins un RTT pour être sûr d'écluser les pics de trafic, mais pas moins pour être sûr de détecter rapidement les mauvaises files. Le RFC recommande donc de prendre comme longueur le RTT maximal de toutes les connexions qui empruntent ce tampon d'entrée/sortie.

Et, petite astuce d'implémentation (un routeur doit aller vite, et utilise souvent des circuits de calcul plus simples qu'un processeur généraliste), on peut calculer la durée de séjour minimale avec une seule variable : le temps écoulé depuis lequel la durée de séjour est inférieure ou supérieure au seuil choisi. (Dans le pseudo-code de la section 5, et dans le noyau Linux, c'est à peu près le rôle de first_above_time.)

Si vous aimez les explications avec images, il y en a plein dans cet excellent exposé à l'IETF.

Ensuite, l'objectif (appelé également référence) : il s'agit de fixer un objectif de durée de séjour dans la file. Apparemment, zéro serait l'idéal. Mais cela entrainerait des « sur-réactions », où on jetterait des paquets (et ralentirait TCP) dès qu'une file d'attente se forme. On va plutôt utiliser un concept dû à l'inventeur du datagramme, Leonard Kleinrock, dans « An Invariant Property of Computer Network Power », la « puissance » (power). En gros, c'est le débit divisé par le délai et l'endroit idéal, sur la courbe de puissance, est en haut (le maximum de débit, pour le minimum de délai). Après une analyse que je vous épargne, le RFC recommande de se fixer comme objectif entre 5 et 10 % du RTT.

Enfin, la boucle de rétroaction. Ce n'est pas tout d'observer, il faut agir. CoDel s'appuie sur la théorie du contrôle, pour un système MIMO (Multi-Input Multi-Output). Au passage, si vous ne comprenez rien à la théorie du contrôle et notamment à la régulation PID, je vous recommande chaudement un article qui l'explique sans mathématiques. Placé à la fin de la file d'attente, au moment où on décide quoi faire des paquets, le contrôleur va les jeter (ou les marquer avec ECN) si l'objectif de durée de séjour est dépassé.

Passons maintenant à la section 4 du RFC, la plus concrète, puisqu'elle décrit précisement CoDel. L'algorithme a deux variables, TARGET et INTERVAL (ces noms sont utilisés tels quels dans le pseudo-code en section 5, et dans l'implémentation dans le noyau Linux). TARGET est l'objectif (le temps de séjour dans la file d'attente qu'on ne souhaite pas dépasser) et INTERVAL est la période d'observation. Ce dernier est également le seul paramètre de CoDel qu'il faut définir explicitement. Le RFC recommande 100 ms par défaut, ce qui couvre la plupart des RTT qu'on rencontre dans l'Internet, sauf si on parle à des gens très lointains ou si on passe par des satellites (cf. M. Dischinger, « Characterizing Residential Broadband Networks », dans les Proceedings of the Internet Measurement Conference, San Diego, en 2007, si vous voulez des mesures). Cela permet, par exemple, de vendre des routeurs bas de gamme, genre point d'accès Wifi sans imposer aux acheteurs de configurer CoDel.

Si on est dans un environnement très différent de celui d'un accès Internet « normal », il peut être nécessaire d'ajuster les valeurs (CoDel n'est donc pas réellement « parameterless »). L'annexe A du RFC en donne un exemple, pour le cas du centre de données, où les latences sont bien plus faibles (et les capacités plus grandes) ; INTERVAL peut alors être défini en microsecondes plutôt qu'en millisecondes.

TARGET, lui, va être déterminé dynamiquement par CoDel. Une valeur typique sera aux alentours de 5 ms (elle dépend évidemment de INTERVAL) : si, pendant une durée égale à INTERVAL, les paquets restent plus longtemps que cela dans la file d'attente, c'est que c'est une mauvaise file, on jette des paquets. Au passage, dans le noyau Linux, c'est dans codel_params_init que ces valeurs sont fixées :

 	params->interval = MS2TIME(100);
	params->target = MS2TIME(5);
   

Les programmeurs apprécieront la section 5, qui contient du pseudo-code, style C++, mettant en œuvre CoDel. Le choix de C++ est pour profiter de l'héritage, car CoDel est juste une dérivation d'un classique algorithme tail-drop. On peut donc le programmer sous forme d'une classe qui hérite d'une classe queue_t, plus générale.

De toute façon, si vous n'aimez pas C++, vous pouvez lire le code source du noyau Linux, qui met en œuvre CoDel depuis longtemps (fichier net/sched/sch_codel.c, également accessible via le Web).

(Pour comprendre les exemples de code, notez que packet_t* pointe vers un descripteur de paquet, incluant entre autres un champ, tstamp, qui stocke un temps, et que le type time_t est exprimé en unités qui dépendent de la résolution du système, sachant que la milliseconde est suffisante, pour du trafic Internet « habituel ».) Presque tout le travail de CoDel est fait au moment où le paquet sort de la file d'attente. À l'entrée, on se contente d'ajouter l'heure d'arrivée, puis on appelle le traitement habituel des files d'attente :

void codel_queue_t::enqueue(packet_t* pkt)
   {
       pkt->tstamp = clock();
       queue_t::enqueue(pkt);
   }       
     

Le gros du code est dans le sous-programme dodequeue qui va déterminer si le paquet individuel vient de nous faire changer d'état :


dodequeue_result codel_queue_t::dodequeue(time_t now)
   {
   ...
       // Calcul de *la* variable importante, le temps passé dans la
       // file d'attente
       time_t sojourn_time = now - r.p->tstamp;
       // S'il est inférieur à l'objectif, c'est bon
       if (sojourn_time_ < TARGET || bytes() <= maxpacket_) {
           first_above_time_ = 0;
       } else {
           // Aïe, le paquet est resté trop longtemps (par rapport à TARGET)
           if (first_above_time_ == 0) {
	       // Pas de panique, c'est peut-être récent, attendons
	       // INTERVAL avant de décider
               first_above_time_ = now + INTERVAL;
           } else if (now >= first_above_time_) {
               // La file ne se vide pas : jetons le paquet
               r.ok_to_drop = true;
           }
       }
       return r;
   }

     

Le résultat de dodequeue est ensuite utilisé par dequeue qui fait se fait quelques réflexions supplémentaires avant de jeter réellement le paquet.

Ce code est suffisamment simple pour pouvoir être mis en œuvre dans le matériel, par exemple ASIC ou NPU.

L'annexe A du RFC donne un exemple de déclinaison de CoDel pour le cas d'un centre de données, où les RTT typiques sont bien plus bas que sur l'Internet public. Cette annexe étudie également le cas où on applique CoDel aux files d'attente des serveurs, pas uniquement des routeurs, avec des résultats spectaculaires :

  • Dans le serveur, au lieu de jeter le paquet bêtement, on prévient directement TCP qu'il doit diminuer la fenêtre,
  • Le RTT est connu du serveur, et peut donc être mesuré au lieu d'être estimé (on en a besoin pour calculer INTERVAL),
  • Les paquets de pur contrôle (ACK, sans données) ne sont jamais jetés, car ils sont importants (et de petite taille, de toute façon).

Sur l'Internet public, il serait dangereux de ne jamais jeter les paquets de pur contrôle, ils pourraient être envoyés par un attaquant. Mais, dans le serveur, aucun risque.


Téléchargez le RFC 8289


L'article seul

RFC 8288: Web Linking

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 :

  • Un registre des types de lien,
  • Un mécanisme standard pour indiquer un lien dans une réponse HTTP.

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 :

  • Un IRI qui indique le contexte, c'est-à-dire la ressource de départ,
  • Le type de la connexion (détaillé en section 2.1),
  • L'IRI cible,
  • D'éventuels attributs de la cible (comme par exemple le hreflang de HTML). Ce sont des couples clé/valeur.

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),
  • Et bien d'autres encore...

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 :

  • Les enregistrements faits par le RFC 5988 sont faits, donc on ne les répète pas dans ce RFC,
  • Un registre des données spécifiques aux applications, qui n'a apparemment pas été un grand succès (il me semble qu'il n'a jamais été créé), n'est plus mentionné,
  • Quatre errata ont été pris en compte, dont un était technique et portait sur la grammaire,
  • La nouvelle annexe B donne un algorithme pour l'analyse des en-têtes,
  • Et enfin ce nouveau RFC apporte pas mal de clarifications et de détails.

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.


Téléchargez le RFC 8288


L'article seul

RFC 8280: Research into Human Rights Protocol Considerations

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.

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) :

  • Est-ce que les protocoles Internet ont des conséquences en matière de droits humains et, si oui, comment ?
  • Si la réponse à la première question est positive, est-il possible d'améliorer ces protocoles, afin de faire en sorte que les droits humains soient mieux protégés ?

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 :

  • L'IETF fait un travail purement technique. Un protocole de communication est un outil, il est neutre, comme, mettons, une voiture, qui peut servir à des gens sympas à se déplacer, ou à des méchants pour commettre un crime.
  • « Tout est politique », toute décision prise par des humains, humains insérés dans un environnement social réel, toute décision va forcément affecter la vie des autres (ou alors c'est que ces décisions n'ont servi à rien) et est donc politique. Pour citer J. Abbate, « protocol is politics by other means ».

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 » :

  • Les droits humains ne sont pas absolus, et un système technique ne peut pas comprendre cela.
  • Il y a d'autres outils que les protocoles (l'action politique classique par exemple). C'était un peu l'opinion défendue avec fougue par Milton Mueller à la réunion HRPC lors de l'IETF 99 à Prague.
  • Il est mauvais de faire des promesses qu'on ne peut pas tenir. Par exemple, on ne peut pas espérer développer de systèmes cryptographiques invulnérables, et donc on ne doit pas compter uniquement sur eux.

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 7816 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 :

  • Faire supprimer le nom auprès du registre ou du BE, comme dans les saisies faites par l'ICE, ou comme dans le cas de Wikileaks. Le protocole ne permet pas de faire la différence entre une saisie au registre, et une réelle suppression. Des systèmes comme Namecoin fournissent une meilleure protection (mais ont leurs propres défauts).
  • Si on ne peut pas peser sur le registre ou sur le BE, on peut agir lors de la résolution du nom, avec des résolveurs menteurs ou bien carrément des modifications faites dans le réseau, méthode surtout connue en Chine, mais également en Grèce. La première technique, le résolveur menteur, peut être défaite par un changement de résolveur (ce qui peut créer d'autres problèmes), la seconde attaque nécessite des solutions comme le RFC 7858.

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 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.


Téléchargez le RFC 8280


L'article seul

RFC 8274: Incident Object Description Exchange Format Usage Guidance

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.


Téléchargez le RFC 8274


L'article seul

RFC 8273: Unique IPv6 Prefix Per Host

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 3315) mais c'est plus rare, 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 :

  • Bit M à zéro (ce qui veut dire « pas d'adresse via DHCP »),
  • Bit O à un (ce qui veut dire « d'autres informations sont disponibles par DHCP, par exemple le serveur NTP à utiliser »),
  • Bit A du préfixe (RFC 4861, section 4.6.2) mis à un (ce qui veut dire « tu es autorisé à te configurer une adresse dans ce préfixe »),
  • Bit L du préfixe (RFC 4861, section 4.6.2) mis à zéro (ce qui veut dire « ce préfixe n'est pas forcément sur le lien où tu te trouves, ne suppose rien, sois gentil, passe par le routeur »).

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 4941, 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.


Téléchargez le RFC 8273


L'article seul

RFC 8272: TinyIPFIX for Smart Meters in Constrained Networks

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.


Téléchargez le RFC 8272


L'article seul

RFC 8270: Increase the Secure Shell Minimum Recommended Diffie-Hellman Modulus Size to 2048 Bits

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.


Téléchargez le RFC 8270


L'article seul

RFC 8266: Preparation, Enforcement, and Comparison of Internationalized Strings Representing Nicknames

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 :

  • Convertit tous les caractères Unicode de type « espace » (la catégorie Unicode Zs) en l'espace ASCII (U+0020),
  • Supprime les espaces en début et en fin du nickname, ce qui fait que " Thérèse" et "Thérèse" sont le même nom,
  • Fusionne toutes les suites d'espaces en un seul espace,
  • Met tout en minuscules (donc les nicknames sont insensibles à la casse),
  • Normalise en NFKC, plus violent que NFC, et réduisant donc les possibilités que deux nicknames identiques visuellement soient considérés comme distincts (cf. section 6, qui prétend à tort que ce serait un problème de sécurité ; comme souvent à l'IETF, le groupe de travail a passé beaucoup de temps sur un faux problème de « confusabilité », cf. UTS#39).

À 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.


Téléchargez le RFC 8266


L'article seul

RFC 8265: Preparation, Enforcement, and Comparison of Internationalized Strings Representing Usernames and Passwords

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 :

  • Une chaîne de caractères vide.
  • 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 :

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.


Téléchargez le RFC 8265


L'article seul

RFC 8264: PRECIS Framework: Preparation, Enforcement, and Comparison of Internationalized Strings in Application Protocols

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 :

  • PR pour la préparation des identificateurs, les opérations préliminaires comme la conversion depuis un jeu de caractères local non-Unicode,
  • E pour l'application (enforcement) des règles, afin de s'assurer qu'une chaîne de caractères Unicode est légale ou pas pour un usage donné,
  • C pour la comparaison entre les identificateurs, afin de déterminer leur égalité (« cette ressource n'est accessible qu'à l'utilisateur Thérèse or elle est connectée avec le nom therese »),
  • Le IS final étant pour Internationalized Strings.

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 :

  • Définir un petit nombre (deux, actuellement) de classes spécifiant un jeu de caractères autorisés, classes applicables à un grand nombre d'usages.
  • Définir le contenu de ces classes en fonction d'un algorithme, reposant sur les propriétés Unicode (contrairement à stringprep où le contenu des classes était énuméré dans le RFC). Ainsi, lorsqu'une nouvelle version d'Unicode sort (la version actuelle est la 10.0), il suffit de refaire tourner l'algorithme et on obtient le contenu à jour de la classe.
  • Spécifier les classes par une inclusion : tout caractère non explicitement listé comme membre de la classe est automatiquement exclu.
  • Permettre aux protocoles applicatifs de définir un profil d'une classe, à savoir une restriction de ses membres, ou bien d'autres précisions sur des sujets comme la normalisation Unicode. Il y a ainsi un profil pour les noms d'utilisateurs, un profil pour les mots de passe, etc (cf. RFC 8265). Notez qu'il est prévu que le nombre de profils reste limité, pour ne pas réintroduire l'excessive variété que voulait justement éviter PRECIS.

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) :

  • Valide (ou PVALID pour Protocol Valid),
  • Interdit,
  • Autorisé dans certains contextes seulement, c'est-à-dire que l'autorisation dépendra des caractères voisins (pas facile de trouver un exemple dans l'alphabet latin, cela sert surtout à d'autres écritures),
  • Pas encore affecté dans Unicode.

La classe IdentifierClass se définit donc par :

  • Caractères valides : les lettres et chiffres (rappelons qu'on définit des identificateurs internationalisés : on ne se limite donc pas aux lettres et chiffres latins), et les symboles traditionnels, ceux d'ASCII (comme le tiret ou le tilde).
  • Caractères valides dans certains contextes : ceux de la rare catégorie JoinControl du RFC 5892, section 2.8.
  • Caractères interdits : tout le reste (espaces, ponctuation, la plupart des symboles...).
  • Les caractères non encore affectés sont également interdits.

La classe Freeform Class se définit, elle, par :

  • Caractères valides : les lettres et chiffres (rappelons qu'on définit des identificateurs internationalisés : on ne se limite donc pas aux lettres et chiffres latins), les espaces, la ponctuation, les symboles (oui, oui, y compris les emojis comme 🥞 cf. sections 4.3.1 et 9.15).
  • Caractères valides dans certains contextes : ceux de la catégorie JoinControl plus quelques exceptions.
  • Caractères interdits : tout le reste, ce qui fait surtout les caractères de contrôle comme U+0007 (celui qui fait sonner votre ordinateur).
  • Les caractères non encore affectés sont également interdits.

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 :

  • Normalisation Unicode à utiliser. NFC est recommandée.
  • La correspondance entre les caractères et leur version large (width mapping). Est-ce que FULLWIDTH DIGIT ZERO (U+FF10) doit être considéré comme équivalent au zéro traditionnel, de largeur « normale », U+0030 ? Notez que certaines normalisations (mais qui ne sont pas celle recommandée), comme NFKC, règlent le problème. Autrement, la recommandation du RFC est « oui, il faut rendre ces caractères équivalents » car l'utilisateur serait certainement surpris que target0 et target0 soient considérés différents (le fameux POLA, principe de la moindre surprise).
  • D'autres correspondances peuvent être spécifiées par le profil (comme de transformer tous les espaces Unicode en l'espace traditionnel ASCII U+0020).
  • Changement de la casse des caractères, par exemple pour tout mettre en minuscules. Si c'est décidé pour un profil, le RFC recommande que cela soit fait avec la méthode Unicode standard, 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.
  • Interdiction de certains mélanges de caractères de directionnalité différentes. Il y a des écritures qui vont de gauche à droite et d'autres de droite à gauche, et leur combinaison peut entrainer des surprises à l'affichage. Dans certains cas, un profil peut vouloir limiter ce mélange de directionnalités.

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 :

  • Il est très difficile de distinguer tous ces espaces entre eux,
  • Certains interfaces utilisateurs risquent de ne pas les afficher, menant à confondre 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 :

  • Il est très déconseillé de créer son propre profil. Non seulement c'est plus compliqué que ça n'en a l'air, mais ça risque de dérouter les utilisateurs, si votre application a des règles légèrement différentes des règles des autres applications analogues.
  • Précisez bien quel partie de l'application va être responsable pour la préparation, l'application et la comparaison. Par exemple, le travail d'application sera-t-il fait par le client ou par le serveur ? Demandez-vous aussi à quel stade les applications devront avoir fait ce travail (par exemple, en cas de login, avant de donner un accès).
  • Définissez bien, pour chaque utilisation d'un identificateur (chaque slot, dit le RFC), quel profil doit être utilisé. Par exemple, « le nom du compte doit être conforme au profil UsernameCaseMapped de la classe IdentifierClass » (cf. RFC 8265).
  • Dressez la liste des caractères interdits (en plus de ceux déjà interdits par le profil) en fonction des spécificités de votre application. Par exemple, un @ est interdit dans la partie gauche d'une adresse de courrier électronique.

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, ˛),
  • Etc.

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()
'ᏚᎢᎵᎬᎢᎬᏒ'
    

Téléchargez le RFC 8264


L'article seul

RFC 8261: Datagram Transport Layer Security (DTLS) Encapsulation of SCTP Packets

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, 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 du RFC 4821 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) :

  • Il faut évidemment établir une session DTLS avant de tenter l'association SCTP (« association » est en gros l'équivalent de la connexion TCP),
  • On peut mettre plusieurs associations SCTP sur la même session DTLS, elles sont alors identifiées par les numéros de port utilisés,
  • Comme DTLS ne permet pas de jouer avec les adresses IP (en ajouter, en enlever, etc), on perd certaines des possibilités de SCTP, notamment le multi-homing, pourtant un des gros avantages théoriques de SCTP par rapport à TCP,
  • Pour la même raison, SCTP sur DTLS ne doit pas essayer d'indiquer aux couches inférieures des adresses IP à utiliser,
  • SCTP sur DTLS ne peut pas compter sur ICMP, qui sera traité plus bas, et doit donc se débrouiller sans lui.

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.


Téléchargez le RFC 8261


L'article seul

RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format

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 https://tls.imirhil.fr/, dont les résultats sont consultables avec un navigateur Web, mais également téléchargeables en JSON. Par exemple, 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 http://data.deutschebahn.com/dataset/data-streckennetz. C'est du GeoJSON (RFC 7946), un profil de JSON. Ici, on cherche la gare de Ratisbonne :

%  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, https://www.data.gouv.fr/fr/datasets/?format=JSON donnera tous les fichiers en JSON. Prenons au hasard les frayères du centre de la France, 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.


Téléchargez le RFC 8259


L'article seul

RFC 8257: Data Center TCP (DCTCP): TCP Congestion Control for Data Centers

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 :

  • Tampon de petite taille, pour ne pas augmenter la latence (attention au bufferbloat),
  • Tampon de grande taille pour être sûr de pouvoir toujours utiliser les liens de sortie au maximum,
  • Tampon de grande taille pour pouvoir encaisser les brusques variations de trafic, lorsqu'un flot bavard commence (par exemple une distribution d'un nouveau travail MapReduce).

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 :

  • Si les estimations de congestion sont fausses, les calculs de DCTCP seront faux. C'est particulièrement un problème en cas de perte de paquets, problème peu étudié pour l'instant.
  • Comme indiqué plus haut, DCTCP n'a aucun mécanisme pour négocier dynamiquement son utilisation. Il ne peut donc pas coexister avec le TCP traditionnel mais, pire, il ne peut pas non plus partager gentiment le réseau avec un futur mécanisme qui, lui aussi, « enrichirait » ECN. (Cf. la thèse de Midori Kato.)

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.


Téléchargez le RFC 8257


L'article seul

RFC 8255: Multiple Language Content Type

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"
    

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)) : mutt-multilingual.png

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 : gmail-multilingual.png

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: Ste