7 min de lecture

Normaliser les adresses IPv6 pour les cibles d'approbation

La normalisation des adresses IPv6 rend les cibles d'approbation lisibles et comparables, malgré les URL entre crochets, les formes IPv4 mappées, les zéros compressés et les zones.

Normaliser les adresses IPv6 pour les cibles d'approbation

Un écran d'approbation qui affiche une chaîne IPv6 brute demande à une personne de faire le travail d'un analyseur sous pression. C'est une erreur de conception. Le système doit analyser la requête pour en faire un point de terminaison typé, rejeter toute ambiguïté, comparer la valeur typée et afficher une forme stable qu'une personne pourra reconnaître lors de la requête suivante.

La notation IPv6 donne aux attaquants comme aux logiciels ordinaires de nombreuses façons de faire paraître une même destination différente : champs nuls compressés, zéros non significatifs, crochets exigés par les URL, terminaison IPv4 intégrée et zones d'interface. Cela ne rend pas IPv6 suspect. Cela signifie qu'une cible d'approbation exige un traitement plus strict qu'un champ qui contient simplement un nom d'hôte.

Une même adresse peut arriver sous plusieurs chaînes

Les chaînes 2001:db8:0:0:0:0:0:9, 2001:0db8::9 et 2001:db8::9 désignent la même adresse IPv6 sans portée. Si votre carte d'approbation stocke une chaîne et qu'une requête ultérieure en fournit une autre, une comparaison de chaînes conclut qu'elles diffèrent. Si votre liste d'autorisations accepte une orthographe mais que la recherche dans l'audit en attend une autre, les opérateurs perdent la piste précisément lorsqu'ils en ont besoin.

IPv6 comprend huit champs de 16 bits. Les auteurs peuvent omettre les zéros non significatifs dans chaque champ, puis remplacer une suite consécutive de champs entièrement nuls par ::. Le marqueur :: est pratique pour les humains, mais il supprime l'information sur le nombre de champs omis. Un analyseur rétablit les champs manquants et renvoie la seule représentation qui compte pour décider de l'égalité : 16 octets d'adresse.

Cette distinction est souvent brouillée : le texte canonique sert à l'examen, tandis que les octets analysés servent à la comparaison. La canonicalisation ne constitue pas à elle seule une autorisation. Elle garantit simplement que la forme montrée à une personne ne dépend pas de l'orthographe de l'appelant.

Traitez les éléments suivants comme des données distinctes :

  • raw_input : le texte exact de l'hôte et la syntaxe d'autorité qui l'entoure, reçus de l'appelant
  • address : 16 octets après une analyse stricte
  • scope_id : une portée d'interface lorsque l'entrée en fournit une de façon valide
  • display_host : une chaîne canonique générée à partir de l'adresse typée
  • port, scheme et détails de la requête : les champs qui décrivent l'action réelle

Conservez l'entrée brute dans l'enregistrement d'audit. Elle explique ce que l'agent a demandé. Ne l'utilisez pas comme jeton de comparaison et ne l'affichez pas seule à l'écran.

Les crochets d'URI relèvent de la syntaxe, pas de l'hôte

Un littéral IPv6 dans l'autorité d'une URI doit être entre crochets, car les deux-points séparent déjà un hôte d'un port. RFC 3986 définit cette forme : https://[2001:db8::9]:8443/v1/jobs. L'adresse est 2001:db8::9 ; les crochets indiquent à l'analyseur d'URI où cet hôte se termine.

Cette règle entraîne un échec courant. Un développeur passe [2001:db8::9] à un analyseur d'adresses, reçoit un rejet, retire des caractères jusqu'à ce que cela fonctionne, puis accepte plus tard une autorité malformée parce que les deux analyseurs ne sont plus d'accord. L'autre version du même bug stocke les crochets avec l'adresse : un enregistrement contient [2001:db8::9], un autre 2001:db8::9. Ils n'auraient jamais dû parvenir dans le même champ de stockage.

