- La zone IPv6 est une notation qui permet d’identifier l’interface cible, comme dans
fe80::4%eth0, lorsque plusieurs interfaces utilisent la même portée link-local fe80::
- Dans une URL, une adresse IPv6 est entourée de crochets, comme
[fe80::4]:80, pour distinguer les deux-points de ceux du port ; en ajoutant la zone, la notation entre crochets devient [fe80::4%eth0]:80
- Le module
net/url de Go interprète ]:80 comme un escape URL invalide %et, ce qui provoque une erreur invalid URL escape
- La RFC 6874 définit un littéral IPv6 avec zone comme
IPv6address "%25" ZoneID, donc dans une URL, le % doit être percent-encodé en %25
- Pour qu’Anubis puisse pointer vers une adresse IPv6 avec zone, il faut utiliser cette notation ; cela reste un cas limite qui implique aussi la compatibilité des origin et des bibliothèques, comme le montrent les problèmes liés aux navigateurs, à nginx et à Requests
Conflit entre la zone IPv6 et la syntaxe des URL
- Les adresses link-local IPv6 peuvent exister dans la plage
fe80::whatever sur chaque interface ; ainsi, lorsqu’il y a deux interfaces réseau, il faut utiliser une scope/zone IPv6) pour distinguer une destination fe80::4
- Le format de la valeur de zone varie selon le système d’exploitation : Linux utilise le nom de l’interface, Windows l’identifiant de l’interface
- Dans l’exemple,
eth0 est le nom du périphérique Ethernet, et l’adresse s’écrit ainsi
fe80::4%eth0
- L’hôte et le port sont normalement séparés par des deux-points, mais comme les adresses IPv6 utilisent elles aussi des deux-points pour séparer les groupes hexadécimaux,
fe80::4 avec le port 80 doit être entouré de crochets comme suit
[fe80::4]:80
- En ajoutant la zone, on obtient le format suivant
[fe80::4%eth0]:80
- Si on l’insère directement dans le nom d’hôte d’une URL,
http://[fe80::4%eth0]:80 échoue dans Go net/url, car %et est interprété à tort comme un escape URL invalide
panic: parse "http://[fe80::4%eth0]:80": invalid URL escape "%et"
Solution standard et problèmes restants
- Les valeurs non conformes à la syntaxe des URL doivent utiliser le percent-encoding ;
%20 dans une URL est par exemple l’encodage d’un espace ASCII, invalide tel quel dans une URL
- Comme le
% de la zone IPv6 doit lui aussi être encodé, il faut écrire en Go http://[fe80::4%25eth0]:80 pour que Hostname() renvoie fe80::4%eth0
- La RFC 9844 fournit des recommandations pour la gestion des zones IPv6 dans les interfaces utilisateur, et la RFC 6874 définit comme suit la syntaxe d’un littéral IPv6 avec zone dans une URL
IP-literal = "[" ( IPv6address / IPv6addrz / IPvFuture ) "]"
ZoneID = 1*( unreserved / pct-encoded )
IPv6addrz = IPv6address "%25" ZoneID
- On retrouve le même cas limite dans le ticket nginx, l’issue Requests et le brouillon BCP sur les URI HTTP link-local
- Les navigateurs ne prennent actuellement pas en charge les zones IPv6, car cela casse le concept d’« origin », utilisé dans de nombreux comportements subtils ; ce brouillon tente donc de définir les origin de zone IPv6 pour permettre un usage dans les navigateurs
- Pour qu’Anubis puisse pointer vers une adresse IPv6 avec zone, le
% doit être percent-encodé et, tant que la politique reste de ne pas forker la bibliothèque standard de Go, il faut accepter la mauvaise UX de ce cas limite
1 commentaires
Commentaires sur Lobste.rs
TL;DR: les ordinateurs étaient une erreurrelève un peu du bébé jeté avec l’eau du bainLittéralement, c’est une logique de méchant d’anime à la Trigun : puisque les humains peuvent commettre des crimes atroces, il faudrait éliminer tous les humains
Je sais que c’est formulé sur le ton de la blague, mais je trouve ça intéressant, et je pense en faire le sujet de ma prochaine présentation en préparation
Cela dit, je suis d’accord sur le fond. La situation en elle-même est quand même assez ridicule
Cela dit, les cas où l’on ne gère pas correctement les adresses IPv6 link-local ne sont pas si rares
En effet,
%prend alors un sens différent uniquement dans la partie hôte de l’URL, et encore seulement si l’hôte est une adresse IPv6 entre[...]La grammaire peut toujours rester non ambiguë, mais ce n’est pas là l’essentiel. Plus on accumule ce genre de cas particuliers, plus un parseur d’URL risque d’en oublier un, et plus les différences entre parseurs peuvent laisser se glisser des bugs sales ou des problèmes de sécurité
Personnellement, je préfère qu’on gère les zones IPv6 dans les URL, mais comme il y a déjà eu à une époque des recommandations imposant d’encoder
%dans une URL, revenir en arrière introduirait aujourd’hui de vraies ambiguïtés. C’est regrettablepct-encoded = "%" HEXDIG HEXDIGIci,
HEXDIGest défini comme[a-fA-F], donc%etest analysé comme une séquence invalideLa spécification dit aussi que le caractère
%sert d’indicateur pour un octet encodé en pourcentage ; pour l’utiliser comme donnée dans un URI, il faut donc l’encoder en%25. Si une chaîne déjà décodée est décodée une nouvelle fois, un octet de donnée%peut être pris à tort pour le début d’un encodage en pourcentage ; et si une chaîne déjà encodée est réencodée, on obtient le problème inverse. L’implémentation ne doit donc pas encoder ni décoder la même chaîne plus d’une foisDonc oui, c’est agaçant, mais en pratique j’ai du mal à y voir un bug
%dans une adresse avec zone serait si terrible. Les caractères encodés sont aussi autorisés dans la partie hôte, et utiliser%tel quel dans une adresse de zone crée une ambiguïté%n’est pas lui-même un caractère non réservé, donc il est normal de l’encoder en pourcentageCe n’est pas vraiment nouveau que
net/urletnet/httpde Go entrent en conflit avec la RFC des URL. L’existence même denet/url.URL.Pathet son usage dansnet/httpsont particulièrement pénibles, car cela casse%2F.net/http.Redirectutilise aussipath.Clean, ce qui replie à tort//en/Il y a beaucoup de raisons d’avoir envie de forker les parties de la bibliothèque standard de Go qui manipulent des URL, ou de proposer quelque chose comme
net/url/v2. Mais à en juger par cet article, la gestion par Go des adresses IPv6 avec zone semble raisonnable et correcte