Normalización de direcciones IPv6 para destinos de aprobación
La normalización de direcciones IPv6 hace que los destinos de aprobación sean legibles y comparables entre URL entre corchetes, formas IPv4 mapeadas, ceros comprimidos y zonas.

Una pantalla de aprobación que muestra una cadena IPv6 sin procesar le pide a una persona que haga el trabajo de un analizador bajo presión de tiempo. Es un error de diseño. El sistema debe analizar la solicitud y convertirla en un destino tipado, rechazar ambigüedades, comparar el valor tipado y mostrar una forma estable que una persona pueda reconocer en la siguiente solicitud.
La notación IPv6 ofrece a atacantes y al software común muchas formas de hacer que el mismo destino parezca distinto: campos de ceros comprimidos, ceros iniciales, corchetes obligatorios en las URL, una terminación IPv4 integrada y zonas de interfaz. Nada de esto hace que IPv6 sea sospechoso. Sí implica que un destino de aprobación necesita un tratamiento más estricto que un campo que simplemente contiene un nombre de host.
Una dirección puede llegar con varias cadenas
Las cadenas 2001:db8:0:0:0:0:0:9, 2001:0db8::9 y 2001:db8::9 identifican la misma dirección IPv6 sin ámbito. Si tu tarjeta de aprobación guarda una cadena y una solicitud posterior proporciona otra, una comparación de cadenas dirá que son distintas. Si tu lista de permitidos acepta una grafía, pero la búsqueda de auditoría espera otra, los operadores pierden el rastro justo cuando lo necesitan.
IPv6 tiene ocho campos de 16 bits. Quienes escriben una dirección pueden omitir los ceros iniciales de cada campo y sustituir una secuencia consecutiva de campos completamente en cero por ::. El marcador :: es útil para las personas, pero elimina información sobre cuántos campos omitió quien escribió la dirección. Un analizador restaura los campos que faltan y devuelve la única representación que importa para decidir igualdad: 16 bytes de dirección.
Esta distinción se confunde con frecuencia: el texto canónico sirve para revisión, mientras que los bytes analizados sirven para comparar. La canonicalización por sí sola no autoriza nada. Solo garantiza que la forma mostrada a una persona no dependa de la grafía de quien llama.
Trata estos elementos como datos separados:
raw_input: el texto exacto del host y la sintaxis de autoridad circundante recibidos de quien llamaaddress: 16 bytes tras un análisis estrictoscope_id: un ámbito de interfaz cuando la entrada lo proporcionó de forma válidadisplay_host: una cadena canónica generada a partir de la dirección tipadaport,schemey detalles de la solicitud: campos que describen la acción real
Conserva la entrada sin procesar en el registro de auditoría. Explica qué pidió el agente. No la uses como token de comparación ni como lo único que aparece en pantalla.
Los corchetes de URI son sintaxis, no parte del host
Un literal IPv6 dentro de la autoridad de una URI debe ir entre corchetes porque los dos puntos ya separan un host de un puerto. RFC 3986 define esta forma: https://[2001:db8::9]:8443/v1/jobs. La dirección es 2001:db8::9; los corchetes indican al analizador de URI dónde termina ese host.
Esta regla provoca un fallo común. Un desarrollador pasa [2001:db8::9] a un analizador de direcciones, recibe un rechazo, elimina caracteres hasta que funciona y más tarde acepta una autoridad mal formada porque los dos analizadores ya no coinciden. La otra variante del mismo error guarda los corchetes junto a la dirección, así que un registro contiene [2001:db8::9] y otro 2001:db8::9. Nunca deberían haber llegado al mismo campo de almacenamiento.
Analiza en el límite gramatical donde llegó el valor. Para un destino HTTP, primero analiza la URI completa con un analizador de URI que respete los estándares. Extrae su host, puerto, esquema, ruta y consulta según ese analizador. Después envía solo el valor del host, sin corchetes de URI, al analizador IPv6. Para un argumento de host SSH directo, usa la gramática documentada por la interfaz de comandos SSH, sin fingir que es una URI.
El orden importa. Considera esta solicitud:
https://[2001:0db8:0:0:0:0:0:9]:8443/admin
Un resultado interno correcto tiene esta forma:
kind: ipv6
address: 20010db8000000000000000000000009
scope_id: null
display_host: 2001:db8::9
scheme: https
port: 8443
path: /admin
Ahora una interfaz de aprobación puede mostrar https://[2001:db8::9]:8443/admin. No debería reducirlo discretamente a 2001:db8::9, porque el puerto y la ruta forman parte de lo que evalúa la persona. Tampoco debería conservar la grafía con relleno de quien llama solo porque llegó primero.
Un literal sin más no lleva corchetes. Una autoridad URI con un literal IPv6 lleva corchetes. Mantén esta regla limitada y predecible.
La visualización canónica debe seguir RFC 5952
RFC 5952 recomienda hexadecimal en minúsculas, sin ceros iniciales en cada campo y :: para la secuencia consecutiva más larga de campos en cero. Cuando dos secuencias de ceros tienen la misma longitud, elige la primera. También indica que un único campo en cero no debe usar ::. Estas reglas dan un resultado estable a la forma que ven las personas.
Por ejemplo, normaliza estos valores así:
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
La recomendación del estándar es más útil de lo que parece. Ofrece a los operadores una única grafía para buscar en los registros e impide que quien llama haga que 2001:0DB8::9 parezca un destino nuevo después de que ya se aprobara 2001:db8::9.
No escribas tu propio formateador separando por dos puntos y contando cadenas vacías. La forma con terminación IPv4, la compresión doble mal formada y la sintaxis de ámbito vuelven frágil ese enfoque. Usa un analizador IPv6 probado en el lenguaje que ejecuta la acción, conserva su salida de 16 bytes y formatea a partir de esos bytes. Prueba el formateador con los ejemplos de RFC 5952 y con casos que tengan secuencias de ceros de igual longitud.
Tampoco exageres el alcance de RFC 5952. Describe una representación de texto recomendada. No decide si ::ffff:192.0.2.7 y 192.0.2.7 deben recibir la misma autorización. Es una decisión de producto con consecuencias reales.
Las formas con IPv4 mapeada necesitan una regla de comparación explícita
::ffff:192.0.2.7 es una dirección IPv6 con IPv4 mapeada. Los 32 bits finales contienen una dirección IPv4, y el patrón anterior identifica la forma mapeada. Los sistemas operativos suelen mostrar esta forma cuando una aplicación acepta conexiones IPv4 en un socket IPv6. Aparece en registros con la frecuencia suficiente como para que tratarla como un caso extraño garantice confusión más adelante.
Hay dos modelos internos defendibles. Elige uno para cada límite de autorización y documéntalo en la interfaz.
El primer modelo conserva las familias de direcciones. ::ffff:192.0.2.7 sigue siendo una dirección IPv6 de 16 bytes con tipo ipv6, mientras que 192.0.2.7 sigue siendo una dirección IPv4 de cuatro bytes con tipo ipv4. Nunca se comparan como iguales. Es la opción predeterminada más segura cuando una aprobación describe una conexión de red solicitada de una forma concreta, porque evita ampliar silenciosamente una decisión entre familias.
El segundo modelo proyecta las direcciones mapeadas a IPv4 para un caso de uso de identidad de pares definido de forma estricta. En este modelo, el analizador registra tanto la familia original como un valor embedded_ipv4. El código de comparación indica deliberadamente que una forma mapeada equivale a su dirección IPv4 integrada para este único fin. El registro de auditoría conserva la forma original y explica que la comparación utilizó una proyección.
Lo que falla es la proyección accidental. Muchas bibliotecas estándar ofrecen un método práctico que convierte una dirección mapeada en IPv4 y no devuelve ninguna distinción sobre cómo llegó. Sirve para registrar conexiones, pero es peligroso si un desarrollador lo reutiliza para un token de aprobación. Una regla escrita para 192.0.2.7 puede entonces aprobar ::ffff:192.0.2.7 sin que nadie haya decidido que debe hacerlo.
Usa pares de prueba que hagan visible la elección:
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
No trates como mapeada toda cadena IPv6 con una terminación con puntos. El analizador debe validar el prefijo completo y la posición de la parte IPv4. RFC 4291 define las direcciones IPv4 mapeadas y también permite notación compatible con IPv4 en texto IPv6. Tu formateador debe conservar suficiente información tipada para evitar llamar igual a todas las direcciones con terminación IPv4.
Los identificadores de zona pertenecen a una interfaz local
Un identificador de zona convierte una dirección con ámbito que sería ambigua en un destino local utilizable. fe80::1%en0 significa una dirección de enlace local en la interfaz llamada en0. Sin la zona, un host con más de una interfaz de red no puede saber qué enlace quiere decir quien llama.
RFC 4007 describe esto como un concepto de zona de ámbito, no como una decoración añadida a una dirección. El nombre o índice de interfaz solo tiene sentido para el host que lo resuelve. En otra máquina, en0 puede nombrar otra interfaz o ninguna. Por eso un identificador de zona es un mal destino de aprobación portátil.
Para una acción directa en un socket local, acepta una zona solo si el analizador de la plataforma la valida y la acción se ejecuta en la misma máquina. Guarda el índice numérico de interfaz normalizado como valor de comparación cuando el sistema operativo lo exponga. También puedes conservar el nombre de interfaz suministrado para la pista de auditoría, pero los nombres pueden cambiar cuando cambian el hardware y la configuración de red.
Para una URI HTTP, las reglas son más estrictas. RFC 6874 especifica que el signo de porcentaje que introduce un identificador de zona se codifica como %25 dentro del literal entre corchetes. Por tanto, una URI usa una forma como esta:
http://[fe80::1%25en0]/status
Un analizador de URI debe decodificarlo en la etapa correcta. No decodifiques por porcentaje toda la URL antes de analizarla, porque un decodificador genérico puede cambiar delimitadores y crear una URL distinta de la que proporcionó quien llama. Analiza primero la URI, extrae el literal del host y después aplica las reglas para literales con ámbito requeridas por ese componente.
La mayoría de los flujos de aprobación HTTP orientados a agentes deberían rechazar los literales con ámbito y explicar el motivo: las direcciones de enlace local solo tienen sentido con una interfaz local específica. Pedir al agente que use un nombre DNS estable, una dirección sin ámbito o un destino local configurado deliberadamente crea una aprobación que otra persona puede entender. Permite la excepción solo cuando el producto realmente opera con equipos de red locales y muestra claramente el ámbito de la interfaz.
Comparar un host abarca menos que aprobar una acción
Normalizar una dirección resuelve una clase limitada de engaño. No hace que https://[2001:db8::9]/ equivalga a https://[2001:db8::9]:9443/delete, ni hace seguro un destino SSH porque el texto del host resulte familiar.
El destino de aprobación tipado debe conservar límites que la cadena sin procesar oculta. Para HTTP, normalmente esto incluye como mínimo el esquema, el tipo y los bytes del host, el puerto tras resolver el puerto predeterminado, el método y una descripción de la solicitud que muestre la ruta. Que los encabezados y el resumen del cuerpo formen parte de la decisión depende de la acción. Si una credencial bearer puede llamar a varias API sin relación, la identidad de la credencial también debe aparecer en el aviso.
Para SSH, distingue un destino de red del comando remoto. Una aprobación para abrir una conexión SSH no describe automáticamente permiso para ejecutar sudo, modificar archivos de despliegue o reenviar un puerto. Si una herramienta puede realizar varias operaciones después de una conexión, la interfaz debe indicar qué cubre la autorización de la sesión y conservar un registro por llamada de cada comando.
Aquí es donde un modelo de datos limpio da resultados. Evita una recomendación tentadora pero equivocada: poner hosts canónicos en una lista de permitidos plana y dar el trabajo por terminado. Las listas planas de hosts son populares porque resultan fáciles de explicar. Fallan porque un host es solo una parte de una acción, y porque una respuesta DNS posterior, un puerto, una ruta o un comando pueden cambiar el efecto.
Un registro de aprobación útil puede tener este aspecto:
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
La autoridad sin procesar documenta la solicitud. Los campos tipados guían la comparación. El campo de visualización ofrece a la persona una frase estable para leer. No reutilices uno de estos campos como sustituto de los demás.
Las reglas de rechazo deben ser simples y estrictas
Un analizador debe fallar de forma cerrada cuando la entrada no encaja en la gramática correspondiente a su posición. El mensaje de error puede ser útil, pero el sistema no debe reparar una dirección adivinando lo que pretendía quien llama. Un normalizador que acepta entradas casi válidas crea una segunda gramática que los revisores de seguridad tendrán dificultades para reconstruir.
Rechaza una solicitud de literal IP si tiene alguno de estos defectos:
- más de un marcador de compresión
::o demasiados campos tras la expansión - caracteres no hexadecimales en un campo hexadecimal
- una terminación IPv4 fuera de la posición permitida por la sintaxis de texto IPv6
- corchetes pasados a un analizador de direcciones sin más, o corchetes ausentes en una autoridad URI
- un identificador de zona en un contexto donde la acción no puede vincular una interfaz local
Rechaza también un literal que tu analizador URI y tu analizador de sockets interpreten de forma diferente. No es una sutileza teórica. Diferentes bibliotecas han tomado distintas decisiones sobre la codificación porcentual, formas IPv4 inusuales y análisis de hosts con el tiempo. Que un componente apruebe un texto al que otro componente se conecta crea una brecha de autorización.
Crea un corpus pequeño de pruebas entre capas. Pasa cada caso por el mismo analizador URI, extractor de host, analizador IP, formateador, comparador y constructor de transporte que usas en producción. Para las entradas aceptadas, verifica tanto la visualización canónica como el destino real del socket. Para las rechazadas, verifica que no aparezca ningún objeto de transporte.
Un corpus compacto debería incluir ::, ::1, una dirección completamente expandida, dos secuencias de ceros de igual longitud, un literal URI entre corchetes con puerto, un valor IPv4 mapeado, una terminación con puntos mal formada, un literal de enlace local con ámbito y una zona %25 codificada en una URI. Añade cualquier forma de entrada que los agentes de tu entorno hayan producido realmente. Las pruebas de regresión basadas en aprobaciones reales son menos llamativas que los trucos de analizadores y tienen muchas más probabilidades de detectar el próximo error.
La tarjeta de aprobación debe mostrar la normalización
Una persona no puede evaluar un destino si la tarjeta oculta la parte que cambió. Muestra de forma destacada el destino canónico y luego la grafía enviada cuando difiera. Una línea discreta como Submitted as [2001:0DB8:0:0:0:0:0:9]:8443 aporta evidencia al revisor sin obligarlo a descifrar campos mentalmente.
La tarjeta también debe hacer explícitas las formas especiales. Etiqueta una dirección mapeada como IPv4-mapped IPv6 en lugar de presentarla como un literal IPv6 normal. Etiqueta una dirección con ámbito con el nombre de interfaz e indica que es local a esa interfaz. Si la política de acción proyecta una dirección mapeada a IPv4, indica esa decisión en la tarjeta. La equivalencia silenciosa es lo que sorprende a los revisores.
Para llamadas de gran consecuencia, exige que la persona apruebe la acción completa, no solo el host canónico. Una presentación compacta aún puede contener el detalle necesario:
POST https://[2001:db8::9]:8443/v1/releases
Uses credential: deploy-api
Submitted host spelling: 2001:0DB8:0:0:0:0:0:9
Sallyport sigue esta separación donde importa: el agente nunca recibe el secreto de API o SSH, mientras la app realiza la acción y devuelve su resultado. Su autorización por sesión puede establecer quién inició una ejecución, y sus controles por llamada pueden mantener visibles las acciones sensibles en vez de tratar una aprobación anterior como una autorización en blanco.
Los registros de auditoría necesitan texto sin procesar y significado normalizado
La revisión de incidentes necesita dos respuestas que a menudo entran en conflicto si guardaste una única representación: qué envió realmente el agente y qué destino usó el transporte. Guarda ambos valores. Después deja claro cuál impulsó la decisión.
Un evento de auditoría debe incluir la entrada sin procesar, el tipo de host analizado, la forma canónica mostrada, los bytes de la dirección en una codificación inequívoca, la identidad de ámbito si existe y todos los campos de acción cubiertos por la aprobación. Aplicar hash al evento después de serializarlo solo es útil si la serialización tiene una definición estable. De lo contrario, el mismo evento semántico puede producir registros distintos porque cambió un formateador.
No borres las entradas no canónicas tras derivar la forma de visualización. Pueden revelar un cliente defectuoso, un intento de elusión o una peculiaridad inofensiva de una biblioteca que después explique un incidente. Consérvalas como evidencia, pero no permitas que los paneles de búsqueda las conviertan en una segunda identidad engañosa para el mismo destino.
Los diarios Activity y Sessions de Sallyport se proyectan desde un único registro de auditoría cifrado y encadenado por hashes, y sp audit verify comprueba esa cadena sin conexión sobre texto cifrado. Esta estructura resulta especialmente útil cuando un revisor necesita distinguir una grafía extraña de una acción ejecutada diferente.
Empieza con una prueba antes de rediseñar toda la capa de aprobación: envía el mismo destino en forma completamente expandida y en formato RFC 5952. Si tu sistema produce identidades de aprobación, resultados de búsqueda de auditoría o decisiones de permiso diferentes, el límite del analizador sigue estando en el lugar equivocado.
FAQ
¿Pueden dos cadenas IPv6 referirse a la misma dirección?
No. Varias cadenas pueden representar la misma dirección de 128 bits porque IPv6 permite comprimir ceros, omitir ceros iniciales y mezclar la notación IPv6 e IPv4. Compara los bytes de dirección analizados y los datos de ámbito pertinentes, no el texto que proporcionó quien llama.
¿Por qué las direcciones IPv6 necesitan corchetes en las URL?
Usa corchetes cuando un literal IPv6 aparezca en la autoridad de una URI, como https://[2001:db8::7]:8443/. No guardes los corchetes como parte de la dirección, pertenecen a la gramática de la URI.
¿Qué es una dirección IPv6 con IPv4 mapeada?
::ffff:192.0.2.7 es una dirección IPv6 con IPv4 mapeada. Muchas API generan esta forma cuando un par IPv4 llega a través de un socket IPv6, por lo que un sistema de aprobación debe decidir deliberadamente si la compara como ese valor IPv6 o como la dirección IPv4 incorporada.
¿Debo permitir un identificador de zona en un destino de aprobación HTTP?
Por lo general, no. Un identificador de zona da a una dirección de enlace local el ámbito de su interfaz, así que solo tiene sentido en la máquina que conoce esa interfaz. Para aprobaciones HTTP remotas, pide un nombre DNS o una dirección sin ámbito y enrutable.
¿Cuál es el formato de texto IPv6 canónico?
Sí. RFC 5952 recomienda hexadecimal en minúsculas, suprimir los ceros iniciales y comprimir con :: la secuencia más larga de campos en cero, eligiendo la primera si hay empate. El texto canónico ayuda a revisar destinos, pero no sustituye la comparación a nivel de bytes.
¿Aprobar un host IPv6 aprueba todos sus puertos?
No. Un literal IPv6 entre corchetes solo representa el host dentro de la sintaxis de una URI. El puerto sigue siendo importante, y el esquema, la ruta, la consulta, el método y la identidad de la credencial pueden importar para la acción que el agente quiere aprobar.
¿Qué debe ocurrir si un destino de aprobación contiene un nombre de host?
Recházalo en un analizador de literales IP y trátalo por separado como nombre de host si tu producto admite nombres. Forzar un nombre de host a pasar por la normalización IPv6 crea comportamientos alternativos ambiguos y a menudo inseguros.
¿Debo guardar el texto original de la dirección IPv6?
Conserva la solicitud original para la pista de auditoría, pero muestra un valor canónico y compara un valor interno tipado. Estas tres representaciones responden a preguntas distintas: qué llegó, qué vio una persona y qué autorizó el sistema.
¿Puedo normalizar direcciones IPv6 mal formadas?
No. Un analizador debe rechazar corchetes mal formados, varios marcadores ::, grupos hexadecimales no válidos, una terminación IPv4 en la posición incorrecta y un identificador de zona donde la gramática circundante lo prohíbe. Considera que el desacuerdo entre analizadores es un fallo, no un motivo para adivinar.
¿Cómo debe manejar un sistema de aprobación los nombres DNS y IPv6?
Resuelve DNS por separado con reglas adecuadas para nombres de host y, después, normaliza cada dirección obtenida para el registro y la comparación. No conviertas silenciosamente un nombre de host en un único literal aprobado, porque respuestas DNS posteriores pueden cambiar el destino al que conduce ese mismo nombre.