# La protección contra rebinding de DNS exige una resolución en tiempo de ejecución

Una aprobación para `api.example.test` no aprueba cualquier dirección que ese nombre devuelva más tarde. Si un agente puede hacer una solicitud HTTP autenticada, el rebinding de DNS convierte una decisión obsoleta sobre un nombre de host en una ruta hacia un servicio local, un endpoint de metadatos en la nube o un plano de control interno. La inserción de credenciales puede funcionar exactamente como se diseñó. Lo que falló fue la decisión sobre el destino.

He visto equipos crear pantallas de aprobación cuidadosas y después dejar que la biblioteca HTTP resuelva el nombre en el último momento posible sin comprobar la respuesta. Es fácil pasar por alto esa brecha porque el DNS normal se comporta correctamente. Un atacante no necesita que el DNS normal coopere. Necesita un nombre que controle, una vida útil de caché corta y un cliente que confunda un nombre de host con un destino permanente.

La protección contra rebinding de DNS exige una búsqueda en tiempo de ejecución. Resuelve justo antes de abrir la conexión, inspecciona el resultado completo, elige una dirección permitida y conecta con esa dirección exacta. Conserva el nombre de host original para HTTP y la validación TLS. Repite el trabajo cada vez que el cliente abra otra conexión, siga una redirección o cambie de protocolo.

## La aprobación identifica una clase de destino, no una dirección IP para siempre

Un nombre de host es una instrucción para resolver, no una identidad estable de un par de red. La distinción parece quisquillosa hasta que un agente recibe una URL desde una incidencia, un registro de compilación, la respuesta de una herramienta u otro servicio. El agente puede pedir aprobación para llamar a `reports.partner.test`; quien aprueba ve un destino público verosímil. Minutos después, el mismo nombre puede responder con `127.0.0.1`, una dirección IPv6 de loopback o una dirección accesible solo dentro de la red de la máquina.

La secuencia habitual de rebinding tiene dos fases. Durante la primera, el servidor DNS autoritativo devuelve una dirección pública. El cliente o revisor acepta el nombre de host, a veces después de una solicitud previa similar a la de un navegador. Durante la segunda, el atacante cambia la respuesta y depende de la expiración, una reconexión, una redirección, un reintento o una segunda solicitud para activar otra búsqueda. El servicio de destino ve una solicitud desde una máquina local, donde los cortafuegos de red suelen conceder más acceso que a Internet público.

Una puerta de enlace de agentes presenta una versión más grave de este problema. Puede adjuntar un token bearer, credenciales básicas o un encabezado de autorización personalizado solo después de que una persona apruebe la acción. Si la puerta de enlace verifica el proceso del agente pero no la dirección del par, puede entregar una credencial auténtica al lugar equivocado. Una solicitud a un servicio administrativo local quizá ni necesite la credencial. El atacante puede usar la solicitud únicamente para que la máquina lea una respuesta que el agente pueda recibir después.

No lo resuelvas guardando para siempre la primera dirección IP resuelta. Los servicios públicos usan balanceo de carga, cambios de dirección e IPv6. La afirmación segura es más limitada: cada conexión debe usar una dirección que haya superado la validación cuando comenzó esa conexión. Una aprobación anterior puede autorizar el nombre de host y el uso de la credencial. No puede justificar una dirección posterior que pertenezca a una red bloqueada.

## Resuelve justo antes de abrir el socket

El orden de las operaciones determina si la comprobación sirve de algo. Resuelve el nombre de host, valida la respuesta del resolvedor, elige una dirección y pasa esa dirección directamente a la rutina de conexión. Si validas una dirección y después llamas a una API de conveniencia que vuelve a resolver el nombre de host, habrás comprobado un destino y conectado a otro.

Mantén separados cuatro valores durante toda la solicitud:

- `origin_host` es el nombre de host que aprobó el usuario y que debe cubrir el certificado.
- `peer_ip` es la única dirección validada seleccionada para este socket.
- `port` es el puerto solicitado después de que el cliente aplique sus reglas de puertos permitidos.
- `scheme` determina si el cliente exige TLS.

