# A proteção contra DNS rebinding exige uma consulta no momento da execução

Uma aprovação para `api.example.test` não aprova qualquer endereço que esse nome venha a retornar depois. Se um agente puder fazer uma solicitação HTTP autenticada, o DNS rebinding transforma uma decisão desatualizada sobre um nome de host em uma rota para um serviço local, um endpoint de metadados em nuvem ou um plano de controle interno. A injeção de credenciais pode funcionar exatamente como foi projetada. O que falhou foi a decisão sobre o destino.

Já vi equipes criarem telas de aprovação cuidadosas e depois deixarem a biblioteca HTTP resolver o nome no último instante possível, sem verificar a resposta. Essa brecha é fácil de ignorar porque o DNS comum costuma se comportar bem. Um invasor não precisa que o DNS comum coopere. Basta controlar um nome, usar uma duração curta de cache e contar com um cliente que confunde um nome de host com um destino permanente.

A proteção contra DNS rebinding exige uma consulta no momento da execução. Resolva imediatamente antes de abrir a conexão, examine o resultado completo, selecione um endereço permitido e conecte-se exatamente a ele. Preserve o nome de host original para HTTP e validação TLS. Faça isso novamente sempre que o cliente abrir outra conexão, seguir um redirecionamento ou mudar de protocolo.

## A aprovação define uma classe de destino, não um endereço IP para sempre

Um nome de host é uma instrução para resolver, não uma identidade estável de um par de rede. A distinção parece minuciosa até que um agente receba uma URL em uma issue, um log de compilação, uma resposta de ferramenta ou outro serviço. O agente pode pedir aprovação para chamar `reports.partner.test`; quem aprova vê um destino público plausível. Minutos depois, o mesmo nome pode responder `127.0.0.1`, um endereço IPv6 de loopback ou um endereço acessível apenas dentro da rede da máquina.

A sequência usual de rebinding tem duas fases. Na primeira, o servidor DNS autoritativo retorna um endereço público. O cliente ou revisor aceita o nome de host, às vezes depois de uma solicitação de pré-verificação parecida com a de um navegador. Na segunda, o invasor altera a resposta e conta com a expiração, uma reconexão, um redirecionamento, uma nova tentativa ou uma segunda solicitação para disparar outra consulta. O serviço de destino vê uma solicitação vinda de uma máquina local, onde firewalls de rede muitas vezes concedem mais acesso do que concederiam à internet pública.

Um gateway de agentes tem uma versão mais séria desse problema. Ele pode anexar um token bearer, credenciais básicas ou um cabeçalho de autorização personalizado apenas depois que uma pessoa aprovar a ação. Se o gateway verificar o processo do agente, mas não o endereço do par, poderá entregar uma credencial legítima ao lugar errado. Uma solicitação para um serviço administrativo local talvez nem precise da credencial. O invasor pode usar a solicitação apenas para fazer a máquina ler uma resposta que o agente receberá em seguida.

Não resolva isso registrando para sempre o primeiro endereço IP resolvido. Serviços públicos usam balanceamento de carga, mudanças de endereço e IPv6. A regra segura é mais restrita: toda conexão precisa usar um endereço que passou pela validação quando essa conexão começou. Uma aprovação anterior pode autorizar o nome de host e o uso da credencial. Ela não pode justificar um endereço posterior que pertença a uma rede bloqueada.

## Resolva imediatamente antes de abrir o socket

A ordem das operações determina se a verificação significa algo. Resolva o nome de host, valide a resposta do resolvedor, escolha um endereço e passe esse endereço diretamente à rotina de conexão. Se você validar um endereço e depois chamar uma API prática que resolve o nome de host novamente, terá verificado um destino e conectado a outro.

Mantenha quatro valores separados durante toda a solicitação:

- `origin_host` é o nome de host aprovado pelo usuário e que o certificado precisa cobrir.
- `peer_ip` é o único endereço validado selecionado para este socket.
- `port` é a porta solicitada depois que o cliente aplica as regras de portas permitidas.
- `scheme` determina se o cliente exige TLS.

