Сети и протоколы
Расписание ключей, SNI, ALPN и расширения
Вы понимаете, как ECDHE производит общий секрет. Но этот секрет не используется напрямую как ключ шифрования — он поступает в дерево выведения ключей, которое производит отдельные ключи для трафика рукопожатия, трафика приложения, возобновления и экспорта. Это дерево — расписание ключей HKDF, и каждый ключ, используемый TLS 1.3, вытекает из него.
Расписание ключей HKDF (RFC 8446 §7.1)
TLS 1.3 выводит каждый ключ из двухфазного дерева HKDF:
Фаза 1 — Early Secret.
early_secret = HKDF-Extract(salt=0, IKM=PSK_or_zero)Если нет PSK: IKM — все нули. Из раннего секрета Derive-Secret(early_secret, "derived", "") производит соль для фазы 2.
Фаза 2 — Handshake Secret.
handshake_secret = HKDF-Extract(salt=derived_from_early, IKM=ECDHE_shared_secret)Из секрета рукопожатия:
- Ключ трафика клиента рукопожатия:
HKDF-Expand-Label(hs_secret, "c hs traffic", transcript, hash_len) - Ключ трафика сервера рукопожатия:
HKDF-Expand-Label(hs_secret, "s hs traffic", transcript, hash_len)
Фаза 3 — Master Secret.
master_secret = HKDF-Extract(salt=derived_from_handshake, IKM=zero)Из мастер-секрета:
- Ключ трафика приложения клиента:
HKDF-Expand-Label(master, "c ap traffic", transcript, hash_len) - Ключ трафика приложения сервера:
HKDF-Expand-Label(master, "s ap traffic", transcript, hash_len) - PSK возобновления:
Derive-Secret(master, "res master", transcript)
Метки ("c hs traffic", "s ap traffic" и т.д.) — разделители доменов: компрометация одного пути вывода ключей не может помочь злоумышленнику на другом пути.
- Хэш для TLS_AES_128_GCM_SHA256
- SHA-256
- Хэш для TLS_AES_256_GCM_SHA384
- SHA-384
- Ключи трафика рукопожатия защищают
- EncryptedExtensions → Finished
- Ключи трафика приложения защищают
- Все данные после Finished
- Примитив обновления ключа
- HKDF-Expand-Label(current, 'traffic upd', '', hash_len)
- Набор шифров контролирует
- Шифр AEAD + хэш для расписания; обмен ключами — отдельно
Разделение наборов шифров в TLS 1.3
TLS 1.2 упаковывал три выбора в одну строку набора шифров: обмен ключами, массовый шифр и MAC. TLS 1.3 разделил их:
- Согласование именованных групп — выбирает кривую ECDHE (
x25519,secp256r1, …) - Согласование алгоритмов подписи — выбирает способ верификации подписи сертификата (
ecdsa_secp256r1_sha256,ed25519, …) - Набор шифров — выбирает только шифр AEAD и хэш для расписания ключей:
TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_256_GCM_SHA384
В TLS 1.2 было сотни допустимых комбинаций. В TLS 1.3 — пять стандартных наборов.
SNI: указание имени сервера
Многие имена хостов разделяют один IP-адрес (виртуальный хостинг, CDN). Без дополнительной информации сервер не может выбрать правильный сертификат до чтения запроса — запрос зашифрован. SNI решает эту проблему, заставляя клиента отправлять имя хоста в открытом виде внутри ClientHello. Сервер использует его для выбора правильного сертификата перед ответом.
Цена конфиденциальности: любой в сети узнаёт, какой именно хост вы посетили. Это мотивация для Encrypted ClientHello (ECH), рассматриваемого в следующем уроке.
ALPN: согласование протокола прикладного уровня
После установки TLS клиент и сервер должны договориться о протоколе выше: HTTP/1.1, HTTP/2 или HTTP/3. ALPN помещает список в ClientHello (обычно ["h2", "http/1.1"]), и сервер выбирает один в ServerHello. Поскольку оба сообщения защищены хэшем транскрипта, промежуточный узел не может удалить h2, чтобы принудительно использовать HTTP/1.1 — вмешательство нарушает Finished.
HelloRetryRequest
Если key_share клиента содержит только кривую, которую сервер не поддерживает, сервер отправляет HelloRetryRequest (закодированный как специальный ServerHello со значением magic_retry_request_random), содержащий cookie и желаемую именованную группу. Клиент отправляет новый ClientHello с правильным key_share и cookie. Это добавляет один RTT, но встречается редко: современные клиенты предлагают x25519 и P-256 одновременно, чтобы предотвратить это. Cookie также позволяет серверам без сохранения состояния (балансировщикам нагрузки без привязки соединений) завершить HRR без сохранения состояния между двумя ClientHello.
OCSP-прикрепление и CRLite
Традиционный OCSP: клиент делает живой запрос к OCSP-ответчику CA для проверки отзыва — что раскрывает историю просмотров и добавляет задержку. OCSP-прикрепление переносит это на сервер: сервер периодически получает подписанный OCSP-ответ и прикрепляет его к TLS-рукопожатию через расширение status_request. Никакого дополнительного обращения клиента, никакой утечки конфиденциальности.
Реальность после 2024 года: Let’s Encrypt прекратил обслуживание OCSP 2025-05-07. Firefox 137 поставил CRLite по умолчанию — компактный, подписанный каскад фильтров Блума, охватывающий все отзывы WebPKI из журналов CT, обновляемый каждые 12 часов. Новая инфраструктура должна ориентироваться на 90-дневные (скоро 47-дневные) сроки жизни сертификатов вместо OCSP в реальном времени.
Прозрачность сертификатов (CT)
Каждый публично доверенный сертификат должен появляться как минимум в двух журналах CT — добавляемых только, публично проверяемых, подписанных деревьях Меркла. Клиент проверяет, что сертификат содержит Signed Certificate Timestamps (SCT). Chrome и Safari отклоняют соединения с отсутствующими или недействительными SCT. CT сделала экосистему CA проверяемой: недобросовестный CA не может выдать сертификат для example.com, не оставив публичной записи с защитой от фальсификации, а инструменты обнаружения (crt.sh) оповещают владельцев доменов в течение минут.
Почему расписание ключей HKDF использует разделённые по домену метки для каждого ключа?
Почему OCSP-прикрепление выводится из использования в WebPKI в 2024–2026 годах?
Упорядочите иерархию выведения ключей TLS 1.3 от входных данных до выходных:
- 1 Входные данные: общий секрет ECDHE
- 2 HKDF-Extract производит handshake secret
- 3 Derive-Secret производит секреты трафика рукопожатия (клиента и сервера)
- 4 HKDF-Expand-Label производит ключи трафика рукопожатия и IV
- 5 Записи под ключом трафика рукопожатия защищают EncryptedExtensions → Finished
- 6 После Finished: выводится master secret, производятся ключи трафика приложения
Почему это работает
Обновление ключа для долгоживущих соединений. TLS 1.3 поддерживает смену ключей трафика приложения в середине соединения без повторного согласования: любая из сторон отправляет KeyUpdate, выводит новый секрет трафика приложения через HKDF-Expand-Label(current, "traffic upd", "", hash_len) и начинает использовать новый ключ. Партнёр отвечает своим KeyUpdate. Современные шифры AEAD выдерживают огромные объёмы данных до того, как смена ключей становится критически важной для безопасности, но механизм существует для сверхдолгоживущих сессий (постоянные WebSocket-соединения, каналы сервисной сетки, открытые часами).
- 01Как ALPN предотвращает атаки понижения версии HTTP?
- 02Какую информацию раскрывает SNI, и почему важен ECH?
- 03Когда TLS 1.3-сервер отправляет HelloRetryRequest, и что клиент обязан включить в повторный запрос?
Расписание ключей HKDF выводит дерево ключей из общего секрета ECDHE — ранний секрет, секрет рукопожатия, мастер-секрет — каждый разделён метками домена, так что компрометация одного пути не может помочь злоумышленнику на другом. TLS 1.3 также разделил согласование набора шифров на три независимые оси (именованная группа, алгоритм подписи, шифр AEAD), сократив сотни комбинаций TLS 1.2 до пяти стандартных наборов. SNI отправляет целевое имя хоста в открытом виде для работы виртуального хостинга; ALPN согласовывает протокол приложения над TLS внутри защищённого от вмешательства транскрипта. HelloRetryRequest обрабатывает крайний случай, когда первое предположение клиента о key_share неверно. OCSP-прикрепление снижало задержку проверки отзыва для каждого соединения; CRLite (Firefox 137+) заменяет его каскадом фильтров Блума с локальным обновлением.
встречается в47
- Federation и lookahead: батчинг за пределами DataLoadermiddle
- Senior GraphQL API: scheduling-контракт, изоляция арендаторов, наблюдаемостьsenior
- Инвалидация, dirty-биты и containmiddle
- Слои композитора: продвижение, перекрытие и память GPUmiddle
- Observability в проде: LoAF, INP и полная поверхность атакиsenior
- Hidden classes, деревья переходов и расположение в памятиmiddle
- V8 в production: Isolates, сжатие указателей и реальные аварииsenior
- Что такое воркеры и зачем они нужныjunior
- Механика web workers: dedicated, shared и OffscreenCanvasmiddle
- Structured clone и transferablesmiddle
- SharedArrayBuffer, Atomics и cross-origin isolationsenior
- Пулы воркеров, Comlink и наблюдаемость в продакшенеsenior
- Восемь слоёв трассировки: от service worker до второй навигацииmiddle
- Пять канонических поломок: где производство стабильно ломаетсяsenior
- Метод трёх треков: чтение трасс и построение системы мониторингаsenior
- Лок и single-flight: ограничение параллельных rebuildmiddle
- Stale-while-revalidate и CDN request coalescingmiddle
- Детектирование stampede и дизайн TTL для продакшенаmiddle
- Метастабильный сбой, fencing-токены и production-постмортемыsenior
- Что такое отношение: таблицы, строки, ключи и ограниченияjunior
- Ограничения, ключи и типы данных Postgresmiddle
- JSONB, массивы и когда side table побеждаетmiddle
- Целостность схемы: deferral, версионирование и сбои в продакшнеsenior
- Где происходит data fetching — и почему это решает LCPjunior
- React Server Components и Suspense streamingmiddle
- Senior internals: RSC payload, слои кэша и production паденияsenior
- Что такое OpenTelemetry: API, SDK, Collector, OTLPjunior
- Сигналы OTel, Semantic Conventions и проводной формат OTLPmiddle
- Collector OTel: receivers, processors, exporters и паттерны развёртыванияmiddle
- Vendor-нейтральность, eBPF-инструментирование, Operator и OTel в браузере и serverlesssenior
- Эксплуатация OTel Collector: надёжность, version skew, режимы отказа и управлениеsenior
- Что такое trace propagation и почему сломанная propagation хуже отсутствия трейсовjunior
- traceparent и tracestate: полный формат W3C-заголовкаmiddle
- Baggage и async-границы: перенос контекста через очереди и callback''''иmiddle
- Async context на разных языках, service mesh, миграция B3 и безопасностьsenior
- Production-сбои propagation, span links и платформенный дизайнsenior
- Debugging-воронка: SLO → RED → trace → profilejunior
- Архитектура OTel: один SDK, четыре сигнала, один wire-форматmiddle
- Петля инцидента: от пейджера до постмортема до предотвращенияmiddle
- Масштаб, безопасность и ROI наблюдаемых системsenior
- At-most-once, at-least-once, exactly-once: три контракта доставкиjunior
- Consumer-side dedup: самый дешёвый путь к exactly-once processingmiddle
- Exactly-once в production: impossibility-доказательство, гибридные паттерны и реальные инцидентыsenior
- Что такое OAuth и почему пароли — не ответjunior
- Authorization code flow с PKCEmiddle
- Sender-constrained токены: DPoP и mTLSsenior
- OAuth в production: audience атаки, observability и реальные провалыsenior