Случалось ли так, что вы понимали  —  необходимо добавить HTTPS в приложение, запущенное на локальном хостинге или каком-нибудь еще местном домене вроде local.my-app.com?

Есть некоторые различия в том, как браузеры работают с HTTP и HTTPS. Например, любые попытки загрузить JavaScript на HTTPS-странице по HTTP-ссылке будут заблокированы. Это значит, что если локальная разработка идет на HTTP, то добавленный тэг скрипта прекрасно будет работать на вашей машине, но сломается, как только вы развернете приложение на промежуточном или продакшен-окружении, где всё работает под HTTPS. Не очень вдохновляющая перспектива.

Но не волнуйтесь. Во избежание подобных проблем полезно установить HTTPS в локальной среде разработки, чтобы более точно отображать ситуацию на рабочем сервере. Тем не менее, заставить эту штуку работать не так-то легко. Никто не хочет постоянно видеть предупреждения о сертификатах. Но как тогда обеспечить безопасность локально?

Ответ прост: создать собственный сертификат, либо подписывающий сам себя, либо заверенный локальным корневым сервером, и внести его в хранилище доверенных сертификатов операционной системы. Далее этим сертификатом можно пользоваться на локальном веб-сервере.

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

Важно отметить вот что: с 1 сентября 2020 года SSL-сертификаты не выдаются дольше, чем на тринадцать месяцев (397 дней). Поэтому для сертификатов, создаваемых в этой статье, мы ограничимся двенадцатью месяцами.

Создание сертификата

Источник сертификата (Certificate Authority)

1. Создайте закрытый ключ и самоподписанный сертификат

openssl req -x509 -nodes -new -sha512 \
  -days 365 -newkey rsa:4096 -keyout ca.key \
  -out ca.pem -subj "/C=US/CN=MY-CA"

Опционально: если необходимо, можно заменить MY-CA в CN на что-нибудь другое.

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

openssl x509 -in ca.pem -text -noout

2. Создайте файл сертификата с расширением .crt:

openssl x509 -outform pem -in ca.pem -out ca.crt

Сертификат доменного имени

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

1. Создайте файл расширения x509 v3:

cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names 
[alt_names]
# Локальные хостинги
DNS.1 = localhost
DNS.2 = 127.0.0.1
DNS.3 = ::1 
# Перечислите доменные имена
DNS.4 = local.dev
DNS.5 = my-app.dev
DNS.6 = local.some-app.dev
EOF

Следуя этому шаблону, можно добавить сколько угодно доменных имен.

Примечание: пожалуйста, обновите DNS.4, DNS.5 и DNS.6 или удалите их, если у вас не настроены никакие локальные доменные имена.

2. Создайте закрытый ключ и запрос на подпись сертификата:

openssl req -new -nodes -newkey rsa:4096 \
  -keyout localhost.key -out localhost.csr \
  -subj "/C=US/ST=State/L=City/O=Some-Organization-Name/CN=localhost"

Опционально: страну, штат, город и организацию можно изменять.

3. Создайте самоподписанный сертификат:

openssl x509 -req -sha512 -days 365 \
-extfile v3.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in localhost.csr \
-out localhost.crt

Использование сертификата

Приложениям, обслуживающим ваш контент, понадобится доступ к файлам сертификата и закрытого ключа. Это может быть локальный веб-сервер (Apache или NGINX), локальный сервис или какой-то другой локальный инструмент, допустим, сборщик модулей DevServer.

Вот несколько примеров:

Apache

<VirtualHost 192.168.0.1:443>     
  ...
  ServerName local.dev
  SSLEngine on
  SSLCertificateFile /path/to/localhost.crt
  SSLCertificateKeyFile /path/to/localhost.key
  ...
</VirtualHost>

NGINX

server {
  listen 443;
  ssl on;
  ssl_certificate /path/to/localhost.crt;
  ssl_certificate_key /path/to/localhost.key;
  ...
}

webpack DevServer

webpack-dev-server  --open --https \
  --cert /path/to/localhost.crt \
  --key /path/to/localhost.key