Para HTTPS, conecte o socket TCP a `peer_ip`, mas envie `origin_host` como cabeçalho HTTP Host e use `origin_host` para SNI e verificação de certificado. Um certificado para o endereço IP não substitui isso. Se o certificado não corresponder ao nome de host, a solicitação deve falhar. Desativar a validação de certificado para facilitar a fixação de endereço troca um erro de roteamento por outro muito pior.

Um limite mínimo de conexão pode ser assim:

```text
origin_host = parse_url(request_url).host
answers = resolver.lookup_all(origin_host)
allowed = [ip for ip in answers if public_routable(ip)]
if allowed is empty:
    fail("DNS answer contains no permitted address")

peer_ip = choose_one(allowed)
socket = tcp_connect(peer_ip, request_port)
tls = tls_handshake(socket, server_name=origin_host, verify_name=origin_host)
send_http(tls, host_header=origin_host)
```

Esse exemplo usa intencionalmente `lookup_all`. Uma consulta de endereço único esconde uma falha comum: o resolvedor retorna um endereço IPv4 público permitido e um endereço IPv6 de loopback. Uma biblioteca pode preferir IPv6 mesmo quando a aplicação inspecionou apenas IPv4. Rejeite o nome de host se qualquer resposta estiver bloqueada ou defina uma regra rígida de seleção que passe apenas endereços validados ao conector. Rejeitar respostas mistas é mais simples de auditar e dá menos espaço para um invasor explorar o comportamento de corrida de conexões.

A palavra “imediatamente” importa. Resolva antes de abrir o socket, não quando o agente redige uma solicitação, quando o cartão de aprovação aparece ou quando o usuário armazena uma credencial. Ainda existe um pequeno intervalo entre a consulta e a conexão, mas o cliente já selecionou um endereço e não precisa consultar o DNS novamente para aquele socket.

## Filtrar IPv4 privado é apenas a primeira barreira

A RFC 1918 reserva intervalos IPv4 para redes privadas. Ela traz três blocos conhecidos: `10.0.0.0/8`, `172.16.0.0/12` e `192.168.0.0/16`. Bloqueá-los é necessário em um cliente voltado para a internet, mas tratar essa lista como toda a defesa é um erro comum e caro.

Um filtro de destino precisa rejeitar categorias de endereços que não podem ser tratadas com segurança como públicas. No mínimo, rejeite loopback, endereços não especificados, link-local, multicast, uso privado e endereços locais únicos IPv6. Rejeite formas IPv6 mapeadas para IPv4 depois de normalizá-las, pois `::ffff:127.0.0.1` continua sendo loopback na prática. Trate identificadores de zona IPv6 como inválidos em URLs remotas. Não deixe a grafia textual decidir a segurança; converta o endereço em forma binária e classifique-o nela.

Alguns intervalos exigem uma decisão explícita de produto, não um padrão acidental. Endereços de NAT em escala de operadora em `100.64.0.0/10` não são globalmente alcançáveis. Intervalos de documentação e benchmarking não devem aparecer como destinos normais de produção. O intervalo IPv4 link-local pode alcançar serviços de metadados em algumas configurações de nuvem. Endereços IPv6 link-local exigem o escopo de uma interface e jamais deveriam surgir de uma solicitação de nome de host público. Para um gateway de ações externo, o padrão mais seguro é permitir apenas endereços unicast globais comuns, com exceções nomeadas para um modo de implantação interna separado.

Não use verificação de prefixo de texto. `127.1`, `127.0.0.1`, formatos inteiros aceitos por um analisador permissivo, compactação IPv6 e formas mapeadas tornam isso pouco confiável. Analise o host da URL como um nome DNS ou um IP literal. Se for um IP literal, classifique-o diretamente. Se for um nome DNS, resolva-o e classifique cada endereço retornado. Rejeite nomes malformados antes da resolução.

