Nginx-антиDDoS

Несколько раз натыкался на “велосипеды”, выполненные в виде php-скриптов, проверяющих, сколько раз с некоего IP подряд обратились к сайту и выдающих ошибку 503 или банящих тех, кто долбится слишком часто.

Для пользователей дешевого виртуального хостинга это решение, в принципе, безальтернативно. Хотя раз уж это php-скрипт, то и сам этот скрипт при среднетиповой распределенной DoS-атаке может создать хорошую такую нагрузку.

А вот при помощи модуля ngx_http_limit_req_module веб-сервера Nginx даже на самом скромном VPS можно реализовать нечто куда более красивое, простое и эффективное. Это, конечно, не замена аппаратным решениям, но все равно полезная штука, да еще с парой приятных “побочных эффектов”.

Идем в nginx.conf, секция http. Добавляем:

limit_req_zone $binary_remote_addr zone=two:10m rate=2r/s;

two — это название 10-мегабайтной зоны, где будет храниться айпишник посетителя. 2r/s означает, что нормальному здоровому посетителю разрешено запрашивать две страницы в секунду (это подбирается индивидуально, например, в зависимости от среднего времени генерации ваших страниц или активности DDoS-еров).

Идем к нужной секции server. И в каждый location, число обращений к которому необходимо ограничить, добавляем:

limit_req zone=two burst=6;

Параметр burst задает, насколько большим должен быть всплеск числа обращений с одного IP, чтобы выдать этому IP-шнику ошибку 503. В нашем случае это шесть обращений.

Оцените красоту решения:

  1. если один и тот же пользователь случайно кликнет три раза по одной внутренней ссылке на сайте, Nginx просто заставит его выждать соответствующий промежуток времени и только потом отработает запрос (например, передаст его FastCGI);
  2. если один и тот же пользователь случайно или специально кликнет подряд по нескольким внутренним ссылкам (например, любитель покликать, не дожидаясь загрузки страниц), Nginx заставит его выждать соответствующий промежуток времени (пропорциональный числу бессмысленных нажатий) и только потом отработает запрос (причем, как я имел возможность убедиться, только последний).
  3. если ошибку 503 получит, например, какой-нибудь чумовой робот поисковой системы, то он просто поймет, что надо зайти попозже и ничего страшного с положением сайта в поисковиках не случится;
  4. если сайт находится под DDoS-атакой, в конце каждого дня (или в конце каждого часа) можно просто проанализировать лог ошибок и забанить IP, вызывающие ошибку 503, на уровне файерволла или веб-сервера.

Наибольший восторг вызывают у меня пункты 1 и 2, поскольку, несмотря на внешнюю простоту, нагрузка на тот же FastCGI (или Apache, если Nginx используется как фронтэнд для Apache) в результате может быть заметно снижена.

Живые пользователи, делающие слишком частые запросы, в общем случае вообще ничего не заметят, просто загрузка очередной страницы замедлится для них лишь на доли секунды. Плюс не будут отрабатываться случайные двойные или тройные клики от тех, кто не знает, что на сайте двойные и тройные клики нахрен не нужны.

Нормальных же пользователей, которые заходят на сайт не только чтобы покликать, но и почитать, это вообще не коснется. Для них, возможно, сайт будет работать быстрее: ведь FastCGI/Apache в результате будет меньше заниматься фигней.

Кстати, фигня фигней, а сайт на дохленьком хостинге бессмысленными частыми обращениями к одной и той же странице (или к нескольким страницам подряд) можно вообще “уложить” за считанные минуты. А если не уложить, то серьезно затормозить. И это без всяких там сложносочиненных атак, а просто резво кликая мышкой.

Благодаря вышеописанному решению проблем можно легко избежать, просто не разрешая выдавать для одного пользователя больше страниц, чем сервер в состоянии выдать.