Analysez à la frontière grammaticale où la valeur est arrivée. Pour une cible HTTP, analysez d'abord l'URI complète avec un analyseur d'URI conforme aux standards. Extrayez son hôte, son port, son schéma, son chemin et sa requête selon cet analyseur. Envoyez ensuite uniquement la valeur de l'hôte, sans crochets d'URI, à l'analyseur IPv6. Pour un argument direct d'hôte SSH, utilisez la grammaire documentée par l'interface de commande SSH au lieu de faire comme s'il s'agissait d'une URI.

L'ordre compte. Considérez cette requête :

https://[2001:0db8:0:0:0:0:0:9]:8443/admin

Un résultat interne correct prend cette forme :

kind: ipv6
address: 20010db8000000000000000000000009
scope_id: null
display_host: 2001:db8::9
scheme: https
port: 8443
path: /admin

Une interface d'approbation peut maintenant afficher https://[2001:db8::9]:8443/admin. Elle ne doit pas la réduire discrètement à 2001:db8::9, car le port et le chemin font partie de ce que l'utilisateur évalue. Inversement, elle ne doit pas conserver l'orthographe avec zéros ajoutés de l'appelant simplement parce qu'elle est arrivée en premier.

Un littéral seul n'a pas de crochets. Une autorité d'URI avec un littéral IPv6 a des crochets. Gardez cette règle limitée et prévisible.

L'affichage canonique doit suivre RFC 5952

RFC 5952 recommande des chiffres hexadécimaux minuscules, aucun zéro non significatif dans un champ et :: pour la plus longue suite consécutive de champs nuls. Lorsque deux suites de zéros ont la même longueur, il choisit la première. Il précise aussi qu'un seul champ nul ne doit pas utiliser ::. Ces règles donnent à la forme destinée aux humains un résultat stable.

Par exemple, normalisez ces valeurs ainsi :

2001:0DB8:0000:0000:0000:0000:0000:0009  ->  2001:db8::9
2001:db8:0:1:0:0:0:1                    ->  2001:db8:0:1::1
2001:db8:0:1:0:0:0:0                    ->  2001:db8:0:1::
2001:db8:0:1:0:0:0:2                    ->  2001:db8:0:1::2
0:0:0:0:0:0:0:1                         ->  ::1

Les recommandations de la norme sont plus utiles qu'elles n'en ont l'air. Elles donnent aux opérateurs une orthographe unique à rechercher dans les journaux et empêchent un appelant de faire passer 2001:0DB8::9 pour une nouvelle destination après l'approbation de 2001:db8::9.

N'écrivez pas votre propre formateur en découpant sur les deux-points et en comptant les chaînes vides. La forme avec terminaison IPv4, la double compression malformée et la syntaxe de portée rendent cette approche fragile. Utilisez un analyseur IPv6 éprouvé dans le langage qui exécute l'action, conservez sa sortie de 16 octets et formatez à partir de ces octets. Testez le formateur avec les exemples de RFC 5952, ainsi que des cas ayant des suites de zéros de même longueur.

Ne surinterprétez pas non plus RFC 5952. Il décrit une représentation textuelle recommandée. Il ne décide pas si ::ffff:192.0.2.7 et 192.0.2.7 doivent recevoir la même autorisation. C'est une décision produit aux conséquences réelles.

Les formes mappées IPv4 exigent une règle de comparaison explicite

::ffff:192.0.2.7 est une adresse IPv6 mappée IPv4. Les 32 derniers bits portent une adresse IPv4 et le motif qui précède identifie la forme mappée. Les systèmes d'exploitation exposent souvent cette forme lorsqu'une application accepte des connexions IPv4 sur un socket IPv6. Elle apparaît assez souvent dans les journaux pour que la traiter comme un cas limite étrange garantisse une confusion ultérieure.

Deux modèles internes se défendent. Choisissez-en un pour chaque frontière d'autorisation et documentez-le dans l'interface.

Le premier modèle conserve les familles d'adresses. ::ffff:192.0.2.7 reste une adresse IPv6 de 16 octets avec le type ipv6, tandis que 192.0.2.7 reste une adresse IPv4 de quatre octets avec le type ipv4. Elles ne sont jamais égales. C'est le choix le plus sûr par défaut lorsqu'une approbation décrit une connexion réseau demandée dans une forme particulière, car il évite d'élargir silencieusement une décision entre familles.