É aqui também que equipes confundem DNS público com alcance público. Um resolvedor pode retornar um endereço público que passa por um proxy corporativo, uma VPN ou uma rota de rede com horizonte dividido. A resposta DNS é uma entrada, não uma promessa sobre toda a rota. A filtragem de endereços impede rebinding direto para intervalos locais óbvios. As regras de saída da rede ainda precisam impedir caminhos que o host consegue alcançar, mas que o produto não deveria usar.

## CNAMEs e respostas mistas exigem uma decisão única

Um CNAME não torna um nome seguro. Ele delega a resposta final a outro nome, que também pode mudar ou retornar um conjunto misto de respostas. O resolvedor normalmente segue essa cadeia antes de entregar à aplicação os registros A e AAAA, portanto a aplicação deve julgar os endereços finais que recebe. Se a API do resolvedor expuser a cadeia, registre-a para diagnóstico, mas não baseie a aplicação da regra em o primeiro nome parecer familiar.

Um padrão forte é simples: se qualquer resposta final para um nome de host solicitado pertencer a uma categoria bloqueada, rejeite a solicitação. Isso pode rejeitar um serviço que publica um endereço privado inacessível ao lado de um endereço público, mas essa configuração já cria clientes imprevisíveis. Um gateway que envia credenciais deve pedir ao responsável pelo serviço que corrija o registro DNS, em vez de escolher silenciosamente uma resposta conveniente.

Algumas equipes preferem “usar qualquer resposta permitida”. A regra é popular porque mantém mais integrações funcionando. Ela também cria comportamentos difíceis de reproduzir: uma ordem de resolvedor funciona, outra se conecta a algum lugar bloqueado, e uma implementação Happy Eyeballs inicia tentativas IPv6 e IPv4 em momentos diferentes. Se você seguir esse caminho, a camada de conexão deve receber apenas os endereços permitidos escolhidos e nunca voltar ao nome de host original. Uma biblioteca HTTP genérica muitas vezes não oferece essa garantia.

Registre evidências suficientes para explicar uma rejeição. O log de solicitação deve incluir o nome de host original, a porta, o conjunto de endereços resolvidos, o endereço de par selecionado, se houver, o resultado da política e um identificador de correlação da solicitação. Não registre credenciais nem corpos de resposta apenas para facilitar a depuração. Falhas de segurança DNS normalmente ficam claras só com a lista de endereços.

## Pools de conexão só são seguros quando preservam o par

Uma conexão TLS reutilizada não faz uma nova consulta DNS e não deveria precisar fazê-la. O socket já tem um endereço de par escolhido e validado quando foi aberto. Um cliente pode reutilizar essa conexão exata para outra solicitação à mesma origem se as regras normais de origem HTTP e verificações de certificado permitirem.

Uma nova conexão é diferente. Pools muitas vezes escondem reconexões após um tempo ocioso, uma falha de transporte, um limite de streams HTTP/2 ou uma mudança no estado do proxy. Se o pool pedir ao sistema operacional que se conecte pelo nome de host novamente, terá aberto a janela para rebinding. Coloque o resolvedor e o conector abaixo do pool, não ao lado da primeira solicitação.

O coalescimento de conexões exige cuidado. O HTTP/2 pode reutilizar uma conexão TLS para mais de um nome de host quando o certificado os cobre e o par é adequado. Um navegador genérico pode aceitar essa troca. Um gateway de ações deve explicitar a decisão de destino para cada origem. Não presuma que um socket aberto para um nome de host aprovado possa levar uma solicitação com credencial para outro apenas porque o certificado lista ambos os nomes. O escopo da aprovação, a autoridade HTTP e a reutilização de conexão são decisões separadas.

Novas tentativas têm a mesma estrutura. Uma tentativa após falha de conexão é uma nova saída. Resolva novamente, classifique novamente, selecione um endereço e abra um novo socket. Nunca mantenha em cache um resultado permitido por mais tempo que a conexão que ele autorizou. Você pode manter resultados bloqueados em cache por pouco tempo para reduzir trabalho repetido, mas não transforme esse cache em uma autoridade de roteamento sem revisão.

