Browse Source
Co-authored-by: Artem Golicyn <[email protected]> Co-authored-by: Sebastián Ramírez <[email protected]>pull/9501/head
committed by
GitHub
2 changed files with 199 additions and 0 deletions
@ -0,0 +1,198 @@ |
|||
# Об 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**. 🔒 |
Loading…
Reference in new issue