Para HTTPS, conecta el socket TCP a `peer_ip`, pero envía `origin_host` como encabezado HTTP Host y usa `origin_host` para SNI y la verificación del certificado. Un certificado para la dirección IP no es un sustituto. Si el certificado no coincide con el nombre de host, la solicitud debe fallar. Desactivar la validación de certificados para facilitar la fijación de direcciones sustituye un error de enrutamiento por otro mucho peor.

Un límite mínimo de conexión puede verse así:

```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)
```

Este esquema dice intencionadamente `lookup_all`. Una búsqueda de una sola dirección oculta un fallo habitual: el resolvedor devuelve a la vez una dirección IPv4 pública permitida y una dirección IPv6 de loopback. Una biblioteca puede preferir IPv6 aunque la aplicación solo haya inspeccionado IPv4. Rechaza el nombre de host cuando se bloquee alguna respuesta, o define una regla de selección estricta que pase solo direcciones validadas al conector. Rechazar respuestas mixtas es más fácil de auditar y deja al atacante menos margen para explotar el comportamiento de carreras de conexión.

La palabra «justo» importa. Resuelve antes de que se abra el socket, no cuando el agente redacta una solicitud, no cuando aparece la tarjeta de aprobación y no cuando el usuario guarda una credencial. Sigue habiendo un pequeño intervalo entre la búsqueda y la conexión, pero el cliente ya ha elegido una dirección y no necesita consultar DNS de nuevo para ese socket.

## Filtrar IPv4 privadas es solo la primera barrera

RFC 1918 reserva rangos IPv4 para redes privadas. Ofrece tres bloques conocidos: `10.0.0.0/8`, `172.16.0.0/12` y `192.168.0.0/16`. Bloquearlos es necesario para un cliente expuesto a Internet, pero tratar esa lista como toda la defensa es un error habitual y costoso.

Un filtro de destinos debe rechazar categorías de direcciones que no puedan tratarse de forma segura como públicas. Como mínimo, rechaza loopback, direcciones no especificadas, link-local, multicast, direcciones de uso privado y direcciones locales únicas IPv6. Rechaza las formas IPv6 mapeadas a IPv4 después de normalizarlas, porque `::ffff:127.0.0.1` sigue teniendo en la práctica efecto de loopback. Considera inválidos los identificadores de zona IPv6 en URL remotas. No dejes que la ortografía textual determine la seguridad; analiza la dirección en forma binaria y clasifícala ahí.

Varios rangos merecen una decisión explícita de producto, no un valor predeterminado accidental. Las direcciones NAT de grado operador en `100.64.0.0/10` no son accesibles globalmente. Los rangos de documentación y benchmarking no deberían aparecer como destinos de producción normales. El rango IPv4 link-local puede alcanzar servicios de metadatos en algunas configuraciones de nube. Las direcciones IPv6 link-local requieren un ámbito de interfaz y nunca deberían surgir de una solicitud a un nombre de host público. Para una puerta de enlace de acciones externas, el valor predeterminado más seguro es permitir únicamente direcciones unicast globales normales, con excepciones identificadas para un modo de despliegue interno separado.

No uses una comprobación de prefijo de cadena. `127.1`, `127.0.0.1`, formatos enteros aceptados por un analizador permisivo, la compresión IPv6 y las formas mapeadas la vuelven poco fiable. Analiza el host de la URL como nombre DNS o como literal IP. Si es un literal IP, clasifícalo directamente. Si es un nombre DNS, resuélvelo y clasifica cada dirección devuelta. Rechaza nombres malformados antes de resolverlos.

Aquí también los equipos confunden DNS público con accesibilidad pública. Un resolvedor puede devolver una dirección pública que se enruta a través de un proxy corporativo, una VPN o una ruta de red de horizonte dividido. La respuesta DNS es una entrada, no una promesa sobre toda la ruta. El filtrado de direcciones evita el rebinding directo hacia rangos locales evidentes. Las reglas de salida de red aún deben impedir rutas de acceso que el host puede alcanzar pero que el producto no debería usar.

