Усиливаем безопасность HTTPS в nginx

В данной статье мы рассмотрим как значительно усилить безопасность HTTPS на примере веб-сервера nginx, настроенного в качестве фронтенда.

Введение

Проверить работу SSL/TLS на сервере можно при помощи специализированного ресурса. Определяются слабые, небезопасные и анонимные шифры, а также уязвимости к атакам типа Heartbleed, Poodle и т.д.

С настройками nginx по умолчанию тестер показывает F (ужасно). После нашей настройки будет A+ (отлично).

Отключаем устаревшие протоколы

Из соображений безопасности следует полностью отключить устаревшие протоколы: SSLv2 и особенно SSLv3, который давно подвержен атаке Poodle.

Разрешим в конфиге nginx только TLS версий 1.0, 1.1 и 1.2:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

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

Включаем механизм TLS-FALLBACK-SCSV

Данный механизм предотвращает ослабление шифрования за счёт перехода на уязвимый SSL. Механизм появился во всех новых версиях библиотеки openssl, поэтому требуется обновить:

  • OpenSSL 1.0.1 до версии 1.0.1j и выше;
  • OpenSSL 1.0.0 до версии 1.0.0o и выше;
  • OpenSSL 0.9.8 до версии 0.9.8zc и выше.

Отключаем небезопасные и анонимные шифры

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

Если необходимо обеспечить совместимость только с современными браузерами (Firefox 24+, Chrome/Chromium 32+), пропишем в конфиге nginx:

ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK;

Если необходима и поддержка клиентов с более старыми браузерами (IE7+, Firefox 3.x, Opera 12.x), пропишем такую строку вместо предыдущей:

ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;

Теперь разрешим использование только указанных нами шифров:

ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;

Порядок в строке ssl_ciphers имеет значение: чем ближе он к началу, тем выше его приоритет. Если на клиенте не окажется шифров, разрешённых на сервере, последний закроет с ним соединение.

Включаем механизм Forward Secrecy

Принцип работы этого механизма достаточно прост: клиент и сервер в момент TLS рукопожатия обмениваются особыми одноразовыми ключами, которые уничтожаются по окончании TLS сессии, либо по таймауту. Для обмена ключами используется механизм Диффи-Хеллмана. Ключ, полученный таким способом, используется для шифрования передаваемых в обе стороны данных.

Даже если злоумышленник сможет украсть секретный ключ сервера, он не сможет расшифровать предыдущий TLS трафик, т.к. секретный ключ сервера используется только для подписи рукопожатия DH.

Индивидуальные сессионные ключи клиента и сервера не покидают их и не могут быть получены посредством MITM-атак ни при каком условии.

Веб-сервер nginx начиная с версии 1.4.4 уже использует механизм Диффи-Хеллмана для обмена ключами, однако длина ключа всего 1024 бит, что очень мало по современным меркам. Нам необходимо сгенерировать свой собственный с длиной ключа 4096 бит:

cd /etc/nginx/keys/
openssl dhparam -out dhparam.pem 4096

Процесс займёт достаточно много времени из-за сбора дополнительной энтропии и на выходе мы получим файл dhparam.pem. Теперь нам нужно включить его в конфиге nginx:

ssl_dhparam /etc/nginx/keys/dhparam.pem;

Включаем HTTP Strict Transport Security

Расширение HTTP Strict Transport Security предназначено для сохранения настройки в браузере «этот сайт работает только посредством HTTPS».

Атаковать такой ресурс при помощи «человек посередине» (MITM) уже не получится, т.к. в случае подмены сертификата браузер сразу закроет соединение и не даст возможности продолжить работу с поддельным сертификатом.

Включим поддержку HSTS:

add_header Strict-Transport-Security max-age=31536000;

В параметре max-age указывается на сколько секунд следует кэшировать заголовок HSTS в браузере. Рекомендуется указывать 31536000 (1 год) и выше.

Включаем HTTP Public Key Pinning Extension

Firefox начиная с версии 35.0 начал поддерживать расширение HTTP Public Key Pinning, поэтому включим и его, т.к. безопасности много не бывает.

При помощи данного расширения администратор ресурса может указать какой удостоверяющий центр может использоваться для подписания его сертификатов. В совокупности с уже описанным HSTS, это на 100% исключает возможность MITM-атаки.

Чтобы включить данное расширение, нам необходимо получить отпечаток удостоверяющего центра, выдавшего наш сертификат и закодировать его в base64. Проще всего это сделать так:

openssl x509 -in cert.pem -pubkey -noout | \
openssl rsa -pubin -outform der | \
openssl dgst -sha256 -binary | openssl enc -base64

Здесь cert.pem — это первый в цепочке сертификат, принадлежащий удостоверяющему центру. На выходе мы получим строку в base64.

Включаем расширение и прописываем отпечаток нашего УЦ (не забудьте указать свой base64 отпечаток):

add_header Public-Key-Pins 'pin-sha256="5C8kvU039KouVrl52D0eZSGf4Onjo4Khs8tmyTlV3nU="; max-age=1512000';

