Тема 17

Защищённые соединения: криптография и TLS

Защищённые соединения: криптография и TLS

В предыдущих темах сеть рассматривалась как система доставки: IP находит путь между хостами, TCP превращает ненадёжную доставку дейтаграмм в упорядоченный поток байтов, DNS сопоставляет именам адреса, HTTP передаёт поверх TCP запросы и ответы. Ни один из этих протоколов не задавался вопросом, кто ещё видит передаваемые байты и не изменил ли их кто-нибудь по дороге. Пакет проходит через коммутаторы офиса, точку доступа Wi-Fi, маршрутизаторы нескольких провайдеров и точек обмена трафиком, и каждый из этих узлов технически способен прочитать и переписать его содержимое. Как было показано в теме 15, даже ответ DNS может быть подделан, и тогда клиент установит соединение совсем не с тем сервером, к которому обращался.

Настоящая тема посвящена тому, как поверх такой сети строится защищённый канал: соединение, содержимое которого не может прочитать посторонний, не может незаметно изменить посредник и которое гарантированно ведёт к нужному собеседнику. Стандартное решение для Интернета — протокол защиты транспортного уровня (англ. Transport Layer Security, TLS). Сегодня он защищает подавляющую часть веб-трафика (HTTPS — это HTTP поверх TLS), почту, DNS поверх TLS, и встроен в транспортный протокол QUIC, рассмотренный в теме 16.

TLS собран из криптографических примитивов — шифров, хеш-функций, подписей, обмена ключами, — и понять протокол, не понимая назначения каждого из них, невозможно. Поэтому изложение построено так. Сначала формулируются угрозы и требуемые свойства канала. Затем рассматриваются примитивы на уровне, достаточном для понимания их роли и ограничений; обмен ключами Диффи — Хеллмана разбирается на числовом примере. Третий раздел посвящён инфраструктуре открытых ключей: сертификатам X.509, цепочкам доверия, отзыву, Certificate Transparency и автоматическому выпуску сертификатов. Четвёртый раздел разбирает TLS 1.3 по сообщениям и по байтам, пятый — практику: взаимную аутентификацию, типичные ошибки конфигурации, атаки на понижение версии и HSTS. Математические основания криптографии в теме не излагаются: алгоритмы рассматриваются как «чёрные ящики» с известными свойствами.

Угрозы в сети и свойства защищённого канала

Пассивный перехват, подмена и посредник

Модель угроз сетевой безопасности исходит из пессимистичного допущения: противник контролирует сеть — читает, удаляет, задерживает, переупорядочивает, повторяет и вставляет пакеты (модель Долева — Яо, 1983). Это не значит, что каждый канал прослушивается; это значит, что безопасность протокола не должна зависеть от честности промежуточных узлов.

Пассивный противник (англ. eavesdropper) только наблюдает: подключается к открытой сети Wi-Fi, зеркалирует порт коммутатора, записывает трафик на магистрали, чтобы прочитать пароли, cookie сессии, письма. Пассивный перехват не оставляет следов, а записанный сегодня трафик может быть расшифрован через годы, если станет известен ключ; к этому предстоит вернуться при обсуждении прямой секретности.

Активный противник изменяет трафик: подменяет содержимое (вставляет код в загружаемую страницу), повторяет перехваченные сообщения (дважды отправленное «перевести 1000 рублей»), обрывает соединения. Наиболее сильная форма активной атаки — атака «человек посередине» (англ. man-in-the-middle, MITM): противник встраивается между клиентом и сервером, для клиента выдаёт себя за сервер, для сервера — за клиента и пересылает данные между двумя соединениями, читая и изменяя их по своему усмотрению. Встроиться в путь можно разными способами, знакомыми по предыдущим темам: подменой ARP в локальной сети (тема 18), поддельной точкой доступа, подложным ответом DHCP со «своим» DNS-сервером, подделкой ответа DNS (тема 15), перехватом префикса в BGP (тема 10). Поэтому защиту нельзя строить на предположении, что «трафик и так придёт куда надо».

Конфиденциальность, целостность, аутентификация

Защищённый канал должен обеспечивать три свойства.

  • Конфиденциальность (англ. confidentiality): содержимое канала доступно только его сторонам. Обеспечивается шифрованием.
  • Целостность (англ. integrity): любое изменение, вставка, удаление или переупорядочение данных обнаруживается получателем. Обеспечивается кодами аутентификации сообщений и нумерацией записей.
  • Аутентификация (англ. authentication): сторона уверена, с кем установлено соединение. Обеспечивается цифровыми подписями и сертификатами.

Эти свойства не сводятся друг к другу. Шифрование без проверки целостности не мешает изменять шифротекст: у потокового шифра инверсия бита шифротекста инвертирует тот же бит открытого текста, и противник, знающий формат сообщения, изменит сумму перевода, не зная ключа. Шифрование и целостность без аутентификации бесполезны против посредника: клиент установит защищённый канал — с противником. Аутентификация сервера — самая сложная часть TLS, требующая внешней инфраструктуры сертификатов.

Чего TLS не обеспечивает, следует назвать явно. TLS не скрывает адреса IP и порты, размеры и моменты передачи данных, по которым часто можно определить посещаемый сайт; имя сервера по умолчанию передаётся открыто (расширение SNI). TLS не защищает данные на конечных узлах после расшифровки. Наконец, TLS защищает канал, а не сообщение: данные, полученные по TLS, ничем не доказывают третьему лицу своё происхождение.

Место TLS в стеке протоколов

TLS работает поверх надёжного упорядоченного транспорта — как правило, TCP — и предоставляет приложению такой же поток байтов, но защищённый. Приложение вызывает вместо send и recv функции библиотеки TLS, поэтому TLS относят то к «уровню 4,5», то к прикладному. Защищённым вариантам протоколов выделены отдельные порты: HTTPS — 443, SMTP с отправкой через TLS — 465, IMAPS — 993, DNS поверх TLS — 853. Другой способ — команда STARTTLS внутри уже открытого соединения (SMTP, IMAP), уязвимая к удалению активным посредником.

Для дейтаграммного транспорта существует вариант DTLS, а в QUIC (тема 16) рукопожатие TLS 1.3 встроено в сам транспортный протокол: QUIC использует сообщения рукопожатия TLS, но шифрует пакеты собственным механизмом. Таким образом, изложенное ниже о рукопожатии TLS 1.3 полностью относится и к HTTP/3.

Предшественник TLS — протокол SSL (англ. Secure Sockets Layer) компании Netscape, версии 2.0 (1995) и 3.0 (1996). В IETF протокол стандартизован как TLS 1.0 (1999), 1.1 (2006), 1.2 (2008) 12 и 1.3 (2018) 1; допустимы сегодня только TLS 1.2 и 1.3 6. Название SSL сохранилось в обиходе («SSL-сертификат») и в именах программ (OpenSSL).

Криптографические примитивы

Симметричные шифры и аутентифицированное шифрование

Симметричный шифр использует один и тот же секретный ключ для зашифрования и расшифрования. Современные симметричные шифры быстры (гигабайты в секунду на ядро процессора) и при длине ключа 128 бит и более не поддаются перебору. Основной блочный шифр — AES (англ. Advanced Encryption Standard), шифрующий блоки по 128 бит ключом длиной 128, 192 или 256 бит. Для сообщений произвольной длины блочный шифр применяется в одном из режимов работы. Второй широко используемый шифр — потоковый ChaCha20: он порождает из ключа и одноразового числа псевдослучайную гамму, которая складывается с открытым текстом операцией «исключающее или». ChaCha20 быстро работает и без аппаратной поддержки AES, поэтому популярен на мобильных устройствах.

