# Об HTTPS

Обычно представляется, что HTTPS это некая опция, которая либо "включена", либо нет.

Но всё несколько сложнее.

/// tip | "Заметка"

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

///

Чтобы **изучить основы HTTPS** для клиента, перейдите по ссылке <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.

Здесь же представлены некоторые концепции, которые **разработчик** должен иметь в виду при размышлениях об HTTPS:

* Протокол HTTPS предполагает, что **серверу** нужно **располагать "сертификатами"** сгенерированными **третьей стороной**.
    * На самом деле эти сертификаты **приобретены** у третьей стороны, а не "сгенерированы".
* У сертификатов есть **срок годности**.
    * Срок годности **истекает**.
    * По истечении срока годности их нужно **обновить**, то есть **снова получить** у третьей стороны.
* Шифрование соединения происходит **на уровне протокола TCP**.
    * Протокол TCP находится на один уровень **ниже протокола HTTP**.
    * Поэтому **проверка сертификатов и шифрование** происходит **до HTTP**.
* **TCP не знает о "доменах"**, но знает об IP-адресах.
    * Информация о **запрашиваемом домене** извлекается из запроса **на уровне HTTP**.
* **Сертификаты HTTPS** "сертифицируют" **конкретный домен**, но проверка сертификатов и шифрование данных происходит на уровне протокола TCP, то есть **до того**, как станет известен домен-получатель данных.
* **По умолчанию** это означает, что у вас может быть **только один сертификат HTTPS на один IP-адрес**.
    * Не важно, насколько большой у вас сервер и насколько маленькие приложения на нём могут быть.
    * Однако, у этой проблемы есть **решение**.
* Существует **расширение** протокола **TLS** (который работает на уровне TCP, то есть до HTTP) называемое **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Указание имени сервера">SNI</abbr></a>**.
    * Расширение SNI позволяет одному серверу (с **одним IP-адресом**) иметь **несколько сертификатов HTTPS** и обслуживать **множество HTTPS-доменов/приложений**.
    * Чтобы эта конструкция работала, **один** её компонент (программа) запущенный на сервере и слушающий **публичный IP-адрес**, должен иметь **все сертификаты HTTPS** для этого сервера.
* **После** установления защищённого соединения, протоколом передачи данных **остаётся HTTP**.
    * Но данные теперь **зашифрованы**, несмотря на то, что они передаются по **протоколу HTTP**.

Обычной практикой является иметь **одну программу/HTTP-сервер** запущенную на сервере (машине, хосте и т.д.) и **ответственную за всю работу с HTTPS**:

* получение **зашифрованных HTTPS-запросов**
* отправка **расшифрованных HTTP запросов** в соответствующее HTTP-приложение, работающее на том же сервере (в нашем случае, это приложение **FastAPI**)
* получение **HTTP-ответа** от приложения
* **шифрование ответа** используя подходящий **сертификат HTTPS**
* отправка зашифрованного **HTTPS-ответа клиенту**.
Такой сервер часто называют **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">Прокси-сервер завершения работы TLS</a>** или просто "прокси-сервер".

Вот некоторые варианты, которые вы можете использовать в качестве такого прокси-сервера:

* Traefik (может обновлять сертификаты)
* Caddy (может обновлять сертификаты)
* Nginx
* HAProxy

## Let's Encrypt (центр сертификации)

До появления Let's Encrypt **сертификаты HTTPS** приходилось покупать у третьих сторон.

Процесс получения такого сертификата был трудоёмким, требовал предоставления подтверждающих документов и сертификаты стоили дорого.

Но затем консорциумом Linux Foundation был создан проект **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>**.

Он автоматически предоставляет **бесплатные сертификаты HTTPS**. Эти сертификаты используют все стандартные криптографические способы шифрования. Они имеют небольшой срок годности (около 3 месяцев), благодаря чему они даже **более безопасны**.

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

Суть идеи в автоматическом получении и обновлении этих сертификатов, чтобы все могли пользоваться **безопасным HTTPS. Бесплатно. В любое время.**

## HTTPS для разработчиков

