Ir al contenido

¿Cómo se convierte una URL de webhook en un ataque SSRF?

15 sept 2026 12 min de lectura Paymos Tech Paymos Tech
Un trazo naranja punteado entra por una ranura de un muro blanco, gira por detrás y sale por la misma ranura, con dos bloques de servidor intactos al fondo

En resumen

Un SSRF por webhook ocurre cuando la URL de notificación que registra un cliente la va a buscar un servidor que está dentro de tu red. La comprobación que lo frena mira la dirección a la que resuelve la URL, no el texto de la URL, y tiene que sobrevivir a una redirección que llega cuando la comprobación ya pasó. OWASP retiró el SSRF como título propio del Top 10 de 2025 y metió el CWE-918 dentro de Broken Access Control, que nombra el problema real: la petición lleva la posición de red de tu servidor, y la posición es lo que da acceso.

Quien envía un webhook va a buscar una URL que eligió otra persona. Un SSRF, falsificación de peticiones del lado del servidor, no es más que eso, y una plataforma de pagos lo publica a propósito, porque la URL de notificación (callback) es como el sistema del comercio se entera de que una factura se pagó.

Lo que convierte la función en ataque es el punto de partida. La entrega sale de dentro de tu perímetro, desde una máquina con una tabla de rutas y una identidad de salida que ningún navegador de internet tiene. Alguien registra una dirección; tu plataforma se conecta a ella por él, con horario y con reintentos.

La comprobación que aguanta mira la dirección resuelta, se aplica antes de abrir el socket y se vuelve a aplicar si una redirección intenta mover el destino después del visto bueno. Lo de abajo va sobre dónde puede plantarse esa comprobación y qué le cuesta al comercio del otro extremo.

¿Qué es un SSRF por webhook?

Es la clase de fallo en la que un atacante elige el destino de una petición que hace tu servidor, y entra por el único campo del formulario construido justo para eso.

Los SSRF famosos son los que nadie quiso construir. Un generador de PDF sigue la URL de una imagen; un importador de avatares va a buscarla por cuenta del usuario. Nadie se propuso regalarle un cliente HTTP a desconocidos, y el hallazgo se lee después como un descuido. El emisor de webhooks es el caso deliberado: el campo está documentado, y el proceso que va a buscar esa URL no tiene otro trabajo.

OWASP movió la categoría en 2025. El SSRF tenía plaza propia en la lista de 2021, como A10; en el Top 10 de 2025 ya no aparece como título, y el CWE-918 figura entre las debilidades notables de A01, Broken Access Control.

La colocación nueva describe el daño mejor que el nombre viejo. La petición es auténtica. La mandó tu infraestructura, y quien la mandó alcanza todo lo que ella alcanza.

¿Por qué vale más la posición que la respuesta?

Porque la respuesta casi nunca llega al atacante, y el ataque funciona igual.

Un proceso de entrega lee un código de estado y tira el cuerpo. Nada vuelve a un panel, así que quien registró la URL aprende un bit por intento: la conexión se aceptó o no se aceptó. Esa es la variante a ciegas, y es la razón de que la infravalore quien piensa el SSRF como robo de datos. Un bit, repetido, dibuja una red.

El activo es la posición. Dentro del perímetro, el proceso alcanza máquinas que nunca se construyeron para responderle a nadie de fuera, muchas de ellas heredadas de una época en la que estar en la red interna era la autenticación. Fuera del perímetro, la dirección de salida de la plataforma suele estar en la lista de permitidos de algún socio que aceptó tráfico tuyo y de nadie más.

El escalón de reintentos es lo que pone a una plataforma de pagos por delante de un generador de PDF. Quien reintenta con una espera creciente convierte un registro en un trabajo que se dispara una y otra vez, sobre infraestructura que mantiene otro.

¿Dónde va la comprobación?

Después de resolver el nombre y antes de abrir la conexión, que es un sitio incómodo para plantarse en casi cualquier librería HTTP.

El filtro sobre la cadena de texto se escribe primero, porque es el que se puede escribir sin salir del handler de la petición. Falla por un motivo estructural y no por uno ingenioso: un nombre de host es un puntero, y adónde apunta lo decide un registro DNS que puede ser de quien registra la URL. Leer el texto no dice nada de adónde va el paquete. La guía de prevención de OWASP pide que la aplicación recupere las direcciones que hay detrás del nombre, A y AAAA, y aplique las reglas de dirección a lo que vuelva.

Esa incomodidad es la razón de que la versión ingenua se siga publicando. Una librería cliente quiere tomar una URL y devolver una respuesta, mientras que el punto de enganche que necesitas queda entre dos pasos que ella considera asunto suyo. Llegar ahí significa resolver el nombre por tu cuenta o engancharte al socket, y las dos cosas dan más trabajo que una expresión regular sobre un campo de formulario.

¿Qué rangos hay que rechazar?

Más de los que la mayoría escribe de memoria.

