Una avalancha de scrapers en el plan gratuito de Cloudflare: qué la detuvo y qué dejamos desactivado
Operaciones
Una mañana, uno de nuestros sitios de fichas de producto dejó de abrir. Cloudflare mostraba a los visitantes un error 524, el origen tardaba entre dos y seis minutos por página, y nada nos avisó durante unas diez horas. La causa fue un scraper que pasaba por proxies residenciales a unos 225 solicitudes por segundo.
La forma de la avalancha
- Miles de direcciones, una a tres solicitudes cada una. Conexiones de banda ancha domésticas en EE. UU., España, Francia, Brasil, el Reino Unido y otros lugares, el tipo de tráfico que alquila una red de proxies residenciales.
- User agents de Chrome en Mac, con números de versión del 103 al 151.
- Casi nunca una cookie o un Referer. De 2.697 solicitudes que muestreamos en el servidor, 4 llevaban una cookie y 4 un Referer.
- Dos tipos de cliente. Aproximadamente la mitad no enviaba encabezados
Sec-Fetch-*ni client hints: un script con una cadena de user agent copiada. La otra mitad enviaba encabezados completos de navegador: un navegador headless.
Cada página se renderizaba para cada solicitud, por lo que cada worker encolaba cientos de renderizados y alcanzaba su límite de CPU.
Por qué los límites por dirección no podían ayudar
Un límite de frecuencia cuenta las solicitudes por dirección. Con una a tres solicitudes de cada una de miles de direcciones, ninguna dirección se acerca nunca a un límite. Bloquear países habría bloqueado a nuestros visitantes. Lo que el scraper no podía fingir de forma barata era comportarse como un navegador que ya había visitado el sitio.
En el edge de Cloudflare: tres reglas gratuitas
El plan gratuito permite cinco reglas personalizadas de WAF por zona. Usamos tres, todas con un desafío gestionado en lugar de un bloqueo, para que una persona atrapada por error haga clic y pase:
- Navegador falso: una GET cuyo user agent contiene
Chrome/y que no tiene encabezadoSec-Fetch-Mode. Todo Chrome desde 2019 lo envía. - Nubes de scrapers: una solicitud de página desde las redes de Alibaba, Tencent y Huawei Cloud, de donde procedía gran parte de lo que desafiamos.
- Emergencia, desactivada por defecto: una GET sin ninguna cookie y sin Referer. Una persona que ha estado en el sitio, o que llega a través de un enlace, pasa sin cambios; un scraper que no guarda cookies se encuentra con un desafío en cada solicitud. Solo lo activamos durante una avalancha, porque también desafía a quien escribe la dirección en un navegador nuevo.
Las tres dejan pasar los bots verificados de Cloudflare, los user agents que se identifican como robots, nuestras propias direcciones, las rutas de la API y de los webhooks, y los archivos que no son páginas. Dos días después, la inundación seguía: en una hora, la regla de navegador falso había desafiado a más de 10.000 solicitudes. Los 10.000 eventos que Cloudflare devuelve por consulta provenían de 9.627 direcciones, ninguna con más de 4.
En el origen: una barrera en la app
La mitad sin interfaz gráfica tiene cabeceras de navegador completas, por lo que las reglas del borde no la detectan mientras la regla de emergencia está desactivada. En la propia app, colocamos un pequeño middleware delante del renderizador de páginas:
- un user agent de Chrome sin
Sec-Fetch-Modeni client hints recibe un 403 antes de que se realice cualquier trabajo; - un desconocido, sin ninguna de nuestras cookies y sin Referer, solo recibe contenido mientras el worker tiene capacidad de sobra, a unas pocas páginas por segundo. Por encima de ese límite, una página 503 de 400 bytes establece una cookie y recarga tras dos segundos. Una persona obtiene la página al segundo intento; un scraper que no guarda cookies nunca lo consigue.
En su primer minuto, la barrera rechazó unos 6.700 navegadores falsos y apartó a unos 6.700 desconocidos, mientras servía a los desconocidos unas 9 páginas por segundo. Las solicitudes lentas cayeron a cero, la CPU de los workers bajó de su límite al 12-15 por ciento, y su memoria de 4-5 GB cada uno a 0,65 GB.
Lo que dejamos desactivado
- Bot Fight Mode. Desafía al tráfico automatizado y no se puede omitir con una regla personalizada en el plan Free, y bloqueaba los webhooks de nuestro proveedor de pagos.
- Modo Under Attack para toda la zona. Pasa a cada visitante, cliente de la API y webhook por la página intermedia. La regla de emergencia hace lo mismo solo para las solicitudes de páginas de desconocidos.
- La regla de limitación de frecuencia del plan Free. Cuenta por dirección, algo que esta inundación nunca activa.
- Bloquear los rastreadores de IA. Queremos que nos encuentren los asistentes de búsqueda de IA, y esos rastreadores no eran el problema.
La conclusión
Una inundación a través de proxies residenciales es fácil de detectar por lo que le falta: cabeceras de fetch del navegador, cookies y Referers. Desafíala en el borde, mantén a los desconocidos dentro de lo que tus workers pueden renderizar y asegúrate de que algo externo al servidor se dé cuenta cuando una página deja de abrirse. Night Watch empaqueta las tres cosas: la vigilancia, las reglas con un interruptor automático para la de emergencia, y la barrera para apps web en Python.