Ниже, шаг за шагом, с заострением внимания на идеях, важных для разработчика, описано, как может выглядеть HTTPS API.

### Имя домена

Чаще всего, всё начинается с **приобретения имени домена**. Затем нужно настроить DNS-сервер (вероятно у того же провайдера, который выдал вам домен).

Далее, возможно, вы получаете "облачный" сервер (виртуальную машину) или что-то типа этого, у которого есть <abbr title="Не изменяемый">постоянный</abbr> **публичный IP-адрес**.

На DNS-сервере (серверах) вам следует настроить соответствующую ресурсную запись ("`запись A`"), указав, что  **Ваш домен** связан с публичным **IP-адресом вашего сервера**.

Обычно эту запись достаточно указать один раз, при первоначальной настройке всего сервера.

/// tip | "Заметка"

Уровни протоколов, работающих с именами доменов, намного ниже HTTPS, но об этом следует упомянуть здесь, так как всё зависит от доменов и IP-адресов.

///

### DNS

Теперь давайте сфокусируемся на работе с HTTPS.

Всё начинается с того, что браузер спрашивает у **DNS-серверов**, какой **IP-адрес связан с доменом**, для примера возьмём домен `someapp.example.com`.

DNS-сервера присылают браузеру определённый **IP-адрес**, тот самый публичный IP-адрес вашего сервера, который вы указали в ресурсной "записи А" при настройке.

<img src="/img/deployment/https/https01.svg">

### Рукопожатие TLS

В дальнейшем браузер будет взаимодействовать с этим IP-адресом через **port 443** (общепринятый номер порта для HTTPS).

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

<img src="/img/deployment/https/https02.svg">

Эта часть клиент-серверного взаимодействия устанавливает TLS-соединение и называется **TLS-рукопожатием**.

### TLS с расширением SNI

На сервере **только один процесс** может прослушивать определённый **порт** определённого **IP-адреса**. На сервере могут быть и другие процессы, слушающие другие порты этого же IP-адреса, но никакой процесс не может быть привязан к уже занятой комбинации IP-адрес:порт. Эта комбинация называется "сокет".

По умолчанию TLS (HTTPS) использует порт `443`. Потому этот же порт будем использовать и мы.

И раз уж только один процесс может слушать этот порт, то это будет процесс **прокси-сервера завершения работы TLS**.

Прокси-сервер завершения работы TLS будет иметь доступ к одному или нескольким **TLS-сертификатам** (сертификаты HTTPS).

Используя **расширение SNI** упомянутое выше, прокси-сервер из имеющихся сертификатов TLS (HTTPS) выберет тот, который соответствует имени домена, указанному в запросе от клиента.

То есть будет выбран сертификат для домена `someapp.example.com`.

<img src="/img/deployment/https/https03.svg">

