Zalanie scraperem na darmowym planie Cloudflare: co je zatrzymało i co zostawiliśmy wyłączone
Operacje
Pewnego ranka nasza strona z opisami produktów przestała się otwierać. Cloudflare wyświetlał odwiedzającym błąd 524, serwer źródłowy potrzebował od dwóch do sześciu minut na stronę, a przez około dziesięć godzin nic nam tego nie zgłaszało. Przyczyną był scraper korzystający z proxy domowych przy około 225 żądaniach na sekundę.
Kształt zalewu
- Tysiące adresów, po jednym do trzech żądań z każdego. Łącza domowe w USA, Hiszpanii, Francji, Brazylii, Wielkiej Brytanii i innych krajach – taki ruch wynajmują sieci proxy domowych.
- Agent użytkownika Chrome na Macu, z numerami wersji od 103 do 151.
- Prawie nigdy plik cookie ani Referer. Z 2697 żądań, które próbowaliśmy na serwerze, 4 zawierały plik cookie, a 4 Referer.
- Dwa typy klientów. Około połowa nie wysyłała nagłówków
Sec-Fetch-*ani wskazówek klienta: był to skrypt ze skopiowanym ciągiem agenta użytkownika. Druga połowa wysyłała pełne nagłówki przeglądarki: przeglądarka headless.
Każda strona była renderowana dla każdego żądania, więc każdy worker kolejkował setki renderów i uderzał w limit CPU.
Dlaczego limity na adres nie mogły pomóc
Limit częstotliwości liczy żądania na adres. Przy jednym do trzech żądań z każdego z tysięcy adresów żaden pojedynczy adres nigdy nie zbliża się do limitu. Blokowanie krajów zablokowałoby naszych odwiedzających. Tego, czego scraper nie mógł tanio podrobić, było zachowanie się jak przeglądarka, która już była na stronie.
Na krawędzi Cloudflare: trzy darmowe reguły
Plan darmowy pozwala na pięć własnych reguł WAF na strefę. Użyliśmy trzech, wszystkie z wyzwaniem zarządzanym zamiast blokady, więc osoba złapana przez pomyłkę może przejść dalej:
- Fałszywa przeglądarka: żądanie GET, którego agent użytkownika zawiera
Chrome/i które nie ma nagłówkaSec-Fetch-Mode. Każda Chrome od 2019 roku go wysyła. - Chmury scraperów: żądanie strony z sieci Alibaba, Tencent i Huawei Cloud, skąd pochodziła większość tego, co rzuciliśmy wyzwanie.
- Awaryjna, domyślnie wyłączona: żądanie GET bez żadnego pliku cookie i bez Referer. Osoba, która była już na stronie lub która przychodzi przez link, przechodzi nietknięta; scraper, który nie przechowuje plików cookie, napotyka wyzwanie przy każdym żądaniu. Włączamy ją tylko podczas zalewu, ponieważ rzuca ona również wyzwanie komuś, kto wpisuje adres w nowej przeglądarce.
Wszystkie trzy przepuszczają zweryfikowane boty Cloudflare, user agenty, które same nazywają się robotami, nasze własne adresy, ścieżki API i webhooków oraz pliki, które nie są stronami. Dwa dni później fala wciąż trwała: w ciągu godziny reguła dla fałszywych przeglądarek rzuciła wyzwanie ponad 10 000 żądań. 10 000 zdarzeń zwracanych przez Cloudflare w jednym zapytaniu pochodziło z 9 627 adresów, z których żaden nie miał więcej niż 4.
Na serwerze źródłowym: bramka w aplikacji
Główna część ruchu pochodzi z przeglądarek bez interfejsu (headless) i ma pełne nagłówki przeglądarki, więc same reguły na krawędzi sieci nie przechwycą tego ruchu, gdy reguła awaryjna jest wyłączona. W samej aplikacji umieściliśmy małe oprogramowanie pośredniczące (middleware) przed renderowaniem strony:
- user agent Chrome bez nagłówka
Sec-Fetch-Modeani wskazówek klienta (client hints) otrzymuje kod 403, zanim wykonana zostanie jakakolwiek praca; - nieznajomy, bez naszych plików cookie i bez nagłówka Referer, jest obsługiwany tylko wtedy, gdy worker ma wolną przepustowość, przy kilku stronach na sekundę. Powyżej tego limitu strona błędu 503 o rozmiarze 400 bajtów ustawia plik cookie i przeładowuje się po dwóch sekundach. Osoba fizyczna dostanie stronę za drugim podejściem; scraper, który nie przechowuje plików cookie, nigdy jej nie dostanie.
W pierwszej minucie bramka odrzuciła około 6 700 fałszywych przeglądarek i odesłała około 6 700 nieznajomych, jednocześnie obsługując nieznajomych z prędkością około 9 stron na sekundę. Liczba wolnych żądań spadła do zera, zużycie CPU workerów spadło z limitu do 12–15 procent, a pamięci z 4–5 GB na każdy do 0,65 GB.
Co pozostawiliśmy wyłączone
- Bot Fight Mode. Rzuca wyzwanie zautomatyzowanemu ruchowi i na planie Free nie można go pominąć za pomocą niestandardowej reguły, a przy okazji złapał webhooki naszego operatora płatności.
- Tryb Under Attack dla całej strefy. Przepuszcza każdego odwiedzającego, klienta API i webhook przez stronę przejściową (interstitial). Reguła awaryjna robi to samo, ale tylko dla żądań stron od nieznajomych.
- Reguła limitowania żądań z planu Free. Liczy żądania na adres, czego ta fala nigdy nie wywołuje.
- Blokowanie crawlerów AI. Chcemy być znajdowani przez asystentów wyszukiwania AI, a te crawlery nie były problemem.
Wniosek
Falę przez proxy mieszkaniowe łatwo rozpoznać po tym, czego brakuje: nagłówków fetch przeglądarki, plików cookie i nagłówków Referer. Rzuć jej wyzwanie na krawędzi sieci, utrzymaj nieznajomych w granicach tego, co twoje workerzy są w stanie wyrenderować, i upewnij się, że coś poza serwerem zauważy, gdy strona przestanie się otwierać. Night Watch pakuje wszystkie trzy elementy: watchdog, reguły z automatycznym przełącznikiem dla tej awaryjnej oraz bramkę dla aplikacji webowych w Pythonie.