Express.js

const https = require("https");
const fs = require("fs");
const express = require("express");

// прочитайте ключи
const key = fs.readFileSync("localhost.key");
const cert = fs.readFileSync("localhost.crt");

// создайте экспресс-приложение
const app = express();

// создайте HTTPS-сервер
const server = https.createServer({ key, cert }, app);

// добавьте тестовый роут
app.get("/", (req, res) => {
  res.send("this is an secure server");
});

// запустите сервер на порту 8000
server.listen(8000, () => {
  console.log("listening on 8000");
});

Как только вы настроите обслуживающий ваше приложение инструмент на работу с вашим сертификатом, ваше приложение будет доступно по URL’у с HTTPS.

Следуя приведенному выше примеру с Express, вы можете открыть вкладку браузера по адресу https://localhost:8000 и увидеть ваш контент:

Погодите секунду! Где же сообщение «это защищенный сервер»?

Вы рассчитывали увидеть кое-что другое, но именно этого и следовало ожидать  —  потому что источник сертификата еще не входит в число доверенных.

Доверие к сертификатам

Чтобы получить обозначение безопасного доступа, ваш новый источник сертификата должен считаться доверенным на вашей машине. Процесс присваивания этого статуса различается в зависимости от операционной системы и удовлетворит большинство браузеров. Если вы используете Firefox, процесс имеет некоторые отличия.

macOS — Chrome и Safari

1. Дважды кликните на корневом сертификате (ca.crt).

2. Выберите нужную связку ключей [keychain] (login, если вы хотите, чтобы сертификат считался доверенным только в вашем аккаунте, или System, если сертификат должен считаться доверенным во всей системе).

3. Добавьте сертификат.

4. Откройте «Keychain Access» (если еще не открыт).

5. Выделите keychain, который выбрали раньше.

6. Вы должны увидеть сертификат MY-CA (это будет имя, которое вы, как CN, дали вашему источнику сертификата).

7. Дважды кликните по сертификату.

8. Разверните «Доверять» и выберите опцию «Доверять всегда» в пункте «При использовании этого сертификата».

9. Закройте окно сертификата и введите свой пользовательский пароль (если требуется).

Windows 10 — Chrome, IE11 и Edge

1. Дважды кликните на сертификате (ca.crt).

2. Кликните на кнопку «Установить сертификат».

3. Выберите, хотите ли вы хранить его на уровне пользователя или на уровне машины.

4. Кликните «Дальше».

5. Выберите «Разместить все сертификаты в следующем хранилище».

6. Кликните «Обзор».

7. Выберите «Доверенные корневые источники сертификатов».

8. Кликните «ОК».

9. Кликните «Дальше».

10. Кликните «Завершить».

11. Если появится подсказка, кликните «Да».

Firefox

Даже после того, как вы установите доверенный источник сертификата в хранилище, Firefox все равно будет выдавать предупреждения. Этого может не случиться в Windows 10, но почти наверняка случится в macOS. Справиться с этим достаточно просто. Firefox демонстрирует вот такой экран:

Чтобы добавить разрешения сертификату, кликните «Дополнительно…». Сразу же после этого кликните на «Принять риск и продолжить», чтобы дать понять, что вы знаете о риске.

Это нужно сделать всего один раз, но для каждого локального домена.

Заключение

Теперь, когда сертификат создан и доверие к нему обеспечено, вы без проблем можете посещать свой локальный домен! Обслуживание приложений стало безопасным, и вы можете спокойно продолжать разработку. Возвращаясь к примеру с Express, результат на экране будет таким:

Сайт полностью загружен, и рядом с URL в Chrome теперь отображается символ безопасного соединения.

Надеюсь, эта статья помогла вам превозмочь трудности с HTTPS. 

Удачного программирования!

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи: Hicaro Adriano, “How to Create Trusted SSL Certificates for Your Local Development”

Предыдущая статьяredis-hawk: детализированное отслеживание и контроль развертывания Redis
Следующая статьяАсинхронность в Java