Шифрование само по себе не защищает целостность. Исторически к шифру добавляли отдельный код аутентификации, и способ их сочетания оказался источником уязвимостей: на схеме «сначала MAC, потом шифрование» в режиме CBC из TLS 1.2 построены атаки на дополнение (англ. padding oracle) — POODLE, Lucky Thirteen. Современный подход — аутентифицированное шифрование с присоединёнными данными (англ. Authenticated Encryption with Associated Data, AEAD), единый алгоритм с интерфейсом 14:

Зашифровать(ключ K, одноразовое число N, открытый текст P, присоединённые данные A) -> шифротекст C || тег T
Расшифровать(K, N, C || T, A) -> P   или   ОШИБКА, если C, T или A изменены

Присоединённые данные A не шифруются, но защищаются тегом: в TLS это заголовок записи. Тег — короткая (обычно 16 байт) контрольная величина, которую невозможно подделать без ключа. Если при расшифровании тег не сходится, алгоритм не возвращает никакого открытого текста — это исключает целый класс атак, в которых противник изучает реакцию получателя на испорченные данные. TLS 1.3 разрешает только AEAD-алгоритмы: AES-GCM (режим счётчика с аутентификатором GHASH), ChaCha20-Poly1305 и AES-CCM 1.

Требование к одноразовому числу (англ. nonce) принципиально: пара «ключ — одноразовое число» не должна повторяться никогда. Для AES-GCM повтор приводит к катастрофе: противник получает сумму двух открытых текстов по модулю 2 и возможность подделывать теги. Поэтому в TLS одноразовое число не передаётся по сети, а вычисляется из номера записи, который у каждой стороны свой и монотонно растёт (подробнее — в разделе о TLS 1.3).

Хеш-функции и HMAC

Криптографическая хеш-функция отображает сообщение произвольной длины в короткий «отпечаток» фиксированной длины: SHA-256 — в 256 бит, SHA-384 — в 384 бита. От неё требуются три свойства: по отпечатку невозможно найти сообщение (стойкость к нахождению прообраза), по сообщению невозможно найти другое с тем же отпечатком (стойкость к нахождению второго прообраза), невозможно найти вообще какую-либо пару сообщений с одинаковым отпечатком (стойкость к коллизиям). Последнее свойство самое трудное: из-за «парадокса дней рождения» коллизию в выходе длиной n бит можно найти примерно за 2 в степени n/2 попыток, поэтому 256-битный хеш даёт 128-битную стойкость к коллизиям. Хеш-функции MD5 и SHA-1 считаются сломанными: для MD5 коллизии вычисляются за секунды, для SHA-1 первая практическая коллизия опубликована в 2017 году. В TLS 1.3 обе функции запрещены для подписи рукопожатия (сообщение CertificateVerify); SHA-1 допускается лишь как унаследованный алгоритм подписи сертификатов 1.

printf 'Severny Uzel' | sha256sum
printf 'Severny uzel' | sha256sum
b1e0ce97ceffb01b34ee9210323990028b62c37eacb8281ee5e145209d38b2c2  -
e60e47670ab6549bf04fa3c1e9bc1e0c3dccd041721030e1808e3ae665337905  -

Изменение одной буквы меняет примерно половину битов отпечатка непредсказуемым образом (лавинный эффект).

Хеш-функция не имеет ключа, и противник, изменивший сообщение, просто пересчитает отпечаток. Для проверки целостности нужен код аутентификации сообщения (англ. Message Authentication Code, MAC) — функция от ключа и сообщения. Стандартная конструкция MAC на основе хеш-функции — HMAC 11:

HMAC(K, m) = H( (K XOR opad) || H( (K XOR ipad) || m ) )

где ipad и opad — константы (байты 0x36 и 0x5C, повторённые до длины блока хеш-функции). Двойное хеширование нужно для защиты от атаки удлинения сообщения, которой подвержена наивная конструкция H(K || m). Помимо проверки целостности HMAC служит строительным блоком функций выработки ключей. В TLS 1.3 все ключи выводятся функцией HKDF (англ. HMAC-based Key Derivation Function) 8, которая состоит из двух шагов: HKDF-Extract(соль, исходный материал) сжимает неравномерно распределённый секрет (например, результат обмена Диффи — Хеллмана) в равномерный псевдослучайный ключ, а HKDF-Expand(ключ, контекст, длина) порождает из него сколько угодно независимых ключей для разных целей, различаемых строкой контекста.

Асимметричная криптография: шифрование, подписи, обмен ключами

Симметричная криптография требует заранее общего секрета, что невыполнимо, когда клиент впервые обращается к незнакомому серверу. Решение предложили в 1976 году Диффи и Хеллман 7: криптография с открытым ключом, в которой у каждой стороны есть пара ключей — закрытый (англ. private key), хранимый в секрете, и открытый (англ. public key), который можно публиковать. Асимметричные операции на порядки медленнее симметричных, поэтому служат только для установления общего секрета и аутентификации.