Le second modèle projette les adresses mappées en IPv4 pour un cas d'usage d'identité de pair défini de façon restrictive. Dans ce modèle, l'analyseur enregistre à la fois la famille d'origine et une valeur embedded_ipv4. Le code de comparaison indique délibérément qu'une forme mappée est égale à son adresse IPv4 intégrée pour ce seul usage. L'enregistrement d'audit conserve malgré tout la forme brute et précise que la comparaison a utilisé une projection.

Ce qui échoue, c'est la projection accidentelle. De nombreuses bibliothèques standard offrent une méthode pratique qui transforme une adresse mappée en IPv4 sans indiquer comment elle est arrivée. C'est utile pour journaliser les connexions ; c'est dangereux si un développeur la réutilise pour un jeton d'approbation. Une règle écrite pour 192.0.2.7 peut alors approuver ::ffff:192.0.2.7 sans que personne ait décidé qu'elle le devait.

Utilisez des paires de test qui rendent le choix visible :

input A: 192.0.2.7
input B: ::ffff:192.0.2.7

strict endpoint comparison: different
explicit peer-identity projection: same IPv4 peer, if the product says so

Ne traitez pas toute chaîne IPv6 avec une terminaison pointée comme une adresse mappée. L'analyseur doit valider le préfixe complet et la position de la partie IPv4. RFC 4291 définit les adresses IPv4 mappées et autorise aussi la notation IPv4 compatible dans le texte IPv6. Votre formateur doit conserver assez d'informations typées pour éviter d'appeler toutes les adresses à terminaison pointée la même chose.

Les identifiants de zone appartiennent à une interface locale

Garder les secrets hors des prompts
Son coffre-fort chiffré conserve les clés API et SSH dans l'app Mac, pas dans le contexte de l'agent.

Un identifiant de zone transforme une adresse à portée limitée par ailleurs ambiguë en destination locale utilisable. fe80::1%en0 signifie une adresse link-local sur l'interface nommée en0. Sans la zone, un hôte doté de plusieurs interfaces réseau ne peut pas savoir quel lien l'appelant désigne.

RFC 4007 décrit cela comme un concept de zone de portée, pas comme une décoration ajoutée à une adresse. Le nom ou l'index d'interface n'a de sens que pour l'hôte qui le résout. Sur une autre machine, en0 peut nommer une autre interface ou aucune. Un identifiant de zone est donc une mauvaise cible d'approbation portable.

Pour une action directe sur un socket local, acceptez une zone uniquement si l'analyseur de la plateforme la valide et si l'action s'exécute sur la même machine. Stockez l'index numérique d'interface normalisé comme valeur de comparaison lorsque le système d'exploitation l'expose. Vous pouvez aussi conserver le nom d'interface fourni pour la piste d'audit, mais les noms peuvent changer avec le matériel et la configuration réseau.

Pour une URI HTTP, les règles sont plus strictes. RFC 6874 précise que le signe pourcent qui introduit un identifiant de zone est encodé en %25 dans le littéral entre crochets. Une URI utilise donc une forme comme celle-ci :

http://[fe80::1%25en0]/status

Un analyseur d'URI doit décoder cela au bon stade. Ne décodez pas en pourcentage l'URL entière avant de l'analyser, car un décodeur générique peut modifier des délimiteurs et produire une URL différente de celle fournie par l'appelant. Analysez d'abord l'URI, extrayez le littéral d'hôte, puis appliquez les règles de littéral à portée requises pour ce composant.

La plupart des flux d'approbation HTTP destinés aux agents devraient rejeter les littéraux à portée et expliquer pourquoi : les adresses link-local n'ont de sens qu'avec une interface locale précise. Demander à l'agent d'utiliser un nom DNS stable, une adresse sans portée ou un point de terminaison local délibérément configuré crée une approbation qu'une autre personne peut comprendre. Autorisez l'exception uniquement lorsque le produit agit réellement sur des équipements réseau locaux et affiche clairement la portée de l'interface.