Mudanças durante a chamada não permitem que um invasor mova uma conexão TCP já estabelecida para um novo IP. O TCP já selecionou seu par. O perigo está no caminho de código que cria uma conexão substituta, segue um redirecionamento, atualiza um protocolo ou envia outra solicitação após a primeira resposta. Testar apenas uma solicitação bem-sucedida deixa de fora os caminhos que clientes de produção usam sob carga.

## Redirecionamentos são solicitações de saída separadas

Um redirecionamento muda a URI de destino. Trate-o como uma nova ação, mesmo que a biblioteca HTTP o chame de recurso prático. Analise o valor de `Location`, rejeite esquemas incompatíveis, resolva seu nome de host no momento da execução, valide as respostas e verifique sua porta antes de conectar. Levar cabeçalhos de autorização automaticamente para outra origem é um erro separado de vazamento de credenciais, portanto remova-os, a menos que a nova origem tenha sua própria decisão explícita de autorização.

Limite os redirecionamentos a uma pequena quantidade fixa. Um servidor de teste de rebinding pode alternar nomes e respostas a cada salto, e redirecionamentos ilimitados transformam uma ação de saída simples em uma cadeia opaca. Registre cada salto com a origem de partida, a origem de destino, o conjunto de endereços, o par selecionado, o código de status e o motivo de qualquer rejeição.

As verificações DNS também devem envolver recursos de protocolo fáceis de esquecer. Um proxy HTTP muda quem recebe a conexão TCP inicial, então valide o endereço do proxy e defina o que ele pode resolver. Caso contrário, um túnel CONNECT pode mover a consulta do nome de host para um componente que não executa as mesmas verificações. Webhooks, URLs de callback, endpoints de armazenamento de objetos e registros de pacotes merecem o mesmo tratamento quando um agente puder influenciar seu destino.

Uma regra que bloqueia respostas DNS privadas não torna URLs arbitrárias seguras. Ela não impede que um servidor público emita um comando perigoso, retorne uma resposta enorme ou redirecione para um serviço aprovado, mas hostil. A defesa contra DNS rebinding é uma fronteira. Ela deve ser precisa o bastante para que ninguém a confunda com uma linguagem de políticas para qualquer finalidade.

## Teste o resolvedor e o conector como uma unidade

Testes unitários que passam `127.0.0.1` para um classificador de endereços são necessários, mas insuficientes. A falha ocorre quando o cliente passa um nome de host por várias camadas e uma delas faz uma resolução sem proteção. Seus testes precisam observar o destino real do socket.

Crie uma zona de teste autoritativa controlada com um nome como `flip.test`. Na primeira consulta, retorne um endpoint público de teste que registre uma solicitação inofensiva. Na consulta seguinte, retorne um endereço bloqueado. Depois force uma segunda conexão fechando o primeiro endpoint após sua resposta. O resultado esperado não é uma segunda solicitação bem-sucedida com um aviso nos logs. O conector precisa recusar antes de abrir um socket para o endereço bloqueado.

Use uma matriz de testes que altere uma condição por vez:

1. Retorne uma resposta IPv4 pública e, depois de um TTL baixo, uma resposta IPv4 de loopback.
2. Retorne um endereço IPv4 permitido ao lado de `::1` no mesmo conjunto de respostas.
3. Retorne um CNAME cuja resposta final mude de pública para local única IPv6.
4. Retorne um redirecionamento para um segundo nome de host que resolva para um endereço bloqueado.
5. Feche uma conexão agrupada e confirme que a nova tentativa recebe uma validação nova.

Adicione uma asserção no limite da rede. Um conector falso deve registrar o IP numérico e a porta que recebeu. O teste falha se ele receber um nome de host, pois isso significa que outro resolvedor ainda poderá ser executado após a validação. Em testes de integração, coloque um listener na porta de loopback bloqueada e confirme que ele registra zero conexões. Uma solicitação rejeitada que chega ao listener já atravessou a fronteira que você queria proteger.