Клиент уже **доверяет** тому, кто выдал этот TLS-сертификат (в нашем случае - Let's Encrypt, но мы ещё обсудим это), потому может **проверить**, действителен ли полученный от сервера сертификат.

Затем, используя этот сертификат, клиент и прокси-сервер **выбирают способ шифрования** данных для устанавливаемого **TCP-соединения**. На этом операция **TLS-рукопожатия** завершена.

В дальнейшем клиент и сервер будут взаимодействовать по **зашифрованному TCP-соединению**, как предлагается в протоколе TLS. И на основе этого TCP-соедениния будет создано **HTTP-соединение**.

Таким образом, **HTTPS** это тот же **HTTP**, но внутри **безопасного TLS-соединения** вместо чистого (незашифрованного) TCP-соединения.

/// tip | "Заметка"

Обратите внимание, что шифрование происходит на **уровне TCP**, а не на более высоком уровне HTTP.

///

### HTTPS-запрос

Теперь, когда между клиентом и сервером (в нашем случае, браузером и прокси-сервером) создано **зашифрованное TCP-соединение**, они могут начать **обмен данными по протоколу HTTP**.

Так клиент отправляет **HTTPS-запрос**. То есть обычный HTTP-запрос, но через зашифрованное TLS-содинение.

<img src="/img/deployment/https/https04.svg">

### Расшифровка запроса

Прокси-сервер, используя согласованный с клиентом ключ, расшифрует полученный **зашифрованный запрос** и передаст **обычный (незашифрованный) HTTP-запрос** процессу, запускающему приложение (например, процессу Uvicorn запускающему приложение FastAPI).

<img src="/img/deployment/https/https05.svg">

### HTTP-ответ

Приложение обработает запрос и вернёт **обычный (незашифрованный) HTTP-ответ** прокси-серверу.

<img src="/img/deployment/https/https06.svg">

### HTTPS-ответ

Пркоси-сервер **зашифрует ответ** используя ранее согласованный с клиентом способ шифрования (которые содержатся в сертификате для домена `someapp.example.com`) и отправит его браузеру.

Наконец, браузер проверит ответ, в том числе, что тот зашифрован с нужным  ключом, **расшифрует его** и обработает.

<img src="/img/deployment/https/https07.svg">

Клиент (браузер) знает, что ответ пришёл от правильного сервера, так как использует методы шифрования, согласованные ими раннее через **HTTPS-сертификат**.

### Множество приложений

На одном и том же сервере (или серверах) можно разместить **множество приложений**, например, другие программы с API или базы данных.

Напомню, что только один процесс (например, прокси-сервер) может прослушивать определённый порт определённого IP-адреса.
Но другие процессы и приложения тоже могут работать на этом же сервере (серверах), если они не пытаются использовать уже занятую **комбинацию IP-адреса и порта** (сокет).

<img src="/img/deployment/https/https08.svg">

Таким образом, сервер завершения TLS может обрабатывать HTTPS-запросы и использовать сертификаты для **множества доменов** или приложений и передавать запросы правильным адресатам (другим приложениям).

### Обновление сертификата

В недалёком будущем любой сертификат станет **просроченным** (примерно через три месяца после получения).

Когда это произойдёт, можно запустить другую программу, которая подключится к Let's Encrypt и обновит сертификат(ы). Существуют прокси-серверы, которые могут сделать это действие самостоятельно.

<img src="/img/deployment/https/https.svg">

**TLS-сертификаты** не привязаны к IP-адресу, но **связаны с именем домена**.

Так что при обновлении сертификатов программа должна **подтвердить** центру сертификации (Let's Encrypt), что обновление запросил **"владелец", который контролирует этот домен**.

Есть несколько путей осуществления этого. Самые популярные из них:

* **Изменение записей DNS**.
    * Для такого варианта Ваша программа обновления должна уметь работать с API DNS-провайдера, обслуживающего Ваши DNS-записи. Не у всех провайдеров есть такой API, так что этот способ не всегда применим.
* **Запуск в качестве программы-сервера** (как минимум, на время обновления сертификатов) на публичном IP-адресе домена.
    * Как уже не раз упоминалось, только один процесс может прослушивать определённый порт определённого IP-адреса.
    * Это одна из причин использования прокси-сервера ещё и в качестве программы обновления сертификатов.
    * В случае, если обновлением сертификатов занимается другая программа, вам понадобится остановить прокси-сервер, запустить программу обновления сертификатов на сокете, предназначенном для прокси-сервера, настроить прокси-сервер на работу с новыми сертификатами и перезапустить его. Эта схема далека от идеальной, так как Ваши приложения будут недоступны на время отключения прокси-сервера.

Весь этот процесс обновления, одновременный с обслуживанием запросов, является одной из основных причин, по которой желательно иметь **отдельную систему для работы с HTTPS** в виде прокси-сервера завершения TLS, а не просто использовать сертификаты TLS непосредственно с сервером приложений (например, Uvicorn).

## Резюме

Наличие **HTTPS** очень важно и довольно **критично** в большинстве случаев. Однако, Вам, как разработчику, не нужно тратить много сил на это, достаточно **понимать эти концепции** и принципы их работы.

Но узнав базовые основы **HTTPS** вы можете легко совмещать разные инструменты, которые помогут вам в дальнейшей разработке.

В следующих главах я покажу вам несколько примеров, как настраивать **HTTPS** для приложений **FastAPI**. 🔒
