nginx: как предотвратить использование точно названного блока сервера SSL в качестве ловушки для всех SSL


17

У меня есть веб-сервер со многими виртуальными серверами. Только 1 из которых является SSL. Проблема заключается в том, что из-за того, что серверный блок catchall не прослушивает SSL, любой запрос https на другие сайты обслуживается блоком 1 SSL.

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

# the catch all
server {
  listen 80 default;

  # I could add this, but since I have no default cert, I cannot enable SSL,
  # and this listen ends up doing nothing (apparently).
  # listen 443; 

  server_name _;
  # ...
}

# some server
server {
  listen 80;
  server_name server1.com;
  # ...
}

# some other server ...
server {
  listen 80;
  server_name server2.com;
  # ...
}

# ... and it's https equivalent
server {
  listen 443;
  ssl on;
  server_name server2.com;
  # ...
}

Теперь, когда для 443 нет прослушивателя по умолчанию, такой запрос в https://server1.comитоге будет обслуживаться server2.comблоком https. Это следует логике для server_nameв документах.

Если совпадений нет, будет использован блок сервера {...} в файле конфигурации в следующем порядке:

  1. блок сервера с соответствующей директивой прослушивания, помеченной как [default | default_server]
  2. первый серверный блок с соответствующей директивой прослушивания (или неявным прослушиванием 80;)

Какое решение для этой проблемы является предпочтительным? Нужно ли мне устанавливать фиктивный сертификат для моего блока перехвата всех серверов, чтобы я мог прослушивать 443 и обрабатывать неправильные запросы? Есть ли параметр, о котором я не знаю, который заставляет точное имя хоста соответствовать server?


Что вы хотите случиться, когда люди пытаются получить доступ к другим сайтам, используя https?
Дэвид Шварц

В идеале, я бы хотел, чтобы nginx вообще не обслуживал https, если не совпадает имя хоста, или чтобы он перенаправлялся на http на том же хосте.
1311407

Ответы:


9

В идеале, я бы хотел, чтобы nginx вообще не обслуживал https, если не совпадает имя хоста, или чтобы он перенаправлялся на http на том же хосте.

Ни то, ни другое невозможно. Соединение от клиента, которое идет к https://foo.example.com/, не может быть принято ничем, кроме сертификата SSL с foo.example.com в качестве одного из его имен. Нет возможности перенаправить, пока не будет принято SSL-соединение.

Если вы настраиваете каждый сайт для SSL, пользователь, который щелкает по ошибке сертификата, получит запрошенный сайт. Если вы настраиваете сайт «поймать все» для SSL, который предоставляет только страницу ошибок, и настраиваете виртуальный хостинг на основе имен для одного сайта, который должен поддерживать SSL, вы можете предоставить эту страницу клиентам.

Виртуальный хостинг SSL и HTTP просто не играют друг с другом.


Это то, что я собрал после прочтения документов. Я просто надеялся, что что-то пропустил. Я не забочусь о предупреждениях SSL вообще. Я просто не хочу, чтобы кто-то заходил на server1.com и смотрел на домашнюю страницу server2.com ... Неужели нет способа сказать nginx, что он не принимает запрос?
1311407

Если он не примет запрос, первый сайт не будет работать. Он должен принять запрос, чтобы выяснить, к какому сайту пользователь пытается получить доступ.
Дэвид Шварц

2
«Соединение от клиента, которое идет к foo.example.com, не может быть принято ничем, кроме SSL-сертификата с« foo.example.com »в качестве одного из его имен». - Это не правильно, сервер примет запрос, и клиент должен проверить, соответствует ли запрошенный DN сертификату DN сервера.
ColinM

4

Единственный способ сделать это - создать самозаверяющий SSL-сертификат и использовать его для получения контроля над входящими https-запросами. Вы можете создать свой самозаверяющий сертификат SSL в несколько простых шагов, описанных в этом посте .

Допустим, вы создаете самозаверяющий сертификат с именем файла server.crt. Затем вы добавили бы следующее в вашу конфигурацию nginx:

server {
    listen  443;

    ssl    on;
    ssl_certificate         /etc/nginx/ssl/server.crt;
    ssl_certificate_key     /etc/nginx/ssl/server.key;

    server_name server1.com;

    keepalive_timeout 60;
    rewrite ^       http://$server_name$request_uri? permanent;
}

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


1
Я согласен с этим. Есть проблема с браузером, показывающим предупреждающее сообщение о недоверенных сертификатах, но если вы пытаетесь только запретить пользователям переходить на https: // <ip-адрес>, чтобы получить serverd одинаково недействительный сертификат для одного из ваших реальных vhosts (недействительный) поскольку имя хоста не будет совпадать), вам лучше предоставить им недействительный самозаверяющий фиктивный сертификат. Такой тип говорит им: «Здесь нечего видеть, даже сертификат другого хоста».
Даниэль Ф

2

Добавьте универсальный блок сервера и верните код состояния 444. Он сообщает nginx закрыть соединение перед отправкой каких-либо данных.

server {
    listen 443 default_server ssl;
    server_name _;
    return 444;
}

1

В эти дни вы можете использовать расширение индикации имени сервера TLS (SNI, RFC 6066). Приемник HTTPS сможет распознать доменное имя до подачи соответствующего сертификата.

Это означает, что вам понадобятся сертификаты для ВСЕХ ваших доменов, и когда SNI используется для распознавания одного из других доменов, вы можете просто использовать перенаправление HTTP 301 на незашифрованную версию HTTP, если только имя сервера не соответствует тому, который нужен шифрование.

Дополнительную информацию о SNI можно найти в документации по nginx http://nginx.org/en/docs/http/configuring_https_servers.html.


0

Сопоставьте запрошенное имя хоста с действительными именами хостов в http {}блоке:

map $ssl_server_name $correct_hostname_example {
  default 0;
  example.com 1;
  www.example.com 1;
}

А затем в server {}блоке уничтожить соединения с неправильным именем хоста:

if ($correct_hostname_example = 0) {
  return 444;
}

При необходимости используйте несколько карт для нескольких блоков серверов. Соединение будет по-прежнему устанавливаться с использованием одного из ваших сертификатов, но если этот последний блок присутствует в каждом блоке сервера, который обслуживает SSL, тогда вы фактически «заблокируете» соединения с неверными именами хостов. Это может быть необходимо только в первом блоке сервера, но добавление его в каждый блок сервера гарантирует, что порядок не имеет значения.

$ssl_server_nameПеременная присутствует в Nginx 1.7 или выше.


0

Вот как я решил проблему:

  1. Создать самозаверяющий сертификат:

openssl req -nodes -x509 -newkey rsa:4096 -keyout self_key.pem -out self_cert.pem -days 3650

  1. Скопируйте это, где NginX может найти это:

cp self*.pem /etc/nginx/ssl/

  1. Установите универсальный маршрут:
server {
    listen 443 default_server ssl;

    ssl on;
    ssl_certificate /etc/nginx/ssl/self_cert.pem;
    ssl_certificate_key /etc/nginx/ssl/self_key.pem;

    return 301 http://$host;
}

Что это будет делать: оно выдаст вам предупреждение (никак не обойти) на любом сервере, который не имеет собственного сертификата, но в предупреждении не будет указано неправильное имя сертификата. Если пользователь нажимает «посетить в любом случае», он будет перенаправлен на не-ssl версию сайта, которую он напечатал.

предостережение :

если ваш сайт с поддержкой SLL только определяет www.example.com(и не определяет example.com), тогда ваш маршрут перехвата в конечном итоге будет обслуживаться https://example.comсамозаверяющим сертификатом и соответствующим предупреждением.


-2

Перенаправить на http:

server {
    listen       443;
    server_name  *.com;
    rewrite        ^ http://$host$request_uri? permanent;
}    

Возврат 404 :

server {
    listen       443;
    server_name  *.com;
    return 404;
}    

1
Это все равно приведет к предупреждению SSL, поскольку необходимо установить туннель SSL, прежде чем произойдет какое-либо перенаправление HTTP. Смотрите принятый ответ Дэвида Шварца.
CJC
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.