La comparaison d'un hôte est plus restreinte que l'approbation d'une action

Normaliser une adresse corrige une catégorie limitée de tromperie. Cela ne rend pas https://[2001:db8::9]/ équivalent à https://[2001:db8::9]:9443/delete, et ne rend pas une destination SSH sûre parce que le texte de l'hôte semble familier.

La cible d'approbation typée doit préserver les frontières que la chaîne brute masque. Pour HTTP, cela inclut généralement au minimum le schéma, le type et les octets de l'hôte, le port après résolution du port par défaut, la méthode et une description de requête qui expose le chemin. La présence des en-têtes et du condensat du corps dans la décision dépend de l'action. Si un même jeton bearer peut appeler plusieurs API sans rapport, l'identité de l'identifiant doit aussi apparaître dans le prompt.

Pour SSH, distinguez un point de terminaison réseau de la commande distante. L'approbation d'ouvrir une connexion SSH ne décrit pas automatiquement l'autorisation d'exécuter sudo, de modifier des fichiers de déploiement ou de transférer un port. Si un outil peut effectuer plusieurs opérations après une connexion, l'interface doit indiquer ce que couvre l'autorisation de session et conserver un enregistrement de chaque commande.

C'est là qu'un modèle de données clair porte ses fruits. Il évite une recommandation tentante mais erronée : mettre des hôtes canoniques dans une liste d'autorisations plate et considérer le travail terminé. Les listes d'hôtes plates sont populaires parce qu'elles sont faciles à expliquer. Elles échouent parce qu'un hôte n'est qu'une partie d'une action et parce qu'une réponse DNS, un port, un chemin ou une commande ultérieurs peuvent modifier l'effet.

Un enregistrement d'approbation utile peut ressembler à ceci :

transport: https
host_kind: ipv6
host_bytes: 20010db8000000000000000000000009
host_display: 2001:db8::9
scope_id: null
port: 8443
method: POST
path: /v1/releases
credential_ref: deploy-api
raw_authority: [2001:0db8::9]:8443

L'autorité brute documente la requête. Les champs typés déterminent la comparaison. Le champ d'affichage donne à la personne une formulation stable à lire. Ne réutilisez aucun de ces champs comme substitut aux autres.

Les règles de rejet doivent être simples et strictes

Approuver l'action HTTP complète
Sallyport exécute lui-même l'appel HTTP, l'agent ne reçoit donc jamais le jeton d'accès à l'API.

Un analyseur doit échouer de façon sûre lorsque l'entrée ne correspond pas à la grammaire de sa position. Le message d'erreur peut être utile, mais le système ne doit pas réparer une adresse en devinant l'intention de l'appelant. Un normaliseur qui accepte des entrées presque valides crée une seconde grammaire que les relecteurs de sécurité auront du mal à reconstituer.

Rejetez une requête de littéral IP si elle présente l'un de ces défauts :

  • plus d'un marqueur de compression :: ou trop de champs après expansion
  • des caractères non hexadécimaux dans un champ hexadécimal
  • une terminaison IPv4 hors de la position autorisée par la syntaxe textuelle IPv6
  • des crochets transmis à un analyseur d'adresse nue, ou des crochets absents dans une autorité d'URI
  • un identifiant de zone dans un contexte où l'action ne peut pas se lier à une interface locale

Rejetez aussi un littéral que votre analyseur d'URI et votre analyseur de socket interprètent différemment. Ce n'est pas un scrupule théorique. Selon les périodes, différentes bibliothèques ont fait des choix différents pour l'encodage en pourcentage, les formes IPv4 inhabituelles et l'analyse d'hôtes. Lorsqu'un composant approuve un texte auquel un autre se connecte, il existe une faille d'autorisation.

Constituez un petit corpus de tests inter-couches. Faites passer chaque cas par le même analyseur d'URI, extracteur d'hôte, analyseur IP, formateur, comparateur et constructeur de transport qu'en production. Pour une entrée acceptée, vérifiez à la fois l'affichage canonique et la destination réelle du socket. Pour une entrée rejetée, vérifiez qu'aucun objet de transport n'apparaît.

