Normalização de endereços IPv6 para destinos de aprovação
A normalização de endereços IPv6 torna destinos de aprovação legíveis e comparáveis entre URLs com colchetes, formas IPv4 mapeadas, zeros comprimidos e zonas.

Uma tela de aprovação que mostra uma string IPv6 bruta pede que uma pessoa faça o trabalho de um analisador sob pressão de tempo. Isso é um erro de design. O sistema precisa analisar a solicitação e convertê-la em um endpoint tipado, rejeitar ambiguidades, comparar o valor tipado e exibir uma forma estável que a pessoa reconheça na próxima solicitação.
A notação IPv6 oferece a invasores e a softwares comuns muitas maneiras de fazer o mesmo destino parecer diferente: campos zero comprimidos, zeros à esquerda, colchetes exigidos por URLs, uma terminação IPv4 incorporada e zonas de interface. Nada disso torna o IPv6 suspeito. Mas um destino de aprovação exige um tratamento mais rigoroso que um campo que por acaso contém um nome de host.
Um endereço pode chegar em várias strings
As strings 2001:db8:0:0:0:0:0:9, 2001:0db8::9 e 2001:db8::9 identificam o mesmo endereço IPv6 sem escopo. Se seu cartão de aprovação armazena uma string e uma solicitação posterior fornece outra, uma comparação de strings diz que elas são diferentes. Se sua lista de permissões aceita uma grafia, mas a busca de auditoria espera outra, as equipes perdem o rastro justamente quando precisam dele.
O IPv6 tem oito campos de 16 bits. Quem escreve pode omitir zeros à esquerda em cada campo e depois substituir uma sequência consecutiva de campos formados só por zeros por ::. O marcador :: é conveniente para humanos, mas elimina a informação sobre quantos campos foram omitidos. Um analisador restaura os campos ausentes e retorna a única representação que importa para decidir igualdade: 16 bytes de endereço.
Essa distinção costuma ser confundida: texto canônico serve para revisão, enquanto bytes analisados servem para comparação. A canonização, por si só, não é autorização. Ela apenas garante que a forma mostrada a uma pessoa não dependa da grafia usada por quem chamou.
Trate estes itens como dados separados:
raw_input: o texto exato do host e a sintaxe de autoridade ao redor recebidos de quem fez a chamadaaddress: 16 bytes após uma análise rigorosascope_id: um escopo de interface quando a entrada o forneceu de forma válidadisplay_host: uma string canônica gerada a partir do endereço tipadoport,schemee detalhes da solicitação: campos que descrevem a ação real
Mantenha a entrada bruta no registro de auditoria. Ela explica o que o agente pediu. Não a use como token de comparação e não deixe que seja a única informação na tela.
Colchetes em URI são sintaxe, não parte do host
Um literal IPv6 dentro da autoridade de uma URI precisa estar entre colchetes porque os dois-pontos já separam um host de uma porta. O RFC 3986 define a forma: https://[2001:db8::9]:8443/v1/jobs. O endereço é 2001:db8::9; os colchetes informam ao analisador de URI onde esse host termina.
Essa regra produz uma falha comum. Uma pessoa desenvolvedora passa [2001:db8::9] a um analisador de endereços, recebe uma rejeição, remove caracteres até funcionar e, mais tarde, aceita uma autoridade malformada porque os dois analisadores deixaram de concordar. A outra versão do mesmo erro armazena os colchetes junto ao endereço, de modo que um registro tem [2001:db8::9] e outro tem 2001:db8::9. Eles nunca deveriam ter chegado ao mesmo campo de armazenamento.
Analise na fronteira gramatical em que o valor chegou. Para um destino HTTP, primeiro analise a URI completa com um analisador de URI compatível com os padrões. Extraia host, porta, esquema, caminho e consulta de acordo com esse analisador. Em seguida, envie ao analisador IPv6 apenas o valor do host, sem colchetes de URI. Para um argumento direto de host SSH, use a gramática documentada pela interface de comando SSH, em vez de fingir que ele é uma URI.
A ordem importa. Considere esta solicitação:
https://[2001:0db8:0:0:0:0:0:9]:8443/admin
Um resultado interno correto tem este formato:
kind: ipv6
address: 20010db8000000000000000000000009
scope_id: null
display_host: 2001:db8::9
scheme: https
port: 8443
path: /admin
Agora, uma interface de aprovação pode mostrar https://[2001:db8::9]:8443/admin. Ela não deve reduzir isso silenciosamente a 2001:db8::9, pois a porta e o caminho fazem parte do que a pessoa avalia. Da mesma forma, não deve preservar a grafia preenchida com zeros só porque ela chegou primeiro.
Um literal isolado não tem colchetes. Uma autoridade URI com um literal IPv6 tem colchetes. Mantenha essa regra limitada e previsível.
A exibição canônica deve seguir o RFC 5952
O RFC 5952 recomenda hexadecimal em minúsculas, sem zeros à esquerda em um campo e :: para a maior sequência consecutiva de campos zero. Quando duas sequências de zeros têm o mesmo tamanho, ele escolhe a primeira. Também diz que um único campo zero não deve usar ::. Essas regras dão à forma voltada a pessoas um resultado estável.
Por exemplo, normalize estes valores assim:
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
A recomendação do padrão é mais útil do que parece. Ela dá às equipes uma única grafia para buscar nos logs e impede que alguém faça 2001:0DB8::9 parecer um destino novo depois que 2001:db8::9 já foi aprovado.
Não escreva seu próprio formatador dividindo por dois-pontos e contando strings vazias. A forma com terminação IPv4, a compressão dupla malformada e a sintaxe de escopo tornam essa abordagem frágil. Use um analisador IPv6 testado na linguagem que executa a ação, retenha sua saída de 16 bytes e formate a partir desses bytes. Teste o formatador com exemplos do RFC 5952 e casos com sequências de zeros de mesmo tamanho.
Também não atribua ao RFC 5952 mais do que ele diz. Ele descreve uma representação textual recomendada. Não decide se ::ffff:192.0.2.7 e 192.0.2.7 devem receber a mesma autorização. Essa é uma decisão de produto com consequências reais.
Formas mapeadas para IPv4 precisam de uma regra explícita de comparação
::ffff:192.0.2.7 é um endereço IPv6 mapeado para IPv4. Os 32 bits finais carregam um endereço IPv4, e o padrão anterior identifica a forma mapeada. Sistemas operacionais costumam expor essa forma quando uma aplicação aceita conexões IPv4 em um socket IPv6. Ela aparece em logs com frequência suficiente para que tratá-la como um caso extremo estranho garanta confusão mais tarde.
Há dois modelos internos defensáveis. Escolha um para cada fronteira de autorização e documente-o na interface.
O primeiro modelo preserva as famílias de endereços. ::ffff:192.0.2.7 continua sendo um endereço IPv6 de 16 bytes com tipo ipv6, enquanto 192.0.2.7 continua sendo um endereço IPv4 de quatro bytes com tipo ipv4. Eles nunca se comparam como iguais. Esse é o padrão mais seguro quando uma aprovação descreve uma conexão de rede solicitada em uma forma específica, pois evita ampliar silenciosamente uma decisão entre famílias.
O segundo modelo projeta endereços mapeados para IPv4 em um caso de uso de identidade de par estritamente definido. Nele, o analisador registra tanto a família original quanto um valor embedded_ipv4. O código de comparação afirma deliberadamente que uma forma mapeada equivale ao seu endereço IPv4 incorporado para essa única finalidade. O registro de auditoria ainda preserva a forma bruta e explica que a comparação usou uma projeção.
O que falha é a projeção acidental. Muitas bibliotecas padrão oferecem um método conveniente que converte um endereço mapeado em IPv4 e não informa como ele chegou. Isso é útil para registrar conexões, mas é perigoso se alguém reutilizá-lo para um token de aprovação. Uma regra escrita para 192.0.2.7 pode então aprovar ::ffff:192.0.2.7 sem que ninguém tenha decidido que deveria.
Use pares de teste que deixem a escolha visível:
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
Não trate toda string IPv6 com uma terminação pontuada como mapeada. O analisador precisa validar o prefixo completo e a posição da parte IPv4. O RFC 4291 define endereços IPv4 mapeados e também permite notação compatível com IPv4 no texto IPv6. Seu formatador deve preservar informação tipada suficiente para não chamar todos os endereços com terminação pontuada de mesma coisa.
Identificadores de zona pertencem a uma interface local
Um identificador de zona transforma um endereço com escopo que seria ambíguo em um destino local utilizável. fe80::1%en0 significa um endereço local de enlace na interface chamada en0. Sem a zona, um host com mais de uma interface de rede não consegue saber a qual enlace quem chamou se refere.
O RFC 4007 descreve isso como um conceito de zona de escopo, não como decoração acrescentada a um endereço. O nome ou índice da interface só tem significado para o host que o resolve. Em outra máquina, en0 pode nomear outra interface ou nenhuma. Isso faz de um identificador de zona um alvo de aprovação portátil ruim.
Para uma ação direta em socket local, aceite uma zona apenas se o analisador da plataforma a validar e se a ação for executada na mesma máquina. Armazene o índice numérico normalizado da interface como valor de comparação quando o SO o expuser. Você também pode manter o nome de interface fornecido para a trilha de auditoria, mas nomes podem mudar quando hardware e configuração de rede mudam.
Para uma URI HTTP, as regras são mais rígidas. O RFC 6874 especifica que o sinal de porcentagem que introduz um identificador de zona é codificado como %25 dentro do literal entre colchetes. Portanto, uma URI usa uma forma como esta:
http://[fe80::1%25en0]/status
Um analisador de URI deve decodificar isso na etapa correta. Não decodifique percentuais na URL inteira antes de analisá-la, pois um decodificador genérico pode alterar delimitadores e criar uma URL diferente da fornecida. Primeiro analise a URI, extraia o literal do host e depois aplique as regras de literal com escopo exigidas para esse componente.
A maioria dos fluxos de aprovação HTTP voltados a agentes deve rejeitar literais com escopo e explicar o motivo: endereços locais de enlace só fazem sentido com uma interface local específica. Pedir que um agente use um nome DNS estável, um endereço sem escopo ou um endpoint local configurado deliberadamente cria uma aprovação que outra pessoa consegue entender. Permita a exceção apenas quando o produto realmente opera com equipamentos de rede locais e mostra claramente o escopo da interface.
Comparar hosts é menor que aprovar uma ação
Normalizar um endereço corrige uma classe restrita de engano. Isso não torna https://[2001:db8::9]/ equivalente a https://[2001:db8::9]:9443/delete, nem torna um destino SSH seguro porque o texto do host parece familiar.
O destino de aprovação tipado deve manter os limites que a string bruta esconde. Para HTTP, isso normalmente inclui pelo menos esquema, tipo e bytes do host, porta após resolver a porta padrão, método e uma descrição da solicitação que exponha o caminho. A inclusão de cabeçalhos e hash do corpo na decisão depende da ação. Se uma credencial bearer puder chamar várias APIs sem relação entre si, a identidade da credencial também deve aparecer no prompt.
Para SSH, diferencie um endpoint de rede do comando remoto. Uma aprovação para abrir uma conexão SSH não descreve automaticamente permissão para executar sudo, alterar arquivos de implantação ou encaminhar uma porta. Se uma ferramenta puder realizar várias operações após uma conexão, a interface deve informar o que a autorização da sessão abrange e manter um registro por chamada de cada comando.
É aqui que um modelo de dados bem definido compensa. Ele evita uma recomendação tentadora, mas errada: colocar hosts canônicos em uma lista de permissões simples e considerar o trabalho concluído. Listas simples de hosts são populares porque são fáceis de explicar. Elas falham porque um host é apenas uma parte de uma ação e porque uma resposta DNS posterior, porta, caminho ou comando pode mudar o efeito.
Um registro de aprovação útil pode ter esta aparência:
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
A autoridade bruta documenta a solicitação. Os campos tipados conduzem a comparação. O campo de exibição dá à pessoa uma frase estável para ler. Não reutilize um desses campos como substituto dos outros.
Regras de rejeição devem ser simples e rigorosas
Um analisador deve falhar de forma segura quando a entrada não se encaixa na gramática de sua posição. A mensagem de erro pode ajudar, mas o sistema não deve corrigir um endereço tentando adivinhar o que quem chamou pretendia. Um normalizador que aceita entrada quase válida cria uma segunda gramática que profissionais de revisão de segurança terão dificuldade para reconstruir.
Rejeite uma solicitação de literal IP quando ela tiver qualquer uma destas falhas:
- mais de um marcador de compressão
::ou campos demais após a expansão - caracteres não hexadecimais em um campo hexadecimal
- uma terminação IPv4 fora da posição permitida pela sintaxe textual IPv6
- colchetes passados a um analisador de endereço isolado, ou ausência de colchetes em uma autoridade URI
- um identificador de zona em um contexto no qual a ação não pode vincular uma interface local
Também rejeite um literal que seu analisador de URI e seu analisador de socket interpretem de maneira diferente. Isso não é preciosismo teórico. Bibliotecas diferentes tomaram decisões diferentes ao longo do tempo sobre codificação de percentuais, formas incomuns de IPv4 e análise de hosts. Um componente aprovar um texto ao qual outro componente se conecta é uma lacuna de autorização.
Crie um pequeno conjunto de testes entre camadas. Passe cada caso pelo mesmo analisador de URI, extrator de host, analisador IP, formatador, comparador e construtor de transporte usados em produção. Para entradas aceitas, verifique tanto a exibição canônica quanto o destino real do socket. Para entradas rejeitadas, verifique que nenhum objeto de transporte seja criado.
Um conjunto compacto deve incluir ::, ::1, um endereço totalmente expandido, duas sequências de zeros de mesmo tamanho, um literal de URI entre colchetes com porta, um valor IPv4 mapeado, uma terminação pontuada malformada, um literal local de enlace com escopo e uma zona %25 codificada em uma URI. Acrescente qualquer formato de entrada que agentes no seu ambiente realmente tenham produzido. Testes de regressão extraídos de aprovações reais são menos glamourosos que truques de análise e têm muito mais chance de detectar o próximo erro.
O cartão de aprovação deve mostrar a normalização
Uma pessoa não consegue avaliar um endpoint se o cartão esconde a parte que mudou. Mostre o destino canônico em destaque e, quando for diferente, mostre a grafia enviada. Uma linha discreta como Enviado como [2001:0DB8:0:0:0:0:0:9]:8443 dá evidência a quem revisa sem obrigá-lo a decodificar campos mentalmente.
O cartão também deve deixar claras as formas especiais. Identifique um endereço mapeado como IPv6 mapeado para IPv4 em vez de apresentá-lo como um literal IPv6 comum. Identifique um endereço com escopo pelo nome da interface e informe que ele é local àquela interface. Se a política da ação projeta um endereço mapeado para IPv4, declare essa decisão no cartão. Equivalência silenciosa é o que surpreende quem revisa.
Para chamadas de alto impacto, exija que a pessoa aprove a ação completa, não apenas o host canônico. Uma apresentação compacta ainda pode trazer os detalhes necessários:
POST https://[2001:db8::9]:8443/v1/releases
Uses credential: deploy-api
Submitted host spelling: 2001:0DB8:0:0:0:0:0:9
A Sallyport segue essa separação onde ela importa: o agente nunca recebe o segredo de API ou SSH, enquanto o app executa a ação e retorna seu resultado. Sua autorização por sessão pode estabelecer quem iniciou uma execução, e seus controles por chamada podem manter ações sensíveis visíveis, em vez de tratar uma aprovação anterior como autorização irrestrita.
Registros de auditoria precisam do texto bruto e do significado normalizado
A revisão de incidentes precisa de duas respostas que frequentemente entram em conflito se você salvou apenas uma representação: o que o agente realmente enviou e qual destino o transporte usou? Salve ambos. Depois, deixe claro qual valor conduziu a decisão.
Um evento de auditoria deve incluir a entrada bruta, o tipo de host analisado, a forma canônica de exibição, bytes de endereço em uma codificação inequívoca, identidade de escopo quando houver e todos os campos de ação cobertos pela aprovação. Aplicar hash ao evento após a serialização só é útil se a serialização tiver uma definição estável. Caso contrário, o mesmo evento semântico pode produzir registros diferentes porque um formatador mudou.
Não apague entradas não canônicas depois de derivar a forma de exibição. Elas podem revelar um cliente com erro, uma tentativa de contornar controles ou uma particularidade inofensiva de biblioteca que depois explica um incidente. Mantenha-as como evidência, mas não deixe que painéis de busca as transformem em uma segunda identidade enganosa para o mesmo endpoint.
Os diários de Atividades e Sessões da Sallyport são projetados a partir de um único log de auditoria criptografado e encadeado por hash, e sp audit verify verifica essa cadeia offline sobre texto cifrado. Essa estrutura é especialmente útil quando quem revisa precisa distinguir uma grafia incomum de uma ação executada diferente.
Comece com um teste antes de redesenhar toda a camada de aprovação: envie o mesmo destino em formato totalmente expandido e no formato RFC 5952. Se o sistema produzir identidades de aprovação diferentes, resultados de busca de auditoria diferentes ou decisões de permissão diferentes, a fronteira do analisador ainda está no lugar errado.
FAQ
Duas strings IPv6 podem se referir ao mesmo endereço?
Sim. Várias strings podem indicar o mesmo endereço de 128 bits, pois o IPv6 permite compressão de zeros, omissão de zeros à esquerda e notação mista de IPv6 e IPv4. Compare os bytes do endereço analisado e os dados de escopo relevantes, não o texto fornecido por quem fez a chamada.
Por que endereços IPv6 precisam de colchetes nas URLs?
Use colchetes quando um literal IPv6 aparece na autoridade de uma URI, como em https://[2001:db8::7]:8443/. Não armazene os colchetes como parte do endereço em si, pois eles pertencem à gramática da URI.
O que é um endereço IPv6 mapeado para IPv4?
::ffff:192.0.2.7 é um endereço IPv6 mapeado para IPv4. Muitas APIs o produzem quando um par IPv4 chega por um socket IPv6, portanto um sistema de aprovação precisa decidir deliberadamente se ele será comparado como esse valor IPv6 ou como o endereço IPv4 incorporado.
Devo permitir um identificador de zona em um destino de aprovação HTTP?
Em geral, não. Um identificador de zona atribui a um endereço local de enlace o escopo de uma interface, por isso ele só tem sentido na máquina que conhece essa interface. Para aprovações HTTP remotas, peça um nome DNS ou um endereço roteável sem escopo.
Qual é o formato de texto IPv6 canônico?
O RFC 5952 recomenda hexadecimal em minúsculas, remoção dos zeros à esquerda e a compressão com :: da maior sequência de campos zero, escolhendo a primeira em caso de empate. O texto canônico ajuda as pessoas a revisar destinos, mas não substitui a comparação em nível de bytes.
A aprovação de um host IPv6 libera todas as portas nele?
Não. Um literal IPv6 entre colchetes é apenas uma representação de host na sintaxe de URI. A porta ainda importa, assim como esquema, caminho, consulta, método e identidade da credencial podem importar para a ação que o agente quer aprovar.
O que deve acontecer se um destino de aprovação contiver um nome de host?
Rejeite-o em um analisador de literais IP e trate-o separadamente como nome de host, se o produto oferecer suporte a nomes. Forçar um nome de host pela normalização IPv6 cria um comportamento de fallback ambíguo e frequentemente inseguro.
Devo armazenar o texto original do endereço IPv6?
Preserve a solicitação original para a trilha de auditoria, mas mostre um valor de exibição canônico e compare um valor interno tipado. Essas três representações respondem a perguntas diferentes: o que chegou, o que uma pessoa viu e o que o sistema autorizou.
Posso normalizar endereços IPv6 malformados?
Não. Um analisador deve rejeitar colchetes malformados, vários marcadores ::, grupos hexadecimais inválidos, uma terminação IPv4 na posição errada e um identificador de zona quando a gramática ao redor o proíbe. Trate divergências entre analisadores como falha, não como motivo para adivinhar.
Como um sistema de aprovação deve lidar com nomes DNS e IPv6?
Resolva DNS separadamente, seguindo regras adequadas a nomes de host, e depois normalize cada endereço retornado para registro e comparação. Não transforme silenciosamente um nome de host em um literal aprovado, pois respostas DNS futuras podem mudar o destino para o qual o mesmo nome aponta.