Kod 444. Jak inteligentnie limitować boty w Nginx i uratować procesor serwera przed zawałem?

Autor:

w

Każdy administrator, który utrzymywał kiedykolwiek ciężki sklep oparty na PrestaShop, Magento czy rozbudowanym WordPressie, zna ten scenariusz. Masz serwer VPS (np. 4 rdzenie, 8 GB RAM), wszystko działa płynnie, klienci kupują. Nagle użycie procesora skacze do 100%, pamięć RAM zostaje pożarta, a żywi użytkownicy widzą na ekranach białą stronę śmierci lub błąd 502 Bad Gateway.

Co się stało? Prawdopodobnie jakiś zautomatyzowany bot, skaner podatności albo chińska wyszukiwarka postanowiła „przeklikać” w jednej sekundzie wszystkie 500 kombinacji filtrów wyszukiwania w Twoim sklepie. Każde takie kliknięcie budzi ciężki proces PHP, który z kolei katuje bazę danych. Serwer „klęka”.

Większość poradników w sieci sugeruje wtedy włączenie globalnego limitowania zapytań (Rate Limiting) w Nginx. To najszybsza droga do katastrofy i odcięcia prawdziwych klientów (tzw. False Positives). Dlaczego? Ponieważ wejście na jedną stronę produktu generuje często ponad 100 zapytań o pliki CSS, JS i obrazki. Jeśli utniesz to globalnie – sklep przestanie się ładować.

W architekturze BSNM podchodzimy do tego inaczej. Używamy precyzyjnego skalpela Nginxa, buforowania i kodu 444.

Dlaczego kod 429 to błąd, a 444 to wybawienie?

Kiedy serwer odrzuca nadmiarowy ruch, zazwyczaj wysyła botowi standardowy kod HTTP 429 (Too Many Requests) lub 403 (Forbidden). Problem w tym, że wygenerowanie takiej odpowiedzi wymaga od serwera pracy: musi spakować nagłówki, wygenerować stronę błędu HTML i wysłać ją przez sieć. Ty tracisz zasoby, a bot dostaje jasny sygnał: „Aha, serwer tu jest, działa, tylko ma zadyszkę. Poczekam i uderzę znowu”.

My posłużymy się wewnętrzną, niestandardową dyrektywą Nginxa: return 444; (Connection Closed Without Response).
Ten kod mówi serwerowi: „Natychmiast zamknij gniazdo TCP. Nie wysyłaj absolutnie żadnych nagłówków, nie mów do widzenia”. Dla bota wygląda to tak, jakby ktoś nagle przeciął kabel. Skrypty atakujących często się na tym zawieszają, wyczekując w nieskończoność na odpowiedź, podczas gdy Twój serwer natychmiast uwalnia wątek i wraca do obsługiwania klientów. Zero zużycia procesora.

Jak zaimplementować to w BSNM?

W środowisku BSNM, gdzie stawiamy na bezpieczeństwo i izolację, usługa WWW jest bezpiecznie zamknięta w klatce (Jail). Aby skonfigurować inteligentne limity, wyedytujemy plik konfiguracyjny Nginxa bezpośrednio z poziomu naszego hosta (FreeBSD).

Plik konfiguracyjny znajdziesz pod tą ścieżką:

/usr/local/bastille/jails/nginx_jail/root/usr/local/etc/nginx/10_bsn_nginx.conf

Zbudujemy nasz system anty-botowy w trzech warstwach.

Warstwa 1: Definicja stref i wyłapywanie POST

Zanim nałożymy limity, musimy powiedzieć Nginxowi, jak ma przydzielać pamięć na śledzenie adresów IP. Chcemy stworzyć dwie strefy: jedną ogólną dla PHP, a drugą rygorystyczną dla zapytań typu POST (np. formularze logowania i ciężkie wyszukiwarki).

Na samej górze pliku konfiguracyjnego (w sekcji http {}, przed definicjami serwerów) dodajemy:

# Mapujemy metodę POST do adresu IP. Dla GET zwracamy puste pole (brak limitu w tej strefie)
map $request_method $limit_post {
    default         "";
    POST            $binary_remote_addr;
}