Un corpus compact doit comprendre ::, ::1, une adresse entièrement développée, deux suites de zéros de même longueur, un littéral d'URI entre crochets avec un port, une valeur IPv4 mappée, une terminaison pointée malformée, un littéral link-local à portée et une zone %25 encodée dans une URI. Ajoutez toute forme d'entrée que les agents de votre environnement ont réellement produite. Les tests de régression tirés d'approbations réelles sont moins spectaculaires que les astuces d'analyseur, mais ils ont bien plus de chances de détecter la prochaine erreur.

La carte d'approbation doit exposer la normalisation

Conserver chaque appel exécuté
Le journal Activity enregistre chaque appel et conserve les éléments de preuve avec le résultat de l'action.

Une personne ne peut pas évaluer un point de terminaison si la carte masque la partie qui a changé. Affichez la cible canonique de façon visible, puis l'orthographe soumise lorsqu'elle diffère. Une ligne discrète comme Soumis sous la forme [2001:0DB8:0:0:0:0:0:9]:8443 donne au relecteur des éléments de preuve sans l'obliger à décoder les champs mentalement.

La carte doit aussi rendre les formes spéciales explicites. Étiquetez une adresse mappée comme IPv6 mappée IPv4 au lieu de la présenter comme un littéral IPv6 ordinaire. Étiquetez une adresse à portée avec le nom de l'interface et précisez qu'elle est locale à cette interface. Si la politique d'action projette une adresse mappée en IPv4, indiquez cette décision sur la carte. Les équivalences silencieuses surprennent les relecteurs.

Pour les appels aux conséquences importantes, exigez que la personne approuve l'action complète, pas seulement l'hôte canonique. Une présentation compacte peut tout de même contenir les détails nécessaires :

POST https://[2001:db8::9]:8443/v1/releases
Uses credential: deploy-api
Submitted host spelling: 2001:0DB8:0:0:0:0:0:9

Sallyport applique cette séparation là où elle compte : l'agent ne reçoit jamais le secret API ou SSH, tandis que l'app exécute l'action et renvoie son résultat. Son autorisation par session peut établir qui a lancé une exécution, et ses contrôles par appel peuvent garder les actions sensibles visibles au lieu de considérer une approbation antérieure comme un chèque en blanc.

Les enregistrements d'audit ont besoin du texte brut et du sens normalisé

L'examen d'un incident exige deux réponses qui entrent souvent en conflit si vous n'avez enregistré qu'une seule représentation : qu'a réellement envoyé l'agent et quelle destination le transport a-t-il utilisée ? Enregistrez les deux. Indiquez ensuite clairement quelle valeur a déterminé la décision.

Un événement d'audit doit inclure l'entrée brute, le type d'hôte analysé, la forme d'affichage canonique, les octets d'adresse dans un encodage sans ambiguïté, l'identité de portée lorsqu'elle est présente et tous les champs d'action couverts par l'approbation. Hacher l'événement après sérialisation n'est utile que si la sérialisation a une définition stable. Sinon, le même événement sémantique peut produire des enregistrements différents parce qu'un formateur a changé.

N'effacez pas une entrée non canonique après avoir dérivé la forme d'affichage. Elle peut révéler un client bogué, une tentative de contournement ou une particularité inoffensive de bibliothèque qui expliquera plus tard un incident. Conservez-la comme élément de preuve, mais ne laissez pas les tableaux de bord de recherche en faire une seconde identité trompeuse pour le même point de terminaison.

Les journaux Activity et Sessions de Sallyport sont générés à partir d'un même journal d'audit chiffré et chaîné par hachage, et sp audit verify vérifie cette chaîne hors ligne sur le texte chiffré. Cette structure est particulièrement utile lorsqu'un relecteur doit distinguer une orthographe inhabituelle d'une action exécutée différente.

Commencez par un test avant de repenser toute la couche d'approbation : soumettez la même cible sous une forme entièrement développée puis sous la forme RFC 5952. Si votre système produit des identités d'approbation, des résultats de recherche d'audit ou des décisions d'autorisation différents, la frontière de l'analyseur est encore au mauvais endroit.