Testes de TTL curto importam porque revelam caches em lugares inesperados: o resolvedor da aplicação, o sistema operacional, um proxy, um runtime de linguagem ou a biblioteca HTTP. Teste tanto um resolvedor que respeita o TTL quanto um que retorna uma resposta alterada imediatamente. A segurança não pode depender de um cache específico ser lento ou rápido.

## Aprovação e validação de endereço respondem a perguntas diferentes

A aprovação por sessão responde “qual processo de agente pode agir durante esta execução?”. A aprovação por chamada responde “este uso de credencial merece uma decisão humana agora?”. A validação DNS responde “qual par de rede pode receber esta conexão?”. Unir tudo isso em um único aviso de aprovação faz a interface parecer mais simples, mas esconde uma mudança importante no que a pessoa aprovou.

Mostre o nome de host e a porta em uma superfície de aprovação, mas não finja que um nome de host é um endereço de par imutável. A implementação deve validar o endereço no momento da execução, mesmo que a pessoa tenha visto um aviso segundos antes. Se o resultado estiver bloqueado, negue a ação e informe o nome de host e a categoria de endereço classificada. Um usuário pode corrigir um registro DNS incorreto; ele não consegue aprovar de forma útil uma corrida de rebinding invisível.

O portão do cofre, a autorização de sessão e as aprovações de credenciais por chamada do Sallyport controlam quem pode invocar uma ação e quando uma pessoa precisa confirmá-la. Seu caminho de ações HTTP ainda precisa da mesma disciplina de destino no momento da execução, pois um cofre que nunca entrega um segredo a um agente também deve evitar enviar esse segredo a uma resposta DNS na qual o usuário não pretendia confiar.

Não adicione um mecanismo de regras de formato livre para resolver esse problema específico. O comportamento central pode continuar determinístico: rejeitar respostas não públicas para ações HTTP externas, resolver no momento da conexão, vincular o socket ao endereço selecionado e reavaliar em cada nova conexão. Uma regra pequena com um limite testável é mais fácil de manter correta do que uma página de exceções que ninguém consegue explicar.

## Os logs precisam mostrar o destino realmente usado

Um registro de auditoria que traz apenas o nome de host não responde à pergunta importante após um incidente: para onde o socket foi? Guarde tanto a origem solicitada quanto o endereço de par selecionado em cada conexão permitida. Em tentativas negadas, guarde o conjunto de respostas retornado e a classificação que causou a negação. Mantenha o timestamp próximo da criação da conexão para que um investigador possa correlacioná-lo ao comportamento do resolvedor.

Evite alegar uma certeza que o log não pode sustentar. Se a biblioteca HTTP recebe apenas um nome de host, o registro de auditoria pode dizer que a aplicação solicitou um nome de host, mas não pode provar o par numérico. Corrija o conector antes de aperfeiçoar a entrada de auditoria. A evidência deve vir da camada que controla o socket.

Para uma trilha de auditoria resistente a adulteração, inclua registros das decisões DNS na mesma sequência dos eventos de autorização, conexão, redirecionamento e resposta. O Sallyport projeta seus diários Sessions e Activity a partir de um log de auditoria criptografado, encadeado por hash e cego para gravação, e `sp audit verify` consegue verificar essa cadeia offline sobre texto cifrado. Isso só é útil se o registro da ação incluir o destino resolvido, em vez de um nome de host reconfortante, mas incompleto.

Comece pela fábrica de conexões. Encontre todos os caminhos que aceitam um nome de host, faça-os retornar um par numérico validado e faça a API de socket rejeitar nomes de host brutos. Quando essa invariante estiver garantida, TTLs curtos e mudanças de resposta se tornam casos de teste comuns, em vez de uma surpresa de segurança esperando a próxima reconexão.