# Strefa 1: Ogólny limit dla skryptów PHP (10 zapytań na sekundę)
limit_req_zone $binary_remote_addr zone=php_limit:10m rate=10r/s;

# Strefa 2: Limit dla ataków Bruteforce / POST (30 zapytań na minutę, czyli 1 na 2 sekundy)
limit_req_zone $limit_post zone=post_limit:10m rate=30r/m;

Warstwa 2: Immunitet dla plików statycznych

To absolutny fundament. Zdjęcia, fonty, CSS i JS muszą ładować się bez żadnych limitów prędkości, aby żywi klienci mieli błyskawiczne czasy ładowania (nawet jeśli strona waży 5 MB i ma 200 obrazków). Zostawiamy bloki lokalizacji statycznych w spokoju. Nie dodajemy tam żadnych dyrektyw limit_req.

Warstwa 3: Bufor i pułapka w bloku PHP

Cała magia dzieje się tam, gdzie Nginx przekazuje żądanie do interpretera, czyli w bloku location ~ .php$. Wklejamy tam nasze limity.

Zwróć szczególną uwagę na dopiski burst oraz nodelay.

location ~ \.php$ {
    # Nakładamy rygorystyczny limit na POST (burst=3 to mały margines błędu)
    limit_req zone=post_limit burst=3 nodelay;
    
    # Nakładamy ogólny limit na PHP (pozwalamy na skok 20 jednoczesnych zapytań dla prawdziwych userów)
    limit_req zone=php_limit burst=20 nodelay;
    
    # Kiedy limit zostanie przekroczony - zrzucamy połączenie w eter
    limit_req_status 444;

    # ... tutaj znajduje się Twoja standardowa konfiguracja fastcgi_pass
}

Dlaczego ta konfiguracja to fundament? (Jak działa „Burst”)

Pewnie zastanawiasz się, po co nam parametr burst=20. To jest nasze „wiadro bezpieczeństwa” dla prawdziwych użytkowników.

Wyobraź sobie, że wejście na stronę koszyka wywołuje główny plik index.php, a szablon ładuje w tle jeszcze 4 zapytania AJAX do innych plików PHP. Gdybyśmy nałożyli „sztywny” limit 10 zapytań na sekundę, szybkie klikanie klienta zablokowałoby mu stronę.

Parametr burst=20 sprawia, że Nginx potrafi przyjąć nagły skok do 20 żądań bez nakładania bana, o ile średnia w dłuższym czasie nie przekracza zadeklarowanych 10/sekundę. Z kolei nodelay zapewnia, że te 20 dozwolonych żądań obsłuży się bez sztucznych opóźnień (lagów).

Żaden człowiek nie wygeneruje 25 zapytań do silnika PHP w ułamku sekundy. Zrobi to tylko skrypt botnetu przeczesujący filtry. I w tym ułamku sekundy, gdy bot przepełni bufor burst, spotka się ze ścianą – Nginx zwróci 444, chamsko urywając połączenie.

Podsumowanie

Limitowanie po adresach URL w dobie nowoczesnych CMS-ów przypomina walkę z wiatrakami. Filtrowanie po typie zachowania (odpuszczamy statykę, chronimy PHP z marginesem błędu, rygorystycznie tniemy ataki POST) to rozwiązanie ostateczne.

Dzięki połączeniu izolacji w klatce BSNM, brutalności kodu 444 i inteligencji buforów, Twój serwer przestanie tracić moc na walkę z internetowym szumem, a procesor znów będzie mógł zająć się tym, czym powinien: obsługiwaniem realnych klientów.

BSNM
Przegląd prywatności

Ta strona korzysta z ciasteczek, aby zapewnić Ci najlepszą możliwą obsługę. Informacje o ciasteczkach są przechowywane w przeglądarce i wykonują funkcje takie jak rozpoznawanie Cię po powrocie na naszą stronę internetową i pomaganie naszemu zespołowi w zrozumieniu, które sekcje witryny są dla Ciebie najbardziej interesujące i przydatne.