FAQ

Deux chaînes IPv6 peuvent-elles désigner la même adresse ?

Non. Plusieurs chaînes peuvent désigner la même adresse de 128 bits, car IPv6 autorise la compression des zéros, l'omission des zéros non significatifs et une notation IPv6 et IPv4 mixte. Comparez les octets d'adresse analysés et les données de portée pertinentes, pas le texte fourni par l'appelant.

Pourquoi les adresses IPv6 ont-elles besoin de crochets dans les URL ?

Utilisez des crochets lorsqu'un littéral IPv6 apparaît dans l'autorité d'une URI, par exemple https://[2001:db8::7]:8443/. Ne stockez pas les crochets comme partie intégrante de l'adresse, ils relèvent de la grammaire de l'URI.

Qu'est-ce qu'une adresse IPv6 mappée IPv4 ?

::ffff:192.0.2.7 est une adresse IPv6 mappée IPv4. De nombreuses API la produisent lorsqu'un pair IPv4 arrive via un socket IPv6. Un système d'approbation doit donc décider explicitement s'il la compare comme cette valeur IPv6 ou comme l'adresse IPv4 intégrée.

Dois-je autoriser un identifiant de zone dans une cible d'approbation HTTP ?

En général, non. Un identifiant de zone donne à une adresse link-local la portée de son interface, il n'a donc de sens que sur la machine qui connaît cette interface. Pour les approbations HTTP distantes, demandez un nom DNS ou une adresse non limitée à une interface et routable.

Quel est le format texte canonique d'une adresse IPv6 ?

Oui. RFC 5952 recommande des chiffres hexadécimaux minuscules, la suppression des zéros non significatifs et la compression avec :: de la plus longue suite de champs nuls, en choisissant la première en cas d'égalité. Le texte canonique aide les personnes à examiner les cibles, mais ne remplace pas une comparaison au niveau des octets.

L'approbation d'un hôte IPv6 autorise-t-elle tous ses ports ?

Non. Un littéral IPv6 entre crochets n'est qu'une représentation de l'hôte dans la syntaxe d'une URI. Le port compte toujours, et le schéma, le chemin, la requête, la méthode et l'identité de l'identifiant peuvent compter pour l'action que l'agent demande à faire approuver.

Que doit-il se passer si une cible d'approbation contient un nom d'hôte ?

Rejetez-le pour un analyseur de littéraux IP, puis traitez-le séparément comme nom d'hôte si votre produit prend en charge les noms. Faire passer un nom d'hôte par la normalisation IPv6 crée des comportements de repli ambigus et souvent dangereux.

Dois-je stocker le texte original de l'adresse IPv6 ?

Conservez la requête d'origine dans la piste d'audit, mais affichez une valeur canonique et comparez une valeur interne typée. Ces trois représentations répondent à des questions différentes : ce qui est arrivé, ce qu'une personne a vu et ce que le système a autorisé.

Puis-je normaliser des adresses IPv6 malformées ?

Non. Un analyseur doit rejeter des crochets mal placés, plusieurs marqueurs ::, des groupes hexadécimaux invalides, une terminaison IPv4 à la mauvaise position et un identifiant de zone interdit par la grammaire environnante. Considérez un désaccord entre analyseurs comme un échec, pas comme une raison de deviner.

Comment un système d'approbation doit-il gérer les noms DNS et IPv6 ?

Résolvez le DNS séparément selon des règles adaptées aux noms d'hôte, puis normalisez chaque adresse renvoyée pour la journalisation et la comparaison. Ne transformez pas silencieusement un nom d'hôte en un littéral approuvé, car de futures réponses DNS peuvent changer la destination de ce même nom.

Sallyport

Sallyport exécute les appels d'API et les commandes SSH à la place de votre agent IA. Les clés restent dans un coffre-fort local sur votre Mac ; vous approuvez chaque exécution et chaque action est consignée dans un journal scellé.

© 2026 Sallyport · Open source sous Apache-2.0 · Oleg Sotnikov