## Los CNAME y las respuestas mixtas requieren una sola decisión

Un CNAME no vuelve seguro a un nombre. Delega la respuesta final en otro nombre, que a su vez puede cambiar o devolver un conjunto mixto de respuestas. El resolvedor normalmente sigue esa cadena antes de entregar a la aplicación registros A y AAAA, así que la aplicación debe juzgar las direcciones finales que recibe. Si la API del resolvedor expone la cadena, regístrala para el diagnóstico, pero no bases la aplicación de la política en que el primer nombre pareciera conocido.

Un valor predeterminado firme es sencillo: si cualquier respuesta final para un nombre de host solicitado pertenece a una categoría bloqueada, rechaza la solicitud. Esto puede rechazar un servicio que publique una dirección privada inaccesible junto a una pública, pero esa configuración ya genera clientes impredecibles. Una puerta de enlace que envía credenciales debe pedir al propietario del servicio que corrija el registro DNS en vez de elegir silenciosamente una respuesta conveniente.

Algunos equipos prefieren «usar cualquier respuesta permitida». La regla es popular porque mantiene más integraciones en funcionamiento. También crea comportamientos difíciles de reproducir: un orden del resolvedor funciona, otro conecta con algún lugar bloqueado, y una implementación Happy Eyeballs inicia intentos IPv6 e IPv4 en momentos distintos. Si eliges esa vía, la capa de conexión debe recibir solo las direcciones permitidas elegidas y nunca recurrir al nombre de host original. Una biblioteca HTTP genérica a menudo no ofrece esa garantía.

Registra pruebas suficientes para explicar un rechazo. El registro de la solicitud debe incluir el nombre de host original, el puerto, el conjunto de direcciones resueltas, la dirección de par seleccionada si existe, el resultado de la política y un identificador de correlación de solicitud. No registres credenciales ni cuerpos de respuesta solo por comodidad al depurar. Los fallos de seguridad DNS suelen aclararse con la lista de direcciones por sí sola.

## Los grupos de conexiones son seguros solo si preservan el par

Una conexión TLS reutilizada no realiza una búsqueda DNS nueva y no debería necesitarla. El socket ya tiene una dirección de par elegida y validada cuando se abrió. Un cliente puede reutilizar esa conexión exacta para otra solicitud al mismo origen si lo permiten las reglas normales de origen HTTP y las comprobaciones de certificado.

Una conexión nueva es distinta. Los grupos suelen ocultar reconexiones tras un tiempo de inactividad, un fallo de transporte, un límite de flujos HTTP/2 o un cambio en el estado del proxy. Si el grupo pide al sistema operativo conectar de nuevo por nombre de host, ha abierto la ventana de rebinding. Coloca el resolvedor y el conector debajo del grupo, no junto a la primera solicitud.

La coalescencia de conexiones exige precaución. HTTP/2 puede reutilizar una conexión TLS para más de un nombre de host cuando el certificado los cubre y el par es adecuado. Un navegador general puede aceptar ese equilibrio. Una puerta de enlace de acciones debe hacer explícita la decisión de destino para cada origen. No supongas que un socket abierto para un nombre de host aprobado puede llevar una solicitud con credenciales para otro solo porque el certificado enumera ambos nombres. El alcance de la aprobación, la autoridad HTTP y la reutilización de conexiones son decisiones independientes.

Los reintentos tienen la misma forma. Un reintento después de un fallo de conexión es un nuevo intento saliente. Vuelve a resolver, reclasifica, elige una dirección y abre un socket nuevo. Nunca almacenes en caché un resultado permitido más tiempo que la conexión que autorizó. Puedes almacenar brevemente resultados bloqueados para reducir trabajo repetido, pero no conviertas esa caché en una autoridad de enrutamiento sin revisión.

