Ускорение и защита сайта

Сшиваем SSL-сертификаты правильно на bash

Использование SSL-шифрование на сайтах приобретает уже почти обязательный характер: Google с этого года начал агрессивно предупреждать о небезопасном соединении с сайтами, ряд платежных шлюзов требуют безопасное подключение на сайтах (например, Яндекс.Касса). Установка SSL-сертификата на сайт требует достаточно сложной технической настройки веб-сервера (nginx, например). Одним из аспектов этой настройки является использование «сшивания» (stapling) SSL-сертификатов вплоть до корневого для ускорения установления безопасного соединения из браузеров к сайтам.

SSL stapling позволяет сэкономить для нового посетителя сайта 0,1-1 секунду (за счет экономии 1-2 запросов за промежуточными сертификатами с учетом DNS-запросов, установления соединения и получения данных, каждый из запросов может выполняться до 500 мс в случае 95 перцентиля пользователей). По умолчанию, SSL stapling выполняется для всех сертификатов, загруженных в Айри.

Обычно цепочка SSL-сертификатов, которую должен запросить браузер, выглядит следующим образом:

SSL-сертификат сайта — Промежуточный SSL-сертификат [- Промежуточный SSL-сертификат 2] — Корневой SSL-сертификат

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

Openssl

Основным рабочим инструментом будет библиотека openssl: она позволяет реализовать преобразование сертификатов и извлечение необходимой информации в текстовом формате (в сертификате информация закодирована либо в бинарном формате (DER), либо в base64 (PEM)).

Если на сервере вы подключаете SSL-шифрование для сайта, то сама библиотека, скорее всего, у вас уже установлена. Проверить, например, для какого домена (Canonical name) выпущен сертификат (если он в PEM формате, обычно в таком формате сертификаты выпускаются и передаются для установки на сервер) можно так:

openssl x509 -in ФАЙЛ_СЕРТИФИКАТА -subject -noout

Здесь x509 — входящий формат сертификата, in — ключ для входного файла, subject — указание вывести CN сертификата, а noout — запрет на вывод самого сертификата (в PEM формате).

Получаем «родительский» сертификат

Алгоритм «сшивки» сертификатов достаточно простой: нам нужно из сертификата извлечь путь к «родительскому» сертификату (тому сертификату, которым подписанный данный) и получить этот «родительский» сертификат по извлеченному пути. Простого набора ключей для openssl найти не удалось, поэтому сделаем это с помощью вывода всей информации о сертификате в тестовом формате с помощью ключа text:

openssl x509 -in "ФАЙЛ_СЕРТИФИКАТА" -text -noout

После этого нам нужно лишь выделить поле Issuers и получить из него URL. Это можно сделать, например, так:

openssl x509 -in "ФАЙЛ_СЕРТИФИКАТА" -text -noout | grep Issuers | awk '{sub(/.*http/,"http");print $0}'

Получив URL сертификата, скачиваем его любым удобным образом. Например, через curl:

curl --user-agent "Mozilla/5.0 (compatible; Airee-Speedup/1.0; +http://airee.ru/robots)" $issuer -o /tmp/stapling.crt 2>/dev/null

Запускаем рекурсию

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

Для проверки формата сертификата воспользуемся verify от openssl:

openssl verify /tmp/stapling.crt 2>&1 | grep "unable to load")

В случае возникновения ошибки чтения загруженного сертификата нам нужно будет его преобразовать из бинарного формата в base64:

openssl x509 -inform der -in /tmp/stapling.crt -out /tmp/stapling.crt

или из pkcs7

openssl pkcs7 -inform der -print_certs -in /tmp/stapling.crt -out /tmp/stapling.crt

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

Правильный порядок сертификатов в цепочке

В финале должен получиться файл с цепочкой сертификатов следующего содержания:

-----BEGIN CERTIFICATE-----
сертификат сайта в формате base64
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
промежуточный сертификат в формате base64
-----END CERTIFICATE-----
[-----BEGIN CERTIFICATE-----
возможно, еще один промежуточный сертификат
-----END CERTIFICATE-----]
-----BEGIN CERTIFICATE-----
корневой сертификат в формате base64
-----END CERTIFICATE-----

Браузерам сам корневой сертификат в цепочке не требуется: он у них уже есть. Но он требуется для верификации цепочки самому nginx с включенной настройкой ssl_stapling.

Итог

Для составления цепочки сертификатов из данного требуются следующие действия:

1. Скопировать данный сертификат в начало цепочки (cat).
2. Получить URL «родительского» сертификата (openssl + awk / sed).
3. Загрузить «родительский» сертификат (curl / wget).
4. Переформатировать «родительский» сертификат (openssl).
5. Скопировать «родительский» сертификат в цепочку (cat).
6. Повторить шаги 2-5 или завершить.