Loopback y los bloques privados del RFC 1918 son las entradas obvias. El link-local va ahí también: es el rango donde responde el servicio de metadatos de una instancia en la nube, y lo que se saca de alcanzarlo son credenciales.

Los dos que se quedan fuera son más jóvenes que el modelo mental de partida.

El espacio de direcciones compartido, 100.64.0.0/10, lo reservó el RFC 6598 en abril de 2012 para el NAT a escala de operador, y el propio RFC lo registra como distinto del espacio privado del RFC 1918, porque pertenece a redes de proveedores de servicio.

Un filtro que busca 10. y 192.168. no tiene opinión sobre él, y de paso se deja fuera la mitad del RFC 1918.

Las direcciones locales únicas de IPv6, fc00::/7, vienen del RFC 4193, que las define como no enrutables en la internet global y enrutables dentro de un sitio.

Un destino que encaja en esa descripción no es sitio al que mandar un webhook, y una lista de rangos escrita solo para IPv4 ni lo menciona.

En la entrega de webhooks de Paymos la lista rechaza el loopback, 0.0.0.0/8, los tres bloques privados del RFC 1918, el CGNAT 100.64/10, el link-local 169.254/16, los tres rangos de documentación y el ULA fc00::/7; el loopback y el link-local de IPv6 se comprueban aparte. Antes de cualquier prueba de rango la dirección se normaliza, que es lo que tumba el truco clásico de la dirección IPv4 envuelta en IPv6. Y la URL del endpoint tiene que ser HTTPS: cualquier otra cosa se rechaza al crearlo y al actualizarlo.

Una afirmación de seguridad estrecha vale más que una ancha, porque la estrecha se puede comprobar. Esta protección viene activada. Y es un control, no una propiedad estructural del producto, así que describirla como algo que nadie puede apagar sería pasarse.

¿Por qué una redirección anula una comprobación que ya pasó?

Porque la URL que validaste deja de ser la URL que vas a buscar.

El proceso resuelve un nombre, obtiene una dirección pública, la aprueba y abre la conexión. El servidor responde 302 con una ubicación nueva. Si el cliente HTTP sigue redirecciones —y tanto HttpClient de .NET como requests de Python lo hacen salvo que les digas lo contrario—, la segunda petición no pasó ninguna comprobación: la comprobación fue un suceso de la vida de la primera.

La guía de OWASP lo cierra en una cláusula: desactiva el soporte de redirección en el cliente web.

Es brusca y es correcta. Un endpoint que recibe notificaciones no tiene motivo para pasárselas a una segunda dirección, y un comercio que ha movido la suya puede registrar la nueva.

Aquí las redirecciones automáticas no se siguen nunca, y eso no depende del interruptor de validación de direcciones: queda fijado en el handler, antes de que nadie lo consulte.

¿Y si el DNS cambia entre la comprobación y la conexión?

Ese es el hueco silencioso, y se cierra en cómo está montado el cliente, no con una regla más en el validador.

Una respuesta DNS caduca, y el visto bueno que diste era sobre la respuesta que viste. Si entre aprobar y conectar el cliente vuelve a resolver el nombre, el destino puede haber cambiado sin que nada haya redirigido. Es el mismo problema con otro traje.

En la entrega de Paymos el host se resuelve una vez, se toma la primera dirección que pasa la lista y el socket se abre directamente contra esa dirección. No hay una segunda consulta DNS entre la comprobación y la conexión, así que la conexión queda fijada a lo que se validó y la ventana de DNS rebinding no llega a abrirse. Suele ser la última pieza que le crece a un emisor, así que conviene preguntar por ella con todas las letras.

¿Qué hace un proceso de entrega que deniega por defecto?

Rechaza sin gastar nada, y trabaja dentro de un presupuesto que esos rechazos no pueden agotar.

La primera mitad la lleva el orden. Un destino que no pasa la validación se rechaza antes de intentar ninguna conexión, así que un registro bloqueado no consume nada de la capacidad de salida.

La segunda mitad son los límites de recursos, que suelen saltarse porque parecen trabajo de fiabilidad. En la entrega de webhooks de Paymos se construyeron justo para eso, y acotan también esto:

  • Diez segundos por intento. Sin plazo, un destino que contesta al saludo y luego se calla ata un proceso hasta que algo cede, y unos cuantos de esos dejan la cola sin vaciar.
  • Once intentos cierran un evento: el primero más diez reintentos, espaciados de un minuto a ocho horas, unas dieciséis horas en total. Reintentar sin techo le regalaría a un destino elegido una fuente de tráfico que se recarga sola.
  • Diez endpoints por entorno de comercio. El límite de registro es lo que separa a una cuenta de una parte ilimitada de la capacidad de salida.

Esos intentos acotan el evento y nunca el endpoint. Un ciclo agotado marca el evento como fallido y deja vivo el registro, así que un receptor que estuvo caído un día vuelve y encuentra eventos reenviables.

¿Qué le cuesta esta defensa al comercio?

Se rompen endpoints reales con ella, y la rotura parece una caída en lugar de un control.

