Об HTTPS¶
Легко предположить, что HTTPS — это что-то, что просто «включено» или нет.
Но на самом деле всё гораздо сложнее.
Совет
Если вы торопитесь или вам это не важно, переходите к следующим разделам с пошаговыми инструкциями по настройке всего разными способами.
Чтобы изучить основы HTTPS с точки зрения пользователя, загляните на https://howhttps.works/.
Теперь, со стороны разработчика, вот несколько вещей, которые стоит держать в голове, размышляя об HTTPS:
- Для HTTPS серверу нужно иметь «сертификаты», сгенерированные третьей стороной.
- Эти сертификаты на самом деле приобретаются у третьей стороны, а не «генерируются».
- У сертификатов есть срок действия.
- Они истекают.
- После этого их нужно обновлять, то есть получать заново у третьей стороны.
- Шифрование соединения происходит на уровне TCP.
- Это на один уровень ниже HTTP.
- Поэтому сертификаты и шифрование обрабатываются до HTTP.
- TCP не знает о «доменах». Только об IP-адресах.
- Информация о конкретном домене передаётся в данных HTTP.
- HTTPS-сертификаты «сертифицируют» определённый домен, но протокол и шифрование происходят на уровне TCP, до того как становится известен домен, с которым идёт работа.
- По умолчанию это означает, что вы можете иметь лишь один HTTPS-сертификат на один IP-адрес.
- Неважно, насколько мощный у вас сервер или насколько маленькие приложения на нём работают.
- Однако у этого есть решение.
- Есть расширение протокола TLS (того самого, что занимается шифрованием на уровне TCP, до HTTP) под названием SNI.
- Это расширение SNI позволяет одному серверу (с одним IP-адресом) иметь несколько HTTPS-сертификатов и обслуживать несколько HTTPS-доменов/приложений.
- Чтобы это работало, один компонент (программа), запущенный на сервере и слушающий публичный IP-адрес, должен иметь все HTTPS-сертификаты на этом сервере.
- После установления защищённого соединения, протокол обмена данными — всё ещё HTTP.
- Содержимое зашифровано, несмотря на то, что оно отправляется по протоколу HTTP.
Обычно на сервере (машине, хосте и т.п.) запускают одну программу/HTTP‑сервер, которая управляет всей частью, связанной с HTTPS: принимает зашифрованные HTTPS-запросы, отправляет расшифрованные HTTP-запросы в само HTTP‑приложение, работающее на том же сервере (в нашем случае это приложение FastAPI), получает HTTP-ответ от приложения, шифрует его с использованием подходящего HTTPS‑сертификата и отправляет клиенту по HTTPS. Такой сервер часто называют прокси‑сервером TLS-терминации.
Некоторые варианты, которые вы можете использовать как прокси‑сервер TLS-терминации:
- Traefik (умеет обновлять сертификаты)
- Caddy (умеет обновлять сертификаты)
- Nginx
- HAProxy
Let's Encrypt¶
До появления Let's Encrypt эти HTTPS-сертификаты продавались доверенными третьими сторонами.
Процесс получения таких сертификатов был неудобным, требовал бумажной волокиты, а сами сертификаты были довольно дорогими.
Затем появился Let's Encrypt.
Это проект Linux Foundation. Он предоставляет HTTPS‑сертификаты бесплатно, в автоматическом режиме. Эти сертификаты используют стандартные криптографические механизмы и имеют короткий срок действия (около 3 месяцев), поэтому безопасность фактически выше благодаря уменьшенному сроку жизни.
Домены безопасно проверяются, а сертификаты выдаются автоматически. Это также позволяет автоматизировать процесс их продления.
Идея — автоматизировать получение и продление сертификатов, чтобы у вас был безопасный HTTPS, бесплатно и навсегда.
HTTPS для разработчиков¶
Ниже приведён пример того, как может выглядеть HTTPS‑API, шаг за шагом, с акцентом на идеях, важных для разработчиков.
Имя домена¶
Чаще всего всё начинается с приобретения имени домена. Затем вы настраиваете его на DNS‑сервере (возможно, у того же облачного провайдера).
Скорее всего, вы получите облачный сервер (виртуальную машину) или что-то подобное, и у него будет постоянный публичный IP-адрес.
На DNS‑сервере(ах) вы настроите запись («A record
» - запись типа A), указывающую, что ваш домен должен указывать на публичный IP‑адрес вашего сервера.
Обычно это делается один раз — при первоначальной настройке всего.
Совет
Часть про доменное имя относится к этапам задолго до HTTPS, но так как всё зависит от домена и IP‑адреса, здесь стоит это упомянуть.
DNS¶
Теперь сфокусируемся на собственно частях, связанных с HTTPS.
Сначала браузер спросит у DNS‑серверов, какой IP соответствует домену, в нашем примере someapp.example.com
.
DNS‑серверы ответят браузеру, какой конкретный IP‑адрес использовать. Это будет публичный IP‑адрес вашего сервера, который вы указали в настройках DNS.
Начало TLS-рукопожатия¶
Далее браузер будет общаться с этим IP‑адресом на порту 443 (порт HTTPS).
Первая часть взаимодействия — установить соединение между клиентом и сервером и договориться о криптографических ключах и т.п.
Это взаимодействие клиента и сервера для установления TLS‑соединения называется TLS‑рукопожатием.
TLS с расширением SNI¶
На сервере только один процесс может слушать конкретный порт на конкретном IP‑адресе. Могут быть другие процессы, слушающие другие порты на том же IP‑адресе, но не более одного процесса на каждую комбинацию IP‑адреса и порта.
По умолчанию TLS (HTTPS) использует порт 443
. Значит, он нам и нужен.
Так как только один процесс может слушать этот порт, делать это будет прокси‑сервер TLS-терминации.
У прокси‑сервера TLS-терминации будет доступ к одному или нескольким TLS‑сертификатам (HTTPS‑сертификатам).
Используя расширение SNI, упомянутое выше, прокси‑сервер TLS-терминации определит, какой из доступных TLS (HTTPS)‑сертификатов нужно использовать для этого соединения, выбрав тот, который соответствует домену, ожидаемому клиентом.
В нашем случае это будет сертификат для someapp.example.com
.
Клиент уже доверяет организации, выдавшей этот TLS‑сертификат (в нашем случае — Let's Encrypt, но об этом позже), поэтому может проверить, что сертификат действителен.
Затем, используя сертификат, клиент и прокси‑сервер TLS-терминации договариваются о способе шифрования остальной TCP‑коммуникации. На этом TLS‑рукопожатие завершено.
После этого у клиента и сервера есть зашифрованное TCP‑соединение — это и предоставляет TLS. И они могут использовать это соединение, чтобы начать собственно HTTP‑обмен.
Собственно, HTTPS — это обычный HTTP внутри защищённого TLS‑соединения, вместо чистого (незашифрованного) TCP‑соединения.
Совет
Обратите внимание, что шифрование обмена происходит на уровне TCP, а не на уровне HTTP.
HTTPS‑запрос¶
Теперь, когда у клиента и сервера (конкретно у браузера и прокси‑сервера TLS-терминации) есть зашифрованное TCP‑соединение, они могут начать HTTP‑обмен.
Клиент отправляет HTTPS‑запрос. Это обычный HTTP‑запрос через зашифрованное TLS‑соединение.
Расшифровка запроса¶
Прокси‑сервер TLS-терминации использует согласованное шифрование, чтобы расшифровать запрос, и передаёт обычный (расшифрованный) HTTP‑запрос процессу, запускающему приложение (например, процессу с Uvicorn, который запускает приложение FastAPI).
HTTP‑ответ¶
Приложение обработает запрос и отправит обычный (незашифрованный) HTTP‑ответ прокси‑серверу TLS-терминации.
HTTPS‑ответ¶
Затем прокси‑сервер TLS-терминации зашифрует ответ с использованием ранее согласованного способа шифрования (который начали использовать для сертификата для someapp.example.com
) и отправит его обратно в браузер.
Далее браузер проверит, что ответ корректен и зашифрован правильным криптографическим ключом и т.п., затем расшифрует ответ и обработает его.
Клиент (браузер) узнает, что ответ пришёл от правильного сервера, потому что используется способ шифрования, о котором они договорились ранее с помощью HTTPS‑сертификата.
Несколько приложений¶
На одном и том же сервере (или серверах) могут работать несколько приложений, например другие программы с API или база данных.
Только один процесс может обрабатывать конкретную комбинацию IP и порта (в нашем примере — прокси‑сервер TLS-терминации), но остальные приложения/процессы тоже могут работать на сервере(ах), пока они не пытаются использовать ту же комбинацию публичного IP и порта.
Таким образом, прокси‑сервер TLS-терминации может обрабатывать HTTPS и сертификаты для нескольких доменов (для нескольких приложений), а затем передавать запросы нужному приложению в каждом случае.
Продление сертификата¶
Со временем каждый сертификат истечёт (примерно через 3 месяца после получения).
Затем будет другая программа (иногда это отдельная программа, иногда — тот же прокси‑сервер TLS-терминации), которая свяжется с Let's Encrypt и продлит сертификат(ы).
TLS‑сертификаты связаны с именем домена, а не с IP‑адресом.
Поэтому, чтобы продлить сертификаты, программа продления должна доказать удостоверяющему центру (Let's Encrypt), что она действительно «владеет» и контролирует этот домен.
Для этого, учитывая разные потребности приложений, есть несколько способов. Популярные из них:
- Изменить некоторые DNS‑записи.
- Для этого программа продления должна поддерживать API DNS‑провайдера, поэтому, в зависимости от используемого провайдера DNS, этот вариант может быть доступен или нет.
- Запуститься как сервер (как минимум на время получения сертификатов) на публичном IP‑адресе, связанном с доменом.
- Как сказано выше, только один процесс может слушать конкретный IP и порт.
- Это одна из причин, почему очень удобно, когда тот же прокси‑сервер TLS-терминации также занимается процессом продления сертификатов.
- В противном случае вам, возможно, придётся временно остановить прокси‑сервер TLS-терминации, запустить программу продления для получения сертификатов, затем настроить их в прокси‑сервере TLS-терминации и перезапустить его. Это не идеально, так как ваше приложение(я) будут недоступны, пока прокси‑сервер TLS-терминации остановлен.
Весь этот процесс продления, совмещённый с обслуживанием приложения, — одна из главных причин иметь отдельную систему для работы с HTTPS в виде прокси‑сервера TLS-терминации, вместо использования TLS‑сертификатов напрямую в сервере приложения (например, Uvicorn).
Пересылаемые HTTP-заголовки прокси¶
Когда вы используете прокси для обработки HTTPS, ваш сервер приложения (например, Uvicorn через FastAPI CLI) ничего не знает о процессе HTTPS, он общается обычным HTTP с прокси‑сервером TLS-терминации.
Обычно этот прокси на лету добавляет некоторые HTTP‑заголовки перед тем, как переслать запрос на сервер приложения, чтобы тот знал, что запрос был проксирован.
Тем не менее, так как сервер приложения не знает, что он находится за доверенным прокси, по умолчанию он не будет доверять этим заголовкам.
Но вы можете настроить сервер приложения, чтобы он доверял пересылаемым заголовкам, отправленным прокси. Если вы используете FastAPI CLI, вы можете использовать опцию CLI --forwarded-allow-ips
, чтобы указать, с каких IP‑адресов следует доверять этим пересылаемым заголовкам.
Например, если сервер приложения получает запросы только от доверенного прокси, вы можете установить --forwarded-allow-ips="*"
, чтобы доверять всем входящим IP, так как он всё равно будет получать запросы только с IP‑адреса, используемого прокси.
Таким образом, приложение сможет знать свой публичный URL, использует ли оно HTTPS, какой домен и т.п.
Это будет полезно, например, для корректной обработки редиректов.
Совет
Подробнее об этом вы можете узнать в документации: За прокси — Включить пересылаемые заголовки прокси
Резюме¶
Наличие HTTPS очень важно и во многих случаях довольно критично. Большая часть усилий, которые вам, как разработчику, нужно приложить вокруг HTTPS, — это просто понимание этих концепций и того, как они работают.
Зная базовую информацию о HTTPS для разработчиков, вы сможете легко комбинировать и настраивать разные инструменты, чтобы управлять всем этим простым способом.
В некоторых из следующих глав я покажу вам несколько конкретных примеров настройки HTTPS для приложений FastAPI. 🔒