Назад | Перейти на главную страницу

Защита прокси Nginx

Я использую Nginx в качестве прокси для веб-службы Java.

Моя конфигурация выглядит так:

location /webservice {
    proxy_read_timeout 240;
    proxy_connect_timeout 240;
    proxy_pass      http://127.0.0.1:8080/;
}

В своих журналах я вижу много таких записей:

xx.xx.xx.xx - - [18/Oct/2011:02:44:23 +0000] "GET http://l04.member.in2.yahoo.com/config/login?login=email@example.com&passwd=password HTTP/1.0" 200 9 "-" "Mozilla/4.0 (compatible; MSIE 5.0; Series60/2.8 Nokia6630/4.06.0 Profile/MIDP-2.0 Configuration/CLDC-1.1)"

Я провел некоторое тестирование, насколько я могу видеть свой прокси не пересылка запросов на внешние сайты, но Я хотел бы заблокировать все эти запросы и / или вернуть код статуса, отличный от 200.

Я сделал это:

if ($request_method !~ ^(GET|HEAD|POST)$ ) { return 444; }

который блокирует попытки CONNECT. Любые идеи (помимо блокировки IP) будут оценены.

Nginx будет принимать соединения и обрабатывать их на основе соответствия server_name (которое проверяется на соответствие заголовку Host). Nginx поставляется с серверным блоком по умолчанию, настроенным для соответствия всем хостам. Это позволяет обрабатывать любой запрос, поступающий на сервер.

Мне нравится настраивать проверку блока сервера для пустого заголовка Host, а также настраивать сервер по умолчанию для возврата ошибки 403 (например, если вы пытаетесь получить доступ к моему серверу через его IP-адрес). Затем каждый виртуальный хост получает свою собственную конфигурацию (т.е. любой допустимый хост соответствует конфигурации, все остальные либо попадают в блок сервера по умолчанию, либо в пустой блок хоста).

Сервер для проверки пустого хоста:

server {
    listen       80;
    server_name  "";
    return       444;
}

Сервер для перебрасывания 403 на все ненастроенные хосты:

server {
        listen       80;
        server_name  _;
        root /path/to/error/files;
        error_page 403 /403.html;
                location  /403.html {
                allow all;
        }
        deny all;
}

Следует отметить, что указанная выше директива listen необязательна (nginx по умолчанию прослушивает порт 80), но мой nginx работает за varnish, поэтому фактически не прослушивает порт 80.

В вашем случае вы добавите третий сервер, который будет обрабатывать ваши запросы обратного прокси:

server {
server_name mydomain.com;
...your other blocks...
}

Вы можете протестировать свою конфигурацию различными способами (я уверен, что их больше, но в данный момент они приходят на ум):

(Я использую google.com в качестве тестового домена ниже, измените его на ваш сайт по выбору):

Укажите весь запрос за один раз:

telnet mydomain.com 80
GET http://google.com

Укажите отдельно заголовок запроса и хоста:

telnet mydomain.com 80
GET / HTTP/1.1
Host: google.com

Установите запись в вашем файле hosts (на вашем сервере):

127.0.0.1 google.com

Используйте curl, чтобы попытаться получить страницу:

curl google.com

(В этом случае файл hosts сообщает вашему серверу, что на вашем компьютере можно найти google.com - который получает запрос к nginx - удалите запись после завершения тестирования.)

Редактировать: Похоже, что непреднамеренным следствием вышеизложенного является то, что недопустимые запросы приводят к ошибке 400. Вы можете определить основную причину этого, если хотите, добавив параметр info в свою директиву error_log. В моем случае с 400 ошибками, которые я видел, были связаны следующие причины:

Использование telnet с однострочным запросом GET (без заголовка хоста):

client sent invalid request while reading client request line

Произведены случайные (нестандартные) запросы:

client sent invalid method while reading client request line

Используя telnet, ждал слишком долго:

client timed out (110: Connection timed out) while reading client request headers

Другими частыми причинами были:

client sent invalid host header while reading client request headers
recv() failed (104: Connection reset by peer) while reading client request line
client closed prematurely connection while reading client request line

Использование curl привело к ожидаемой ошибке 444. Я полагаю, что у действительного запроса есть дополнительный синтаксис. Во всяком случае, насколько я понимаю, 400 ошибок обрабатываются раньше, чем 444, поэтому вполне вероятно, что они не исчезнут для действительно недействительных запросов.

Я смог успешно получить ошибку 444 с помощью telnet, хотя для этого потребовалось немного изменить мою конфигурацию:

server {
    listen       80 default;
    server_name  _ "";
    return       444;
}

Обратите внимание, что в приведенном выше «неуказанном имени сервера» (подчеркивание) и пустой хост (двойные кавычки) явно не определяется сервер по умолчанию, поэтому вы должны добавить «по умолчанию» в строку прослушивания.

Выход Telnet:

telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1
Host: google.com

Вывод журнала доступа:

127.0.0.1 - - [19/Oct/2011:00:51:16 -0400] "GET / HTTP/1.1" 444 0 "-" "-" "-"

Я бы порекомендовал вам уменьшить размер отправленных заголовков (длина URL).

Пожалуйста, взгляните на client_header_buffer_size и large_client_header_buffers

Ограничение URI клиента - распространенный способ предотвратить отправку сканером или неисправным клиентом больших запросов, которые могут вызвать переполнение буфера.

Итак, если вы настроите large_client_header_buffers 1 1k ваша служба nginx не будет принимать URI, размер которых превышает (1x1K = 1K) 1 килобайт данных (включая файлы cookie).

Дополнительно вы можете настроить ignore_invalid_headers off если вы не ожидаете получить какие-либо нестандартные заголовки.