В параметре max-age указывается на сколько секунд следует кэшировать отпечаток в кэше браузера. Рекомендуется указывать не слишком большое значение, т.к. в случае смены УЦ клиенты не смогут зайти на ваш ресурс до истечения таймаута.

Дополнительная литература

При написании данной статьи использовалась литература из следующих источников:

15 commentaries to post

  1. Добрый день!
    Подскажите, пожалуйста, каким образом public key pins можно реализовать в Apache? Не могу понять, как и где получить pin2 и что нужно добавить в конфигурационный файл? Информации в инете много, но гуманитарным умом ее не понять …
    Заранее благодарен за ответ!
    С уважением,

    1. Для начала нужно загрузить модуль управления заголовками Apache (если ещё не загружен):

      LoadModule headers_module modules/mod_headers.so

      Теперь просто отправляете заголовок Public-Key-Pins с вычисленными по описанному в статье методу хешами:

      Header set Public-Key-Pins "pin-sha256=\"5C8kvU039KouVrl52D0eZSGf4Onjo4Khs8tmyTlV3nU=\"; pin-sha256=\"633lt352PKRXbOwf4xSEa1M517scpD3l5f79xMD9r9Q=\"; max-age=1512000"
  2. Благодарю за ответ!
    Модуль загружен, но не могу понять, как получить эти отпечатки ключей: по аналогии с другими примерами первый пин — отпечаток собственного сертификата сайта, а вот как получить второй — не могу понять … Или можно добавить свой к Вашим в этом примере?
    С уважением,
    Владимир

    1. Необходимо применить описанную выше команду на цепочку сертификатов, а не конкретный сертификат. Цепочка сертификатов — это сертификат УЦ, подписавший ваш сертификат. Его можно получить на сайте УЦ, выдавшего сертификат.

      Например для Let’s Encrypt цепочкой будет этот сертификат, а хешем — YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=.

      Указанные в примере хеши использовать не следует вообще. Вы должны заменить их на собственные.

  3. Извиняюсь за бестолковость: понять бы, как эту команду использовать и куда ее вводить, чтобы получить отпечаток? Сам «дойти» не могу 🙂

    1. Вводить в терминале. Вместо cert.pem указать имя файла с цепочкой сертификатов.

  4. Ещё раз благодарю за ответ! У меня сертификат COMODO, но пока не нашёл где можно получить отпечаток их сертификата 🙂 Или я опять не там ищу? :)))

  5. Данные своих сертификатов определяет любой сервис их проверки, щас попробую «проверить» сайт COMODO

  6. Премного Вам благодарен! Вы простым русским языком сказали, где можно взять данные для этой «фишки»: проверил на сервисе анализа сертификатов SSL сайт COMODO, скопировал пин их сертификата, указал его номером 2 после своего, внёс запись на свой сайт и «вуаля»: зелёный значок получен! Удачи Вам в Вашей профессии! До этого было два дня (и две ночи) тщательного штудирования интернета, форумов … все отвечали замудрено, но не понятно. Вы же спасли меня от этой головоломки! Сайт работает, все получилось! Спасибо!!!

    1. Рекомендую ещё проверить работу HTTPS на вашем сайте вот этим сервисом. Он обнаруживает множество ошибок при настройке и предлагает способы их решения.

  7. Спасибо! Именно там и проверяю (вчера и сегодня прибавил две зеленые полоски и вместо А стало А+), а также htbridge точка com (тоже A+, 2 зеленые полоски + настройки сервера за два дня с F поднялись до B+). И теперь мучаю хостинг, заставляю настраивать сервер 🙂

  8. добавлением HPKPмне удалось понизить рейтинг до A. 🙂

    Invalid, incomplete or partial HPKP information supplied. More information available below.
    Public Key Pinning (HPKP) Incomplete Required backup pin(s) missing
    pin-sha256=»PyAx2luj2DpOvVY8klEdEewKq2khqXRtjB9lu9lyDlg=»; max-age=1512000

    видимо, не всё так просто.

    1. видимо, не всё так просто.

      Просто сервис предупреждает вас о том, что очень небезопасно использовать хеш единственного УЦ, т.к. в случае если он вдруг изменится (например вам придётся сменить сертификат на выданный другим УЦ), пользователи очень длительное время не смогут попасть на ваш сайт.

      Именно поэтому рекомендуется использовать отпечатки нескольких УЦ, в которых вы можете получить сертификаты для своего домена (например Let’s Encrypt и Comodo). Получать второй сертификат при этом совсем не требуется — достаточно лишь указать хеш данного резервного УЦ.

      1. спасибо за подсказку. действительно, сработало. теперь имею три зелёных полоски. 🙂 вот только A++ всё равно не дают. 🙂

        1. вот только A++ всё равно не дают

          Раньше для получения ранга выше A+ требовалось ещё быть в базе HSTS preload list браузеров. Подать заявку на включение можно здесь.

Обсуждение закрыто.