Многие сталкивались с таким явлением как DDoS атака методом HTTP флуда. Нет, это не очередной туториал по настройке nginx, хочу представить свой модуль, работающий как быстрый фильтр между ботами и бэкэндом во время L7 DDoS атаки и позволяющий отсеивать мусорные запросы.
Модуль умеет
- Ставить cookies стандартным способом через HTTP заголовок Set-Cookie. После установки cookies перенаправлять пользователя (заставить отправить нам полученные cookies) используя код ответа 301 и заголовок Location
- После установки cookies перенаправлять пользователя (заставить отправить нам полученные cookies) используя код ответа 200 и HTML тег Meta «refresh»
- Считать количество попыток поставить cookies и отправлять пользователя по заданному URL после превышения максимального количества неудачных попыток
- Использовать custom шаблоны для ответа фильтра, в которых можно делать что угодно (например, выставить cookies через JavaScript)
- Для предотвращения автоматического парсинга ответов нацеленных на исполнение JavaScript, шифровать значение переменных в шаблоне симметричным криптоалгоритмом с дальнейшей расшифровкой используя JavaScript на стороне клиента (используя SlowAES)
- Whitelist'ить заданные сети (например, сети в которых живут поисковые роботы)
- Кое-какие мелкие фишки полезные во время DoS атаки.
Не умеет
- Модуль лишь возвращает клиенту заданные ответы, принимать решение о блокировке клиента (например, используя fail2ban) вы должны самостоятельно
- Кто-то скажет — «Я сэмулирую JavaScript», но давайте будем реалистами — часто вас DoS'ят ботами с полноценной эмуляцией?
отправьте их ко мне, будем майнить bitcoin'ы - В документации ничего нет про captcha и flash — если нужно, вы можете прикрутить их самостоятельно, нужно лишь проявить фантазию при конфигурации
- Данный модуль не панацея — это лишь маленький компонент в комплексе защитных мер, инструмент, который может помочь, если его правильно использовать.
Как это работает
Чаще всего, боты, реализующие HTTP флуд довольно тупые, и не имеют механизмов HTTP Cookie и редиректа. Иногда попадаются более продвинутые — такие могут использовать cookies и обрабатывать редиректы, но почти никогда DoS бот не несет в себе полноценного JavaScript движка.
Для понимания принципа работы фильтра ниже приведен flow общения клиент-сервер в зависимости от сценария атаки.
Примеры конфигурации для основных сценариев атак
- боты не понимают редиректы и cookies (типичный случай)
server { listen 80; server_name domain.com; testcookie off; testcookie_name BPC; testcookie_secret keepmescret; testcookie_session $remote_addr; testcookie_arg attempt; testcookie_max_attempts 3; testcookie_fallback /cookies.html?backurl=http://$host$request_uri; testcookie_get_only on; location = /cookies.html { root /var/www/public_html; } location / { testcookie on; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://127.0.0.1:8080; } }
- боты понимают редиректы и cookies
server { listen 80; server_name domain.com; testcookie off; testcookie_name BPC; testcookie_secret keepmescret; testcookie_session $remote_addr; testcookie_arg attempt; testcookie_max_attempts 3; testcookie_fallback /cookies.html?backurl=http://$host$request_uri; testcookie_get_only on; testcookie_redirect_via_refresh on; testcookie_refresh_template '<html><body><script>document.cookie="BPC=$testcookie_set";document.location.href="$testcookie_nexturl";</script></body></html>'; location = /cookies.html { root /var/www/public_html; } location / { testcookie on; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://127.0.0.1:8080; } }
- защищаемый URL вставили в iframe на популярном сайте
server { listen 80; server_name domain.com; testcookie off; testcookie_name BPC; testcookie_secret keepmescret; testcookie_session $remote_addr; testcookie_arg attempt; testcookie_max_attempts 3; testcookie_fallback /cookies.html?backurl=http://$host$request_uri; testcookie_get_only on; testcookie_redirect_via_refresh on; testcookie_refresh_template '<html><body><script>function bla() { document.cookie="BPC=$testcookie_set";document.location.href="$testcookie_nexturl";}</script><input type="submit" value="click me" onclick="bla();"></body></html>'; location = /cookies.html { root /var/www/public_html; } location / { testcookie on; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://127.0.0.1:8080; } }
- боты научились вытаскивать значение cookies через regexp
server { listen 80; server_name domain.com; testcookie off; testcookie_name BPC; testcookie_secret keepmescret; testcookie_session $remote_addr; testcookie_arg attempt; testcookie_max_attempts 3; testcookie_fallback /cookies.html?backurl=http://$host$request_uri; testcookie_get_only on; testcookie_redirect_via_refresh on; testcookie_refresh_encrypt_cookie on; testcookie_refresh_encrypt_cookie_key random; testcookie_refresh_encrypt_cookie_iv random; testcookie_refresh_template '<html><body>setting cookie...<script type="text/javascript" src="/aes.min.js" ></script><script>function toNumbers(d){var e=[];d.replace(/(..)/g,function(d){e.push(parseInt(d,16))});return e}function toHex(){for(var d=[],d=1==arguments.length&&arguments[0].constructor==Array?arguments[0]:arguments,e="",f=0;f<d.length;f++)e+=(16>d[f]?"0":"")+d[f].toString(16);return e.toLowerCase()}var a=toNumbers("$testcookie_enc_key"),b=toNumbers("$testcookie_enc_iv"),c=toNumbers("$testcookie_enc_set");document.cookie="BPC="+toHex(slowAES.decrypt(c,2,a,b))+"; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/";document.location.href="$testcookie_nexturl";</script></body></html>'; location = /aes.min.js { gzip on; gzip_min_length 1000; gzip_types text/plain; root /var/www/public_html; } location = /cookies.html { root /var/www/public_html; } location / { testcookie on; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://127.0.0.1:8080; } }
- боты научились вытаскивать параметры через regexp и расшифровывать значение cookies (сомневаюсь, что кто-то будет возиться)
server { listen 80; server_name domain.com; testcookie off; testcookie_name BPC; testcookie_secret keepmescret; testcookie_session $remote_addr; testcookie_arg attempt; testcookie_max_attempts 3; testcookie_fallback /cookies.html?backurl=http://$host$request_uri; testcookie_get_only on; testcookie_redirect_via_refresh on; testcookie_refresh_encrypt_cookie on; testcookie_refresh_encrypt_cookie_key deadbeefdeadbeefdeadbeefdeadbeef; #меняем по крону testcookie_refresh_encrypt_cookie_iv deadbeefdeadbeefdeadbeefdeadbeef; #меняем по крону testcookie_refresh_template '<html><body>setting cookie...<script type="text/javascript" src="/aes.min.js" ></script><script>function toNumbers(d){var e=[];d.replace(/(..)/g,function(d){e.push(parseInt(d,16))});return e}function toHex(){for(var d=[],d=1==arguments.length&&arguments[0].constructor==Array?arguments[0]:arguments,e="",f=0;f<d.length;f++)e+=(16>d[f]?"0":"")+d[f].toString(16);return e.toLowerCase()}var a=toNumbers({используем любимый JS обфускатор чтобы спрятать значение iv, меняем по крону}),b=toNumbers({используем любимый JS обфускатор чтобы спрятать значение ключа, меняем по крону}),c=toNumbers("$testcookie_enc_set");document.cookie="BPC="+toHex(slowAES.decrypt(c,2,a,b))+"; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/";document.location.href="$testcookie_nexturl";</script></body></html>'; location = /aes.min.js { gzip on; gzip_min_length 1000; gzip_types text/plain; root /var/www/public_html; } location = /cookies.html { root /var/www/public_html; } location / { testcookie on; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://127.0.0.1:8080; } }
Исходные тексты
Модуль с инструкциями по установке и документацией доступен на github под BSD лицензией.
Патчи, дополнения, баг-репорты приветствуются.
Автор: kyprizel