Una notificación no puede apuntar a localhost ni a una máquina de la red de la oficina: mientras desarrollas hace falta un túnel que le ponga un nombre público al handler. Con eso se topa toda integración la primera tarde.

Rechazar redirecciones significa que la URL registrada tiene que ser la definitiva. Un endpoint que contesta 301 porque su servidor añade la barra final, o una ruta que se movió el año pasado y todavía rebota, falla la entrega mientras le devuelve una página perfectamente sana a un navegador en esa misma dirección. Los acortadores y los envoltorios de enlaces delante de una URL de notificación caen en lo mismo.

El sutil es un registro DNS que apunta a la dirección equivocada. Cuando el registro A de un endpoint lleva la dirección que se ve desde dentro en vez de la que ve el mundo, el destino cae en un rango bloqueado y no sale nada del edificio.

Ninguno de estos se anuncia. El comercio no ve notificaciones y abre un ticket diciendo que los webhooks no llegan; se depura primero el handler, y la respuesta estaba en la URL registrada.

¿Qué preguntarle a tu proveedor de pagos?

Pregunta dónde corre la comprobación, porque de esa salen todas las demás respuestas.

Un proveedor que valida la dirección resuelta lo dice sin que se lo pidan y nombra los rangos. Loopback y RFC 1918 son la mitad fácil; una respuesta que llega al espacio compartido y a las direcciones locales únicas de IPv6 salió de alguien que lee los RFC actuales, y no de reciclar una lista vieja. Pregunta si se siguen redirecciones automáticas, y si la conexión queda fijada a la dirección validada o se vuelve a resolver el nombre antes de abrirla. La última es de capacidad: qué le hace un destino rechazado a la entrega del resto de comercios de la plataforma.

Después lee la página de seguridad del proveedor y comprueba que se detiene donde se detiene la ingeniería. Una página que describe una defensa en términos más anchos que el componente que la impone es un riesgo distinto del que trata este artículo, y es el que puedes auditar desde fuera.

Si estás en el otro lado y construyes el emisor, lo difícil de todo el control es que calla cuando funciona y parece un fallo cuando salta. Haz que el rechazo diga qué regla tocó, y díselo al comercio en la respuesta de la API, no solo a tu propio log.

Dónde puede estar la comprobación y qué se le escapa (septiembre de 2026)
Dónde compruebaQué veQué se le cuela
En el texto de la URLLo que el cliente escribió en el formularioUn nombre cuyo registro DNS apunta hacia dentro
En el nombre de hostSi el nombre es uno que ya conocesCualquier nombre que no conozcas, resuelva donde resuelva
En las direcciones resueltasTodos los registros A y AAAA del nombreUna redirección devuelta en la respuesta
En cada conexión, en cada intentoLa dirección que esta petición va a tocarLo que la lista de rangos se dejó fuera

Preguntas frecuentes

¿Qué es un SSRF por webhook?

Falsificación de peticiones del lado del servidor a través del campo de la URL de notificación. El destino lo elige el cliente y la conexión la abre un proceso de dentro de tu red, lo que le presta a esa petición una posición de red que el cliente no tiene.

¿Puedo apuntar una URL de webhook a localhost para probar?

En un emisor que bloquea loopback, no. Paymos rechaza los destinos de loopback, privados, link-local, de CGNAT y ULA de IPv6, así que el desarrollo local necesita un túnel que ponga un nombre público delante del handler.

¿Paymos sigue las redirecciones al entregar un webhook?

No. Las redirecciones HTTP automáticas no se siguen, así que la URL que registras tiene que ser la definitiva y no una que rebote hacia ella.

¿Basta con bloquear 127.0.0.1?

No. Un nombre de host resuelve adonde diga su registro DNS, así que la comprobación tiene que mirar las direcciones resueltas, y la lista de rechazo tiene que llegar a los rangos privados, al link-local, al CGNAT y a las direcciones locales únicas de IPv6.

¿Por qué dejaron de llegarme los webhooks al cambiar la URL?

Sin HTTPS la URL ni se guarda. Un destino en rango bloqueado se rechaza sin conectar, pero con una redirección la petición sí sale y el `3xx` cuenta como intento fallido. Comprueba que el nombre resuelve a una dirección pública y que es la URL final.

Fuentes

  1. 1. OWASP Top 10:2025 — A01 Broken Access Control (accessed 2026-09-15)
  2. 2. OWASP — Server Side Request Forgery Prevention Cheat Sheet (accessed 2026-09-15)
  3. 3. RFC 6598 — espacio de direcciones compartido reservado por IANA (accessed 2026-09-15)
  4. 4. RFC 4193 — direcciones IPv6 unicast locales únicas (accessed 2026-09-15)
  5. 5. Microsoft Learn — HttpClientHandler.AllowAutoRedirect (accessed 2026-09-15)
  6. 6. Paymos — documentación de webhooks (accessed 2026-09-15)

Última revisión: 15 sept 2026

#webhooks#ssrf#seguridad#urls-de-notificacion
Compartir