# Об 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**. 🔒