Асимметричные алгоритмы решают три задачи.

  • Шифрование с открытым ключом: зашифровать может каждый, расшифровать — только владелец закрытого ключа. Классический алгоритм — RSA (Ривест, Шамир, Адлеман, 1977), стойкость которого основана на трудности разложения большого числа на множители. Учебный пример: n = 61 · 53 = 3233, открытая экспонента e = 17, закрытая d = 2753; число 65 шифруется как 65 в степени 17 по модулю 3233 = 2790, а 2790 в степени 2753 по модулю 3233 снова даёт 65. Реальные ключи RSA имеют модуль длиной 2048–4096 бит.
  • Цифровая подпись: подписать может только владелец закрытого ключа, проверить — каждый, у кого есть открытый. Подпись вычисляется от хеша сообщения. Используются RSA (схемы PKCS#1 v1.5 и PSS), ECDSA на эллиптических кривых P-256 и P-384, а также EdDSA (Ed25519).
  • Обмен ключами (англ. key exchange, key agreement): две стороны, обменявшись открытыми значениями по открытому каналу, вычисляют общий секрет, неизвестный наблюдателю. Это протокол Диффи — Хеллмана (DH) и его вариант на эллиптических кривых (ECDH).

Криптография на эллиптических кривых (англ. Elliptic Curve Cryptography, ECC) даёт ту же стойкость при гораздо меньших ключах: 256-битный ключ на кривой соответствует примерно 128-битной стойкости, для которой RSA нужен модуль около 3072 бит. В TLS 1.3 основные группы для обмена ключами — X25519 (кривая Curve25519) 10 и secp256r1 (P-256); открытый ключ X25519 занимает всего 32 байта.

ЗадачаАлгоритмыГде используется в TLS 1.3
Шифрование данных и целостностьAES-128/256-GCM, ChaCha20-Poly1305 (AEAD)защита записей
Выработка ключейHKDF на SHA-256 или SHA-384расписание ключей
Обмен ключамиECDHE (X25519, P-256), DHE (ffdhe2048 и др.)расширение key_share
ПодписьECDSA, RSA-PSS, Ed25519CertificateVerify, подписи сертификатов
ХешированиеSHA-256, SHA-384хеш стенограммы рукопожатия, Finished

Обмен Диффи — Хеллмана: числовой пример

Идея обмена Диффи — Хеллмана 7 основана на функции, которую легко вычислить, но трудно обратить: возведении в степень по модулю простого числа. Стороны заранее договариваются об открытых параметрах — простом числе p и генераторе g (числе, степени которого по модулю p пробегают все ненулевые остатки). Затем:

  1. Алиса выбирает случайный секрет a и отправляет Бобу A = g в степени a по модулю p.
  2. Боб выбирает случайный секрет b и отправляет Алисе B = g в степени b по модулю p.
  3. Алиса вычисляет s = B в степени a по модулю p, Боб вычисляет s = A в степени b по модулю p.

Оба получают одно и то же число, поскольку B в степени a = (g в степени b) в степени a = g в степени ab = (g в степени a) в степени b = A в степени b (все вычисления по модулю p). Наблюдатель видит p, g, A и B, но, чтобы получить s, ему нужно найти a по A — решить задачу дискретного логарифмирования, для которой при больших p не известно эффективного алгоритма.

Разберём обмен на малых числах: p = 23, g = 5, секрет Алисы a = 6, секрет Боба b = 15. Возведение в большую степень выполняется методом последовательного возведения в квадрат: вычисляются g, g в квадрате, g в четвёртой, g в восьмой степени по модулю p, и нужная степень собирается из них по двоичной записи показателя.

Степень 5 по модулю 235 в 1-й5 во 2-й5 в 4-й5 в 8-й
Вычисление525 − 23 = 22 · 2 = 44 · 4 = 16

Алиса: a = 6 = 4 + 2, поэтому A = 4 · 2 = 8. Боб: b = 15 = 8 + 4 + 2 + 1, поэтому B = 16 · 4 · 2 · 5 = 640 = 27 · 23 + 19, то есть B = 19. По сети передаются числа 8 и 19. Далее Алиса вычисляет 19 в 6-й степени по модулю 23. Удобно заметить, что 19 ≡ −4, поэтому результат равен 4 в 6-й степени = 4096 = 178 · 23 + 2, то есть s = 2. Боб вычисляет 8 в 15-й степени по модулю 23: 8 во 2-й = 64 ≡ 18, 8 в 4-й ≡ 18 · 18 = 324 ≡ 2, 8 в 8-й ≡ 4, откуда 8 в 15-й ≡ 4 · 2 · 18 · 8 = 1152 = 50 · 23 + 2, то есть тоже s = 2. Общий секрет совпал, хотя ни a, ни b, ни s по сети не передавались. Схема обмена показана на рисунке ниже.

Обмен Диффи — Хеллмана на малых числах: открытые параметры, секреты сторон, передаваемые значения и общий секрет; посредник подменяет открытые значения
Обмен Диффи — Хеллмана при p = 23, g = 5 и атака посредника на неаутентифицированный обмен

В этом примере противник легко найдёт a перебором: достаточно вычислить не более 22 степеней числа 5. При реальных параметрах это невозможно: группа ffdhe2048 построена на 2048-битном простом числе, и лучшие известные алгоритмы оставляют ей стойкость порядка 100 бит. В ECDH роль возведения в степень играет умножение точки кривой на число, и X25519 при 32-байтовых ключах стойкостью превосходит ffdhe2048.

Обмен Диффи — Хеллмана сам по себе не аутентифицирует стороны. Активный посредник Мэллори, получив A от Алисы, отправляет Бобу собственное значение M, а Алисе в ответ — тоже M. Алиса вычисляет общий секрет с Мэллори, Боб — тоже с Мэллори, и посредник расшифровывает и перешифровывает весь трафик (нижняя часть рисунка). Защититься можно только аутентификацией открытых значений: в TLS сервер подписывает стенограмму рукопожатия, включающую его значение обмена, закрытым ключом, открытый ключ которого удостоверен сертификатом.

Прямая секретность

Общий секрет можно установить двумя способами. При «обмене ключами RSA» TLS 1.2 клиент шифрует случайный секрет открытым ключом сервера из сертификата. При эфемерном обмене стороны выполняют обмен Диффи — Хеллмана со свежими одноразовыми секретами a и b, а закрытый ключ сервера служит только для подписи; такой вариант обозначается буквой E: DHE, ECDHE.

Разница проявляется, когда закрытый ключ сервера через годы скомпрометирован — украден или извлечён через уязвимость вроде Heartbleed (2014). При обмене ключами RSA противник, записавший трафик, расшифрует весь архив переписки. При эфемерном обмене ключ сервера ничего не даёт: секреты a и b стёрты по окончании рукопожатия. Это свойство называется прямой секретностью (англ. forward secrecy, perfect forward secrecy, PFS). TLS 1.3 исключил обмен ключами RSA и статический DH, поэтому каждое полное рукопожатие TLS 1.3 обладает прямой секретностью 1.

Инфраструктура открытых ключей

Сертификат X.509 и разбор реального сертификата

Подпись сервера в рукопожатии доказывает только то, что собеседник владеет некоторым закрытым ключом. Клиенту нужно знать большее: что этот ключ принадлежит именно letsencrypt.org, а не посреднику. Связь между именем и открытым ключом удостоверяет сертификат — документ, содержащий имя субъекта, его открытый ключ и срок действия, подписанный третьей стороной, которой доверяет клиент, — удостоверяющим центром (УЦ, англ. Certificate Authority, CA). Совокупность УЦ, сертификатов, правил их выпуска, проверки и отзыва называют инфраструктурой открытых ключей (англ. Public Key Infrastructure, PKI). В Интернете используется формат сертификатов X.509 версии 3, профиль которого определён в RFC 5280 2.

Сертификат состоит из трёх частей: тела tbsCertificate (англ. to be signed), идентификатора алгоритма подписи и самой подписи УЦ, вычисленной над закодированным телом. Тело кодируется по правилам DER (англ. Distinguished Encoding Rules) нотации ASN.1, а для хранения в текстовых файлах переводится в Base64 между строками -----BEGIN CERTIFICATE----- и -----END CERTIFICATE----- (формат PEM). Получим и разберём сертификат реального сайта:

openssl s_client -connect letsencrypt.org:443 -servername letsencrypt.org </dev/null 2>/dev/null \
  | openssl x509 -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            05:43:93:3b:e3:86:b3:a2:b3:ad:d5:34:f2:10:3b:c8:b6:5f
        Signature Algorithm: ecdsa-with-SHA384
        Issuer: C = US, O = Let's Encrypt, CN = YE2
        Validity
            Not Before: Sep  4 14:34:32 2026 GMT
            Not After : Dec  3 14:34:31 2026 GMT
        Subject: CN = letsencrypt.org
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:e9:e5:40:7b:43:1e:46:b9:8e:45:e4:aa:cb:40: ...
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                16:68:8D:1E:2A:32:48:8C:B5:F1:10:0A:16:08:37:08:3B:9D:30:23
            X509v3 Authority Key Identifier:
                B9:59:F2:8E:CF:22:F0:86:D3:37:48:FF:76:14:18:BA:82:D8:55:87
            Authority Information Access:
                CA Issuers - URI:http://ye2.i.lencr.org/
            X509v3 Subject Alternative Name:
                DNS:cp.letsencrypt.org, ..., DNS:letsencrypt.com, DNS:letsencrypt.org,
                DNS:www.lencr.org, DNS:www.letsencrypt.com, DNS:www.letsencrypt.org
            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1
            X509v3 CRL Distribution Points:
                Full Name:
                  URI:http://ye2.c.lencr.org/96.crl
            CT Precertificate SCTs:
                Signed Certificate Timestamp:
                    Log ID    : D8:09:55:3B:94:4F:7A:FF:C8:16:19:6F:94:4F:85:AB:...
                    Timestamp : Sep  4 15:33:02.377 2026 GMT
                ...
    Signature Algorithm: ecdsa-with-SHA384
    Signature Value:
        30:65:02:31:00:ee:1d:b0:7c:35:88:98:15:57:b8:88:73:23: ...

Пройдём по полям (вывод получен в октябре 2026 года; при повторении команды значения будут другими, поскольку сертификат регулярно заменяется).

  • Version: 3 — версия формата, допускающая расширения; Serial Number — уникальный в пределах УЦ случайный номер длиной до 20 байт 2.
  • Signature Algorithm — алгоритм подписи УЦ (ECDSA с SHA-384); он же повторён перед Signature Value после тела.
  • Issuer и Subject — отличительные имена (англ. Distinguished Name) издателя и субъекта: страна C, организация O, общее имя CN. Издатель — промежуточный УЦ Let's Encrypt YE2.
  • Validity — интервал действия. Этот сертификат действует 90 дней. Короткие сроки уменьшают ущерб от компрометации ключа; требования CA/Browser Forum, объединения УЦ и производителей браузеров, предусматривают поэтапное сокращение максимального срока публичных сертификатов до 47 дней к 2029 году.
  • Subject Public Key Info — открытый ключ субъекта: точка на кривой P-256, записанная в несжатом виде (байт 04, затем координаты x и y по 32 байта).
  • Key Usage: Digital Signature и Extended Key Usage: TLS Web Server Authentication — ограничения назначения ключа: он может использоваться только для подписи в роли TLS-сервера. Пометка critical означает, что программа, не понимающая расширение, обязана отвергнуть сертификат 2.
  • Basic Constraints: CA:FALSE — сертификат конечного субъекта; подписывать им другие сертификаты нельзя.
  • Subject Key Identifier и Authority Key Identifier — идентификаторы ключа субъекта и ключа издателя. По совпадению AKI этого сертификата с SKI сертификата YE2 клиент находит издателя при построении цепочки.
  • Authority Information Access — адрес для загрузки сертификата издателя.
  • Subject Alternative Name (SAN) — список имён, для которых действителен сертификат: здесь десять имён DNS. Именно с SAN клиент сверяет имя из URL; поле CN субъекта браузеры для этого давно не используют. Допускаются шаблоны вида *.example.com, покрывающие ровно один уровень имени.
  • Certificate Policies: 2.23.140.1.2.1 — идентификатор политики CA/Browser Forum для сертификатов с проверкой только владения доменом (англ. Domain Validation, DV): УЦ удостоверился, что заявитель управляет доменом, но не проверял организацию.
  • CRL Distribution Points — адрес списка отзыва (подробнее ниже). Адреса службы OCSP в сертификате нет: Let's Encrypt прекратил поддержку OCSP в 2025 году.
  • CT Precertificate SCTs — подписанные метки времени журналов Certificate Transparency, доказывающие, что сертификат опубликован (подробнее ниже).

Цепочка доверия и корневые хранилища

Подпись на сертификате сайта проверяется открытым ключом издателя, доверие к которому следует из сертификата издателя, подписанного следующим УЦ, и так далее до корневого сертификата (англ. root certificate), который подписан своим же ключом (самоподписанный) и которому клиент доверяет безусловно, потому что тот заранее находится в его корневом хранилище (англ. trust store). Последовательность сертификатов от сертификата сервера до корня называют цепочкой сертификации, а процедуру её проверки — проверкой пути сертификации 2. Структура цепочки показана на рисунке.

Цепочка доверия: корневое хранилище клиента, самоподписанный корневой сертификат, промежуточный УЦ и сертификат сервера; стрелки подписи и сопоставление издателя и субъекта
Цепочка сертификатов и проверки, которые выполняет клиент на каждом звене

Корневые ключи хранят в отключённых от сети аппаратных модулях и используют лишь для подписи сертификатов промежуточных (выпускающих) УЦ; повседневная работа ведётся ключами промежуточных УЦ, и при компрометации отзывается промежуточный сертификат, а не корень, зашитый в миллиарды устройств. Сервер обязан отправить клиенту свой сертификат и все промежуточные; корень отправлять бессмысленно — клиент всё равно должен найти его у себя. Распространённая ошибка конфигурации — сервер отдаёт только собственный сертификат: браузер, который ранее встречал нужный промежуточный сертификат или загружает его по Authority Information Access, открывает сайт, а curl или приложение на Python сообщает «unable to get local issuer certificate».

Цепочка letsencrypt.org из примера выглядит так:

openssl s_client -connect letsencrypt.org:443 -servername letsencrypt.org </dev/null 2>/dev/null \
  | grep -E '^ *[0-9] s:|^ *i:|Verif'
 0 s:CN = letsencrypt.org
   i:C = US, O = Let's Encrypt, CN = YE2
 1 s:C = US, O = Let's Encrypt, CN = YE2
   i:C = US, O = ISRG, CN = Root YE
 2 s:C = US, O = ISRG, CN = Root YE
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X2
Verification: OK

Сервер прислал три сертификата. Третий — сертификат нового корня Root YE, подписанный более старым корнем ISRG Root X2 (перекрёстная подпись, англ. cross-signing): клиенты, в хранилище которых нового корня ещё нет, построят цепочку до ISRG Root X2. Так УЦ вводит новые корни, не дожидаясь обновления всех устройств.

При проверке пути для каждого звена клиент проверяет подпись ключом следующего звена, совпадение поля Issuer с Subject издателя, срок действия, наличие у издателя CA:TRUE и права keyCertSign, ограничение длины пути pathlen, отсутствие отзыва; для сертификата сервера дополнительно — совпадение имени с SAN и назначение serverAuth. Ошибка на любом шаге означает отказ в соединении.

Корневые хранилища ведут производители операционных систем и браузеров: Mozilla (хранилище используют Firefox и большинство дистрибутивов Linux — пакет ca-certificates, файл /etc/ssl/certs/ca-certificates.crt), Apple, Microsoft, Google. В каждом около полутора сотен корней, и любой из этих УЦ технически может выпустить сертификат для любого домена. Это главная слабость модели: безопасность всех сайтов определяется самым слабым УЦ. Угроза реальна: в 2011 году взломанный нидерландский УЦ DigiNotar выпустил поддельный сертификат для *.google.com, использованный для перехвата трафика. Ответом на эту слабость стали Certificate Transparency и записи CAA в DNS, ограничивающие круг УЦ, которые вправе выпускать сертификаты для домена.

Отзыв сертификатов: CRL, OCSP и stapling

Если закрытый ключ сервера скомпрометирован, сертификат нужно объявить недействительным до истечения срока — отозвать. RFC 5280 определяет списки отзыва сертификатов (англ. Certificate Revocation List, CRL): подписанный УЦ список серийных номеров отозванных сертификатов, публикуемый по адресу из расширения CRL Distribution Points и обновляемый с указанной периодичностью 2. Недостаток CRL — размер: у крупного УЦ список может содержать сотни тысяч записей.

Протокол OCSP (англ. Online Certificate Status Protocol) 9 позволяет запросить у УЦ подписанный ответ о статусе одного сертификата: «действителен», «отозван» или «неизвестен». Но OCSP замедляет открытие сайта, раскрывает УЦ, какие сайты посещает пользователь, и ненадёжен: при недоступности сервера OCSP браузеры продолжают соединение («мягкий отказ», англ. soft-fail), и посредник может просто заблокировать запрос.

Частичное решение — сшивание OCSP (англ. OCSP stapling): сервер сам периодически получает свежий подписанный ответ OCSP и прикладывает его к сертификату в рукопожатии (расширение status_request 13; в TLS 1.3 ответ передаётся внутри сообщения Certificate). Ответ подписан УЦ, поэтому сервер не может его подделать, а клиенту не нужно никуда обращаться. На практике отзыв в публичной PKI работает плохо, и отрасль движется в сторону коротких сроков действия: сертификат на несколько недель сам перестаёт действовать раньше, чем отзыв успел бы дойти до всех клиентов. Браузеры распространяют сжатые сведения об отозванных сертификатах собственными механизмами (CRLSets в Chrome, CRLite в Firefox), а Let's Encrypt, как отмечалось, полностью отказался от OCSP в пользу CRL.

Certificate Transparency, Let's Encrypt и ACME

Certificate Transparency (CT) 3 решает проблему «тайного» выпуска сертификатов. Каждый публичный сертификат записывается в несколько общедоступных журналов, устроенных как дерево Меркла — хеш-дерево, допускающее только добавление: изменение прошлой записи обнаруживается по несовпадению хешей. Журнал, приняв предварительную версию сертификата, выдаёт подписанную метку времени SCT (англ. Signed Certificate Timestamp) — обещание включить запись в дерево. УЦ вставляет SCT в итоговый сертификат (расширение CT Precertificate SCTs в разобранном выше примере), и браузеры Chrome и Safari отвергают публичные сертификаты без достаточного числа SCT от разных журналов. CT не мешает выпустить поддельный сертификат, но делает его выпуск заметным: владелец домена может отслеживать журналы, например через сервис crt.sh, и узнать о любом сертификате на своё имя.

curl -s 'https://crt.sh/?q=letsencrypt.org&output=json' | python3 -m json.tool | head -20

Долгое время сертификат стоил денег и требовал ручных действий раз в год. Некоммерческий УЦ Let's Encrypt, начавший выпуск в 2015 году, сделал сертификаты DV бесплатными и автоматическими, что и позволило перевести большинство сайтов на HTTPS. Протокол автоматического взаимодействия с УЦ стандартизован как ACME (англ. Automatic Certificate Management Environment) 4. Клиент ACME (например, certbot) регистрирует учётную запись, создаёт заказ на сертификат для списка имён и для каждого имени проходит проверку владения — одно из испытаний (англ. challenge):

  • http-01 — разместить файл со значением, полученным от УЦ и связанным с ключом учётной записи, по адресу http://<домен>/.well-known/acme-challenge/<токен>; УЦ загружает его по HTTP на порт 80;
  • dns-01 — опубликовать запись TXT _acme-challenge.<домен>; единственный способ получить шаблонный сертификат *.example.com;
  • tls-alpn-01 — ответить на соединение TLS с особым протоколом ALPN специальным сертификатом на порту 443.

После успешной проверки клиент отправляет запрос на сертификат с открытым ключом сервера и получает цепочку сертификатов. Проверка http-01 идёт по незащищённому HTTP и доверяет маршрутизации и DNS: противник, перехвативший трафик к домену (например, перехватом префикса BGP), может получить сертификат. Поэтому УЦ проверяют домен из нескольких точек в разных автономных системах.

Протокол TLS 1.3

Протокол записей

TLS состоит из двух уровней. Нижний — протокол записей (англ. record protocol) — разбивает поток данных на записи, шифрует и аутентифицирует их. Над ним работают протокол рукопожатия (англ. handshake), согласующий параметры и ключи, протокол предупреждений (англ. alert), сообщающий об ошибках и о штатном закрытии, и собственно прикладные данные. Каждая запись начинается 5-байтовым заголовком 1:

ПолеРазмерЗначение
ContentType1 байт22 (0x16) — handshake, 23 (0x17) — application_data, 21 (0x15) — alert, 20 (0x14) — change_cipher_spec
legacy_record_version2 байта0x0303 («TLS 1.2»), в первом ClientHello допускается 0x0301
length2 байтадлина содержимого, не более 2 в степени 14 байт открытого текста плюс до 256 байт расширения при шифровании

До окончания обмена ключами записи передаются открыто. После него каждая запись шифруется AEAD-алгоритмом, и снаружи все зашифрованные записи выглядят одинаково: тип application_data (23), версия 0x0303. Настоящий тип содержимого помещается внутрь шифротекста — в последний байт открытого текста, за которым могут следовать нулевые байты дополнения (структура TLSInnerPlaintext). Благодаря этому наблюдатель не отличает сообщения рукопожатия от данных и предупреждений, а дополнение позволяет скрывать длину. Заголовок записи служит присоединёнными данными AEAD и, следовательно, защищён от подмены.

Одноразовое число для AEAD не передаётся: каждая сторона хранит 64-битный счётчик записей, начинающийся с нуля для каждого нового ключа, и вычисляет одноразовое число как «исключающее или» счётчика (дополненного нулями слева до 12 байт) со статическим вектором инициализации iv, выведенным вместе с ключом. Отсюда защита от повтора, удаления и переупорядочения записей: если противник удалит или переставит запись, счётчики сторон разойдутся, тег не сойдётся, и соединение будет разорвано. Защиту от усечения потока обеспечивает предупреждение close_notify, которое сторона обязана отправить перед закрытием соединения: обрыв TCP без него свидетельствует о возможной атаке.

Рукопожатие TLS 1.3 по сообщениям

Полное рукопожатие TLS 1.3 занимает один круговой обход (1-RTT): клиент может отправлять прикладные данные сразу после получения первого ответа сервера. Это достигается тем, что клиент угадывает группу обмена ключами и сразу отправляет своё открытое значение Диффи — Хеллмана. Последовательность сообщений показана на рисунке; фигурными скобками в RFC 8446 помечены сообщения, зашифрованные ключами рукопожатия.

Временная диаграмма рукопожатия TLS 1.3: ClientHello с key_share, ServerHello, зашифрованные EncryptedExtensions, Certificate, CertificateVerify, Finished, Finished клиента и прикладные данные
Полное рукопожатие TLS 1.3 (1-RTT): открытые и зашифрованные сообщения, ключи, которыми они защищены

Рассмотрим сообщения по порядку 1.

  1. ClientHello (открыто). Содержит 32 случайных байта random, поле legacy_session_id, список наборов шифров cipher_suites в порядке предпочтения клиента и расширения. Ключевые расширения: supported_versions — список поддерживаемых версий, именно здесь клиент сообщает о TLS 1.3 (поле версии в самом сообщении по соображениям совместимости зафиксировано как 0x0303); supported_groups — группы обмена ключами (x25519, secp256r1, ffdhe2048…); key_share — открытые значения Диффи — Хеллмана для одной или нескольких групп; signature_algorithms — алгоритмы подписи, которые клиент умеет проверять; server_name — имя сервера (SNI); application_layer_protocol_negotiation (ALPN) — прикладные протоколы, например h2 и http/1.1; psk_key_exchange_modes и pre_shared_key — для возобновления сеанса.
  2. ServerHello (открыто). Сервер выбирает версию (расширение supported_versions со значением 0x0304), набор шифров и группу и отправляет своё значение key_share. Начиная с этого момента обе стороны могут выполнить обмен Диффи — Хеллмана и вывести ключи рукопожатия. Если ни одна из присланных клиентом групп серверу не подходит, вместо ServerHello отправляется HelloRetryRequest с указанием нужной группы, и рукопожатие удлиняется на один обход.
  3. EncryptedExtensions (зашифровано). Остальные расширения, не нужные для выработки ключей: выбранный протокол ALPN, подтверждение SNI. В TLS 1.2 они передавались открыто.
  4. CertificateRequest (зашифровано, необязательно) — запрос сертификата клиента при взаимной аутентификации.
  5. Certificate (зашифровано). Цепочка сертификатов сервера; к каждому сертификату могут быть приложены расширения, например сшитый ответ OCSP. Шифрование этого сообщения — новшество TLS 1.3: пассивный наблюдатель больше не видит сертификат сервера.
  6. CertificateVerify (зашифровано). Подпись закрытым ключом сервера над хешем стенограммы — всех сообщений рукопожатия с ClientHello по Certificate включительно. Подписываемые данные начинаются с 64 байт пробела и строки TLS 1.3, server CertificateVerify, чтобы подпись нельзя было использовать в другом контексте. Именно это сообщение доказывает, что открытое значение key_share прислал владелец сертификата, а не посредник.
  7. Finished (зашифровано). HMAC над стенограммой с ключом, выведенным из секрета рукопожатия. Подтверждает, что обе стороны видели одни и те же сообщения и получили одинаковые ключи: любое вмешательство в ClientHello, например удаление сильных шифров из списка, обнаружится здесь.
  8. Клиент проверяет цепочку сертификатов и имя, подпись CertificateVerify и Finished сервера, после чего отправляет собственный Finished (при взаимной аутентификации ему предшествуют Certificate и CertificateVerify клиента). Сразу за ним может следовать первый прикладной запрос.

После рукопожатия сервер обычно отправляет одно или несколько сообщений NewSessionTicket — билетов для возобновления сеанса (англ. session resumption). При следующем подключении клиент предъявит билет в расширении pre_shared_key, и стороны выведут ключи из общего ранее установленного секрета (PSK), не передавая сертификат; режим psk_dhe_ke при этом дополнительно выполняет обмен Диффи — Хеллмана и сохраняет прямую секретность.

Реальный захват отличается от схемы одной деталью. Промежуточные устройства (межсетевые экраны, системы обнаружения вторжений) обрывали соединения, непохожие на TLS 1.2, поэтому TLS 1.3 обычно работает в режиме совместимости (англ. middlebox compatibility mode): клиент заполняет legacy_session_id случайным значением, сервер повторяет его, и обе стороны отправляют ничего не значащую запись ChangeCipherSpec 1. Для старого оборудования такое рукопожатие выглядит как возобновление сеанса TLS 1.2.

Разберём начало реального ServerHello из захвата, выполненного в лабораторной работе (сервер на Python, клиент curl):

16 03 03 00 7a                 заголовок записи: handshake, 0x0303, длина 122
02 00 00 76                    тип 2 = ServerHello, длина 118
03 03                          legacy_version = 0x0303
bc d4 43 77 ... 48 59 db 59    random (32 байта)
20 ce 04 d9 ... 54 37          legacy_session_id: 32 байта, копия из ClientHello
13 02                          cipher_suite = TLS_AES_256_GCM_SHA384
00                             legacy_compression_method = 0
00 2e                          длина расширений = 46
00 2b 00 02 03 04              supported_versions: 0x0304 = TLS 1.3
00 33 00 24 00 1d 00 20 8e ad ...   key_share: группа 0x001d = x25519, ключ 32 байта

Всё, что следует за ServerHello, в захвате видно только как записи типа 23 с непрозрачным содержимым.

Наборы шифров и расписание ключей

В TLS 1.2 набор шифров (англ. cipher suite) задавал всё сразу: способ обмена ключами, алгоритм подписи, шифр и хеш, например TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 12. Число комбинаций росло мультипликативно и достигло нескольких сотен, многие из них были слабыми. В TLS 1.3 набор шифров определяет только AEAD-алгоритм и хеш-функцию для HKDF; обмен ключами и подпись согласуются независимо расширениями supported_groups и signature_algorithms. Наборов всего пять 1:

КодНабор шифровПримечание
0x1301TLS_AES_128_GCM_SHA256обязателен к реализации
0x1302TLS_AES_256_GCM_SHA384рекомендуется
0x1303TLS_CHACHA20_POLY1305_SHA256рекомендуется, быстр без аппаратного AES
0x1304TLS_AES_128_CCM_SHA256для ограниченных устройств
0x1305TLS_AES_128_CCM_8_SHA256короткий тег, для Интернета не рекомендуется

Все ключи соединения выводятся из нескольких исходных секретов по расписанию ключей (англ. key schedule), основанному на HKDF 18. Расписание показано на рисунке. На первом этапе из предварительно согласованного ключа PSK (при полном рукопожатии — из нулей) вычисляется ранний секрет Early Secret. На втором к нему подмешивается результат обмена (EC)DHE и получается секрет рукопожатия Handshake Secret, из которого выводятся секреты трафика рукопожатия клиента и сервера — ими шифруются сообщения от EncryptedExtensions до Finished. На третьем этапе получается главный секрет Master Secret, из которого выводятся секреты прикладного трафика, секрет для экспорта ключей и секрет для возобновления сеанса.

Расписание ключей TLS 1.3: HKDF-Extract с PSK, (EC)DHE и нулём, выводимые секреты трафика рукопожатия и прикладного трафика, ключи и векторы инициализации записей
Расписание ключей TLS 1.3: три этапа HKDF-Extract и выводимые из них секреты

Каждый секрет трафика привязан к стенограмме: функция Derive-Secret(секрет, метка, сообщения) использует в качестве контекста хеш всех сообщений рукопожатия до соответствующего момента. Из секрета трафика функцией HKDF-Expand-Label с метками key и iv получаются ключ и вектор инициализации AEAD для записей одного направления. Ключи разных направлений и разных этапов независимы: компрометация одного ничего не говорит о других. Секреты прикладного трафика можно обновлять по ходу долгого соединения сообщением KeyUpdate.

Эта конструкция объясняет механизм, используемый в лабораторной работе для расшифровки трафика. Если задать переменную окружения SSLKEYLOGFILE, библиотеки OpenSSL, NSS и другие записывают в указанный файл секреты трафика каждого соединения в формате, предложенном разработчиками NSS:

CLIENT_HANDSHAKE_TRAFFIC_SECRET <client_random> <секрет>
SERVER_HANDSHAKE_TRAFFIC_SECRET <client_random> <секрет>
CLIENT_TRAFFIC_SECRET_0 <client_random> <секрет>
SERVER_TRAFFIC_SECRET_0 <client_random> <секрет>
EXPORTER_SECRET <client_random> <секрет>

Соединение опознаётся по полю random из ClientHello, видимому в захвате. Имея секреты, Wireshark выводит из них ключи и расшифровывает записи. Журнал секретов — отладочное средство: он позволяет расшифровать трафик без закрытого ключа сервера, поэтому в рабочей среде его не включают.

Нулевой обход, SNI и ECH

При возобновлении сеанса клиент может отправить прикладные данные сразу в первом полёте, вместе с ClientHello, зашифровав их ключом, выведенным из PSK, — режим 0-RTT, или ранние данные (англ. early data). Выигрыш — целый круговой обход: для пользователя на мобильной сети это сотни миллисекунд. Цена — две слабости, явно оговорённые в RFC 8446 1. Во-первых, ранние данные не обладают прямой секретностью относительно PSK: ключ выводится до обмена Диффи — Хеллмана. Во-вторых, и это главное, ранние данные можно повторить: противник, записавший ClientHello с ранними данными, отправит его серверу ещё раз, и сервер, не имеющий общего состояния между репликами, обработает запрос повторно. Поэтому 0-RTT допустим только для идемпотентных запросов, например GET без побочных эффектов (понятие идемпотентности — тема 16), а сервер должен уметь отказаться от ранних данных; для HTTP предусмотрен код ответа 425 Too Early.

Расширение server_name (SNI, англ. Server Name Indication) 13 позволяет клиенту сообщить имя сервера в ClientHello, чтобы сервер, на одном адресе IP которого размещено много сайтов, выбрал нужный сертификат. Без SNI виртуальный хостинг HTTPS был бы невозможен без отдельного адреса на каждый сайт. Но ClientHello передаётся открыто, и SNI раскрывает посещаемый сайт любому наблюдателю; по нему же работают системы фильтрации трафика. В TLS 1.3 зашифрован сертификат сервера, но SNI остаётся открытым.

Решение — шифрование ClientHello (англ. Encrypted Client Hello, ECH), разработанное в IETF. Клиент заранее получает открытый ключ ECH сервера, как правило из записи DNS типа HTTPS, и отправляет внешний ClientHello с нейтральным именем фронтального сервера (например, провайдера CDN), внутри которого зашифрован настоящий ClientHello с истинным SNI. ECH имеет смысл, когда за фронтальным сервером много сайтов, и требует защищённого DNS, иначе имя видно в запросе DNS.

Сравнение с TLS 1.2

TLS 1.2 12 по-прежнему широко распространён. Его полное рукопожатие занимает два круговых обхода: ClientHello, затем ServerHello, Certificate, ServerKeyExchange, ServerHelloDone, затем ClientKeyExchange, ChangeCipherSpec, Finished клиента и, наконец, ChangeCipherSpec и Finished сервера.

СвойствоTLS 1.2TLS 1.3
Круговые обходы полного рукопожатия21 (0 при возобновлении с ранними данными)
Обмен ключамиRSA, статический DH, DHE, ECDHEтолько DHE и ECDHE, или PSK
Прямая секретностьтолько с DHE/ECDHEвсегда при полном рукопожатии
Шифрование записейCBC с MAC, RC4, AEADтолько AEAD
Сертификат серверапередаётся открытозашифрован
Сжатие, повторное согласованиеесть (источник атак CRIME, renegotiation)удалены
Выработка ключейPRF на основе HMACHKDF, расписание ключей со стенограммой
Наборы шифровсотни, кодируют всёпять, только AEAD и хеш

Многие из удалённых возможностей стали основой известных атак: CBC — POODLE и Lucky Thirteen, RC4 — статистические атаки на шифр, сжатие — CRIME, обмен ключами RSA — атаки Блейхенбахера и их современная версия ROBOT, произвольные группы DHE — Logjam. TLS 1.3 построен по принципу «меньше вариантов — меньше ошибок».

TLS на практике: аутентификация клиента, ошибки и атаки

Взаимная аутентификация mTLS

В обычном HTTPS сертификат предъявляет только сервер, а клиент аутентифицируется позднее, уже внутри канала, — паролем, cookie или токеном. При взаимной аутентификации (англ. mutual TLS, mTLS) сервер отправляет CertificateRequest, и клиент в ответ предъявляет собственную цепочку сертификатов и подписывает стенограмму сообщением CertificateVerify 1. Сервер проверяет цепочку клиента до корня, которому он доверяет, — как правило, до корня собственного, внутреннего УЦ организации, а не публичного. Сертификаты клиентов содержат в Extended Key Usage назначение clientAuth.

mTLS применяется там, где клиенты — программы, а не люди: между микросервисами (сервисные сетки выпускают сертификат каждому сервису автоматически), в API платёжных систем, при подключении устройств интернета вещей, в сетях с моделью «нулевого доверия» (тема 18). Главная трудность — управление жизненным циклом множества клиентских сертификатов.

Типичные ошибки проверки и конфигурации

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

ОшибкаСообщение OpenSSL и curlПричина и исправление
Неверное имяhostname mismatch, no alternative certificate subject name matchesимя из URL отсутствует в SAN; выпустить сертификат с нужными именами
Истёкший срокcertificate has expiredне настроено автоматическое обновление; использовать ACME и мониторинг сроков
Недоверенный кореньunable to get local issuer certificate, self-signed certificate in certificate chainкорень отсутствует в хранилище клиента; добавить свой корень или получить публичный сертификат
Неполная цепочкаunable to get local issuer certificateсервер не отправляет промежуточный сертификат; указать в конфигурации полную цепочку
Отключённая проверкаошибок нетв коде verify=False, curl -k; соединение уязвимо для посредника

Последняя строка особенно опасна. Проверку, мешающую в тестовой среде, отключают, и этот код попадает в рабочую систему: канал остаётся зашифрованным, но посредник предъявляет любой собственный сертификат. Правильное решение для тестовой среды — собственный УЦ, корень которого явно указан клиенту (curl --cacert), как это делается в лабораторной работе.

К ошибкам конфигурации относятся также поддержка устаревших версий и слабых наборов шифров. Рекомендации IETF по безопасному использованию TLS (RFC 9325, заменивший RFC 7525) требуют: не согласовывать SSL 2.0, SSL 3.0, TLS 1.0 и 1.1; поддерживать TLS 1.2 и предпочитать TLS 1.3; в TLS 1.2 использовать только наборы с ECDHE или DHE и AEAD; не использовать RC4, NULL-шифры, экспортные наборы и шифры со стойкостью менее 128 бит; применять ключи RSA и группы DH не менее 2048 бит 6. Проверить сервер можно командой openssl s_client с ограничением версии или специализированными сканерами.

# поддерживает ли сервер устаревший TLS 1.1? Ожидаемый ответ — отказ рукопожатия
openssl s_client -connect www.example.com:443 -tls1_1 </dev/null
# какие версия, набор шифров и группа согласованы по умолчанию
openssl s_client -connect www.example.com:443 -servername www.example.com </dev/null 2>/dev/null \
  | grep -E 'Protocol|Cipher is|Server Temp Key'

Атаки на понижение версии

Активный посредник может попытаться заставить стороны выбрать самый слабый общий вариант протокола — атака на понижение (англ. downgrade attack). Так работали атаки FREAK и Logjam (2015): посредник оставлял в ClientHello только «экспортные» наборы с 512-битными ключами, сохранившиеся в реализациях с 1990-х годов, и вскрывал слабый ключ за минуты. Другой путь — повторное подключение: браузеры при обрыве рукопожатия повторяли его с более старой версией, и посредник, обрывая соединения, добивался перехода на SSL 3.0, уязвимый к POODLE.

TLS 1.3 защищается от понижения двумя способами 1. Во-первых, стенограмма всего рукопожатия, включая исходный ClientHello, подписывается сервером в CertificateVerify и подтверждается в Finished, поэтому любое изменение предложений клиента обнаруживается. Во-вторых, сервер, поддерживающий TLS 1.3, но вынужденный согласовать TLS 1.2 или ниже, записывает в последние 8 байт своего random особое значение: байты 44 4F 57 4E 47 52 44 01 (ASCII-строка DOWNGRD и байт 01) при согласовании TLS 1.2 и 44 4F 57 4E 47 52 44 00 при более старой версии. Поле random входит в подписываемые данные даже в старых версиях протокола, поэтому посредник не может его изменить, а клиент с поддержкой TLS 1.3, увидев этот маркер, разрывает соединение.

HSTS: защита от перехода на HTTP

Даже идеально настроенный HTTPS не помогает, если пользователь до него не дошёл. Пользователь набирает bank.example, браузер обращается по HTTP на порт 80 и получает перенаправление на https://bank.example/. Посредник перехватывает этот первый незащищённый запрос, сам соединяется с банком по HTTPS, а пользователю отдаёт страницы по HTTP; ошибок сертификата нет, потому что нет TLS. Эта атака называется SSL stripping.

Защита — механизм HTTP Strict Transport Security (HSTS) 5. Сервер, отвечая по HTTPS, добавляет заголовок

Strict-Transport-Security: max-age=31536000; includeSubDomains

Получив его, браузер в течение max-age секунд (здесь — один год) обращается к этому домену, а при includeSubDomains — и ко всем поддоменам, только по HTTPS: запросы на http:// переписываются в https:// до отправки, а ошибки сертификата становятся неустранимыми — пользователь не может нажать «всё равно продолжить». Заголовок, полученный по HTTP, игнорируется. Слабое место HSTS — первый визит до получения заголовка; его закрывает список предварительной загрузки (англ. HSTS preload list), вшитый в код браузеров.

Итоги темы

Сеть, через которую проходят пакеты, следует считать враждебной: противник может читать, изменять, повторять и вставлять данные и встраиваться между сторонами. Защищённый канал должен обеспечить конфиденциальность, целостность и аутентификацию, и эти свойства не сводятся друг к другу: шифрование без аутентификации собеседника не защищает от посредника. TLS решает эту задачу поверх TCP и предоставляет приложению защищённый поток байтов; тот же протокол рукопожатия встроен в QUIC.

Протокол собран из небольшого набора примитивов. Данные защищает AEAD-алгоритм с одноразовым числом из номера записи. Общий секрет устанавливается эфемерным обменом Диффи — Хеллмана, стойкость которого основана на трудности дискретного логарифмирования, а эфемерность даёт прямую секретность. Подпись сервера над стенограммой связывает обмен с владельцем сертификата, а все ключи выводятся функцией HKDF из общих секретов и хеша стенограммы.

Слабое звено системы — не математика, а доверие к открытым ключам. Сертификат X.509 связывает имя с ключом подписью удостоверяющего центра, клиент проверяет цепочку до корня из своего хранилища, а инфраструктура отзыва, Certificate Transparency, короткие сроки действия и автоматический выпуск по протоколу ACME компенсируют неизбежные ошибки УЦ и владельцев ключей. Большинство практических проблем — неверное имя, просроченный сертификат, неполная цепочка, отключённая проверка, устаревшие версии — являются ошибками конфигурации, а не слабостями протокола.

TLS 1.3 упростил протокол: одно рукопожатие за один обход, обязательная прямая секретность, только AEAD, зашифрованный сертификат, защита от понижения версии. Открытыми остаются метаданные: адреса, размеры и время, а без ECH — имя сервера. TLS защищает отдельные соединения приложений; в следующей теме рассматриваются межсетевые экраны и защита сети как целого, а в теме 19 — туннели и виртуальные частные сети, которые защищают весь трафик между узлами или сетями на сетевом уровне.

Литература

  1. Rescorla E. The Transport Layer Security (TLS) Protocol Version 1.3. — RFC 8446, IETF, 2018. https://www.rfc-editor.org/rfc/rfc8446
  2. Cooper D., Santesson S., Farrell S., Boeyen S., Housley R., Polk W. Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile. — RFC 5280, IETF, 2008. https://www.rfc-editor.org/rfc/rfc5280
  3. Laurie B., Langley A., Kasper E. Certificate Transparency. — RFC 6962, IETF, 2013. https://www.rfc-editor.org/rfc/rfc6962
  4. Barnes R., Hoffman-Andrews J., McCarney D., Kasten J. Automatic Certificate Management Environment (ACME). — RFC 8555, IETF, 2019. https://www.rfc-editor.org/rfc/rfc8555
  5. Hodges J., Jackson C., Barth A. HTTP Strict Transport Security (HSTS). — RFC 6797, IETF, 2012. https://www.rfc-editor.org/rfc/rfc6797
  6. Sheffer Y., Saint-Andre P., Fossati T. Recommendations for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS). — RFC 9325, IETF, 2022 (заменяет RFC 7525). https://www.rfc-editor.org/rfc/rfc9325
  7. Diffie W., Hellman M. E. New Directions in Cryptography. — IEEE Transactions on Information Theory, 1976, Vol. 22, No. 6, С. 644–654, DOI: 10.1109/TIT.1976.1055638.
  8. Krawczyk H., Eronen P. HMAC-based Extract-and-Expand Key Derivation Function (HKDF). — RFC 5869, IETF, 2010. https://www.rfc-editor.org/rfc/rfc5869
  9. Santesson S., Myers M., Ankney R., Malpani A., Galperin S., Adams C. X.509 Internet Public Key Infrastructure Online Certificate Status Protocol — OCSP. — RFC 6960, IETF, 2013. https://www.rfc-editor.org/rfc/rfc6960
  10. Langley A., Hamburg M., Turner S. Elliptic Curves for Security. — RFC 7748, IETF, 2016. https://www.rfc-editor.org/rfc/rfc7748
  11. Krawczyk H., Bellare M., Canetti R. HMAC: Keyed-Hashing for Message Authentication. — RFC 2104, IETF, 1997. https://www.rfc-editor.org/rfc/rfc2104
  12. Dierks T., Rescorla E. The Transport Layer Security (TLS) Protocol Version 1.2. — RFC 5246, IETF, 2008. https://www.rfc-editor.org/rfc/rfc5246
  13. Eastlake D. Transport Layer Security (TLS) Extensions: Extension Definitions. — RFC 6066, IETF, 2011. https://www.rfc-editor.org/rfc/rfc6066
  14. McGrew D. An Interface and Algorithms for Authenticated Encryption. — RFC 5116, IETF, 2008. https://www.rfc-editor.org/rfc/rfc5116