Los cambios a mitad de llamada no permiten a un atacante mover una conexión TCP establecida a una IP nueva. TCP ya eligió su par. El peligro está en la ruta de código que crea una conexión de reemplazo, sigue una redirección, actualiza un protocolo o emite otra solicitud después de la primera respuesta. Probar una sola solicitud satisfactoria no cubre las rutas que usan los clientes de producción bajo carga.

## Las redirecciones son solicitudes salientes independientes

Una redirección cambia la URI de destino. Trátala como una acción nueva, aunque la biblioteca HTTP la llame una función de conveniencia. Analiza el valor de `Location`, rechaza esquemas no admitidos, resuelve su nombre de host en tiempo de ejecución, valida sus respuestas y comprueba su puerto antes de conectar. Transferir automáticamente encabezados de autorización a través de un cambio de origen es otro error de fuga de credenciales, así que elimínalos salvo que el nuevo origen tenga su propia decisión explícita de autorización.

Limita las redirecciones a un número pequeño y fijo. Un servidor de pruebas de rebinding puede rotar nombres y respuestas en cada salto, y las redirecciones ilimitadas convierten una acción saliente sencilla en una cadena opaca. Registra cada salto con el origen de partida, el origen de destino, el conjunto de direcciones, el par seleccionado, el código de estado y el motivo de cualquier rechazo.

Las comprobaciones DNS también deben rodear funciones de protocolo fáciles de pasar por alto. Un proxy HTTP cambia quién recibe la conexión TCP inicial, así que valida la dirección del proxy y define después qué puede resolver el proxy. De lo contrario, un túnel CONNECT puede trasladar la búsqueda del nombre de host a un componente que no ejecuta las mismas comprobaciones. Los webhooks, las URL de devolución de llamada, los endpoints de almacenamiento de objetos y los registros de paquetes merecen el mismo tratamiento cuando un agente puede influir en su destino.

Una regla que bloquea respuestas DNS privadas no vuelve seguras las URL arbitrarias. No impide que un servidor público ejecute un comando peligroso, devuelva una respuesta enorme o redirija hacia un servicio aprobado pero hostil. La defensa contra rebinding de DNS es un límite. Debe ser lo bastante precisa para que nadie la confunda con un lenguaje de políticas de propósito general.

## Prueba el resolvedor y el conector como una unidad

Las pruebas unitarias que introducen `127.0.0.1` en un clasificador de direcciones son necesarias, pero insuficientes. El fallo ocurre cuando el cliente pasa un nombre de host por varias capas y una de ellas realiza una resolución sin protección. Tus pruebas deben observar el destino real del socket.

Crea una zona de prueba autoritativa controlada con un nombre como `flip.test`. En su primera búsqueda, devuelve un endpoint público de prueba que registre una solicitud inocua. En la siguiente, devuelve una dirección bloqueada. Después fuerza una segunda conexión cerrando el primer endpoint tras su respuesta. El resultado esperado no es una segunda solicitud correcta con una advertencia en los registros. El conector debe rechazarla antes de abrir un socket a la dirección bloqueada.

Usa una matriz de pruebas que cambie una condición cada vez:

1. Devuelve una respuesta IPv4 pública y luego una respuesta IPv4 de loopback tras un TTL bajo.
2. Devuelve una dirección IPv4 permitida junto a `::1` en el mismo conjunto de respuestas.
3. Devuelve un CNAME cuya respuesta final cambie de pública a local única IPv6.
4. Devuelve una redirección a un segundo nombre de host que se resuelva a una dirección bloqueada.
5. Cierra una conexión agrupada y verifica que el reintento reciba una validación nueva.

Añade una aserción en el límite de red. Un conector simulado debe registrar la IP numérica y el puerto que recibió. La prueba debe fallar si recibe un nombre de host, porque eso significa que otro resolvedor aún puede ejecutarse después de la validación. En las pruebas de integración, coloca un listener en el puerto de loopback bloqueado y confirma que registra cero conexiones. Una solicitud rechazada que llega al listener ya cruzó el límite que pretendías proteger.

Las pruebas con TTL cortos importan porque revelan cachés en lugares inesperados: el resolvedor de la aplicación, el sistema operativo, un proxy, un tiempo de ejecución de lenguaje o la biblioteca HTTP. Prueba tanto un resolvedor que respete el TTL como otro que devuelva una respuesta modificada inmediatamente. La seguridad no puede depender de que una caché concreta sea lenta o rápida.

## La aprobación y la validación de direcciones responden preguntas distintas

La aprobación por sesión responde «¿qué proceso de agente puede actuar durante esta ejecución?». La aprobación por llamada responde «¿este uso de credenciales merece ahora una decisión humana?». La validación DNS responde «¿qué par de red puede recibir esta conexión?». Combinar todo esto en una sola solicitud de aprobación hace que la interfaz parezca más simple, pero oculta un cambio importante en aquello que la persona aprobó.

Muestra el nombre de host y el puerto en una superficie de aprobación, pero no finjas que un nombre de host es una dirección de par inmutable. La implementación debe validar la dirección en tiempo de ejecución aunque la persona haya visto un aviso segundos antes. Si el resultado está bloqueado, deniega la acción e indica el nombre de host y la categoría de dirección clasificada. Un usuario puede corregir un registro DNS erróneo; no puede aprobar de forma útil una carrera de rebinding invisible.

La puerta de la bóveda de Sallyport, la autorización de sesión y las aprobaciones de credenciales por llamada controlan quién puede invocar una acción y cuándo una persona debe confirmarla. Su ruta de acciones HTTP sigue necesitando la misma disciplina de destino en tiempo de ejecución, porque una bóveda que nunca entrega un secreto a un agente también debe evitar enviar ese secreto a una respuesta DNS en la que el usuario no quería confiar.

No añadas un motor de reglas de formato libre para resolver este problema limitado. El comportamiento central puede seguir siendo determinista: rechazar respuestas no públicas para acciones HTTP externas, resolver al crear la conexión, vincular el socket a la dirección elegida y reevaluar en cada conexión nueva. Una regla pequeña con un límite comprobable es más fácil de mantener correcta que una página de excepciones que nadie puede explicar.

## Los registros deben mostrar el destino que realmente se usó

Un registro de auditoría que solo contiene el nombre de host no puede responder a la pregunta que importa tras un incidente: ¿adónde fue el socket? Guarda tanto el origen solicitado como la dirección de par elegida para cada conexión permitida. En los intentos denegados, guarda el conjunto de respuestas devueltas y la clasificación que provocó la denegación. Mantén la marca de tiempo cerca de la creación de la conexión para que un investigador pueda correlacionarla con el comportamiento del resolvedor.

Evita afirmar una certeza que el registro no puede demostrar. Si la biblioteca HTTP recibe solo un nombre de host, el registro de auditoría puede decir que la aplicación solicitó un nombre de host; no puede probar el par numérico. Corrige el conector antes de pulir la entrada de auditoría. Las pruebas deben proceder de la capa que controla el socket.

Para un historial de auditoría resistente a manipulaciones, incluye los registros de decisiones DNS en la misma secuencia que los eventos de autorización, conexión, redirección y respuesta. Sallyport genera sus diarios Sessions y Activity a partir de un registro de auditoría cifrado, encadenado por hash e inaccesible para escritura, y `sp audit verify` puede verificar esa cadena sin conexión sobre texto cifrado. Eso solo sirve si el registro de acción incluye el destino resuelto en vez de un nombre de host tranquilizador pero incompleto.

Empieza por la fábrica de conexiones. Encuentra cada ruta que acepte un nombre de host, haz que devuelva un par numérico validado y obliga a la API de sockets a rechazar nombres de host sin procesar. Cuando se cumpla ese invariante, los TTL cortos y los cambios de respuesta pasarán a ser casos de prueba normales en lugar de una sorpresa de seguridad esperando tras la próxima reconexión.
