open atlas
↑ К треку
Основы безопасности SECF · 04 · 01

TLS вглубь

Валидный замок доказывает лишь, что канал зашифрован и сервер аутентифицирован прямо сейчас. Переживёт ли эта защита кражу ключа, активное понижение или открытый первый участок — зависит от forward secrecy, минимальной версии протокола и HSTS.

SECF Middle ◷ 16 min
Уровень
ОсновыJuniorMiddleSenior

Бэкап с приватным ключом твоего API-сервера всплывает в публичном бакете. Неприятно, но ты ротируешь ключ, отзываешь сертификат и идёшь дальше — инцидент локализован, объявляешь ты команде. А потом исследователь присылает тебе расшифрованный транскрипт запроса, который один из твоих клиентов сделал три месяца назад: его bearer-токен, открытым текстом, вытащенный из трафика, перехваченного задолго до утечки. Тогда твоего сервера никто не трогал. Атакующий просто записал шифртекст и подождал. Твой TLS был «включён» всё это время — валидный сертификат, зелёный замок — и всё равно отдал месяцы истории в тот миг, когда один долговременный ключ сбежал. Замок никогда не был всей историей.

К концу урока ты будешь знать, что на самом деле гарантирует валидное TLS-соединение, почему forward secrecy решает, утечёт ли прошлое при краже ключа, как атака понижения побеждает «мы поставили сильные шифры первыми» и почему SSL stripping — единственная брешь, которую не закроет ни один шифр.

Что на самом деле устанавливает handshake

TLS-соединение делает три дела сразу, и смешение их — корень большинства ошибок в TLS. Оно аутентифицирует сервер: сертификат, проверенный по цепочке до доверенного корня, доказывает, что ты говоришь с настоящим api.example.com, а не с самозванцем. Оно устанавливает сессионный ключ: обе стороны договариваются о симметричных ключах, так что остаток разговора зашифрован. И оно даёт целостность: AEAD-шифр (AES-GCM, ChaCha20-Poly1305) делает подмену обнаружимой. Зелёный замок означает, что все три условия держались в момент handshake — и больше ничего. Он ничего не говорит о том, что станет с этим трафиком, если приватный ключ сервера украдут завтра, и ничего о том, как соединение вообще началось.

В TLS 1.3 (RFC 8446) handshake — это один round trip. Клиент шлёт ClientHello, перечисляя версии протокола и наборы шифров, которые он поддерживает, плюс свеже сгенерированный эфемерный публичный ключ Диффи-Хеллмана. Сервер отвечает своим сертификатом, своим эфемерным ключом DH и Finished, который подписывает весь транскрипт. Обе стороны комбинируют два эфемерных ключа, чтобы вывести сессионный секрет. Ключевая деталь: долговременный ключ сертификата используется только чтобы подписать handshake (доказать личность), а не чтобы зашифровать сессионный секрет. Именно это разделение делает возможным следующий раздел.

Forward secrecy: утечёт ли прошлое при краже ключа?

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

При переносе ключа по RSA — старом дефолте TLS ≤1.2 — ответ «да». Клиент генерирует сессионный секрет, шифрует его публичным RSA-ключом сервера и шлёт. Кто держит соответствующий приватный ключ, тот восстановит этот сессионный секрет — навсегда. Так что атакующий, записавший шифртекст сегодня и укравший ключ через полгода, расшифрует всё ретроактивно. Это модель «собери сейчас, расшифруй потом», и это ровно инцидент из вступления: месяцы перехваченного трафика, отпертые одним утёкшим ключом.

При обмене ключами (EC)DHE — эфемерном Диффи-Хеллмане — сессионный секрет выводится из эфемерных ключей на каждое соединение, которые генерируются, используются и выбрасываются. Долговременный ключ только подписывает; он никогда не несёт секрет. После handshake на диске не остаётся ничего, чем можно восстановить ключ этой сессии. Кража долговременного ключа позволяет атакующему выдавать себя за сервер в будущем (что решают ротация и отзыв), но не расшифровать ни одной записанной прошлой сессии. Это свойство — forward secrecy (прямая секретность), и TLS 1.3 делает его обязательным: перенос ключа по RSA удалён из протокола целиком. В TLS 1.2 ты получаешь его, только если ограничишь наборы шифров до ECDHE.

Так что «у нас HTTPS и сертификат валиден» — не то же самое утверждение, что «наш записанный трафик защищён от будущей кражи ключа». Первое — про аутентификацию и шифрование прямо сейчас; второе — чисто про то, является ли каждый принятый набор ECDHE. Замок над набором с переносом ключа по RSA — это бомба замедленного действия.

Почему это работает

Почему перенос ключа по RSA вообще был дефолтом при таком изъяне? Потому что годами он был проще и быстрее: сервер делал одну расшифровку приватным ключом на handshake и пропускал эфемерную математику DH, что было важно, когда процессоры были медленнее, а ECDHE не имел аппаратного ускорения. Forward secrecy казалась роскошью — пока «собери сейчас, расшифруй потом» не перестало быть теорией (массовый перехват трафика плюс нависающая угроза, что будущий квантовый компьютер или одна утечка ключа ретроактивно отопрут годы архивов). Соотношение цена/выгода перевернулось: современные процессоры делают ECDHE дешёвым, а цена отсутствия forward secrecy катастрофична и ретроактивна. Проектировщики TLS 1.3 закрыли спор, удалив вариант без прямой секретности из протокола.

Атаки понижения: почему «мы поставили сильные шифры первыми» не срабатывает

Распространённая и неверная ментальная модель: «наш сервер перечисляет сильные ECDHE-AEAD-наборы первыми в своём порядке предпочтений, и ssl_prefer_server_ciphers включён, поэтому современный клиент всегда садится на сильный набор». Порядок предпочтений сервера решает победителя, лишь когда оба конца согласуются честно и взаимно поддерживают несколько наборов. Против активного атакующего он неважен.

Атака понижения — это активный человек посередине, подменяющий саму процедуру согласования. Атакующий перехватывает ClientHello и срезает сильные наборы, предложенные клиентом, или вынуждает откат к более старой версии протокола. Тогда сервер видит меню, содержащее только слабый вариант — обмен ключами экспортного уровня (FREAK, Logjam) или старый CBC-набор (POODLE на TLS 1.0) — и выбирает его, потому что только это, как кажется, и предложено. Твоё аккуратное упорядочивание так и не вступило в игру, потому что атакующий управляет тем, что доходит до сервера.

Фикс — не переупорядочивание. Фикс — удаление: тебя нельзя понизить до версии протокола или шифра, на которых сервер наотрез отказывается говорить. Задай минимальную версию протокола TLS 1.2 (в идеале 1.3) и убери каждый не-AEAD, не-ECDHE и экспортный набор из того, что сервер вообще примет. С отключёнными TLS 1.0/1.1 и убранными слабыми наборами в меню не остаётся слабого варианта, который атакующий мог бы навязать. (TLS 1.3 ещё и связывает весь транскрипт handshake в MAC Finished, так что любая подмена предложенных наборов обнаруживается — но «не предлагай этого» остаётся устойчивым контролем.)

Заявление о соединенииЧто оно реально доказываетЧего оно НЕ доказывает
Валидный сертификат / зелёный замокКанал зашифрован + сервер аутентифицирован сейчасЧто прошлый трафик переживёт кражу ключа
Сильные наборы перечислены первымиПобедитель среди честных, взаимных наборовУстойчивость к активному понижению
Навязаны только ECDHE-наборыForward secrecy — прошлые сессии в безопасностиЧто первый участок вообще был зашифрован
Редирект 301 http → httpsЧестные клиенты оказываются на HTTPSЗащиту от активного SSL stripping

SSL stripping: брешь, которую не закроет ни один шифр

Все свойства выше защищают байты, которые уже внутри TLS-сессии. Ни одно из них не защищает момент до того, как сессия существует. Когда пользователь набирает example.com, первый запрос браузера часто уходит как обычный http://. Человек посередине, выполняющий stripping (срыв), перехватывает этот открытый запрос, говорит HTTPS с твоим сервером от имени жертвы и отдаёт ей обычный HTTP — пользователь так и не видит замка, а каждый байт течёт открытым текстом атакующему.

Инстинкт «мы выдаём редирект 301 с http:// на https:// на каждый запрос» это не чинит. Редирект срабатывает, лишь если атакующий пропускает открытый запрос до твоего сервера. Активный человек посередине при срыве перехватывает первый http://-запрос до того, как он вообще дойдёт, никогда не следует твоему редиректу и просто проксирует чистый HTTP жертве. Серверный редирект не может защитить запрос, который до сервера не доходит.

Настоящий контроль — это HSTS (HTTP Strict Transport Security, RFC 6797). Сервер шлёт заголовок Strict-Transport-Security, и браузер запоминает: для этого домена никогда больше не слать открытый текст — повышать http:// до https:// до того, как запрос покинет браузер. Теперь открытого первого участка для перехвата нет. Остаётся брешь самого первого визита, до того как заголовок HSTS был увиден (доверие при первом обращении); список предзагрузки закрывает её, поставляя политику HSTS внутри самого браузера, так что повышение происходит даже на холодном первом запросе. Вот почему усиление шифров и протоколов необходимо, но недостаточно: срыв происходит вне TLS, и только HSTS переносит защиту в браузер, где живёт открытый участок.

Почему это работает

Почему редиректа недостаточно, конкретно? Представь жертву на Wi-Fi кофейни, который контролирует атакующий. Браузер жертвы шлёт GET http://bank.example/. Машина атакующего отвечает на этот запрос сама — она не пересылает его в банк — и параллельно открывает свою чистую HTTPS-сессию к настоящему банку. Для жертвы это обычный (открытый) сайт; для банка это нормальный HTTPS-клиент. «301 → https» банка жертва никогда не видит, потому что запрос жертвы до банка не дошёл. Если HSTS для bank.example уже известен, браузер вообще отказывается выпускать этот http://-запрос и идёт сразу на HTTPS, убирая единственное открытое сообщение, с которым атакующему было с чем работать.

Как сеньор усиливает TLS-эндпоинт

Сложи четыре свойства вместе — и плейбук становится механическим. Сеньорский ход — принять именованный, стандартизированный профиль (рекомендации Mozilla Server Side TLS), а не подбирать шифры руками, потому что ручной список устаревает и возвращает слабые варианты. Версии: минимум TLS 1.2, сохрани 1.3, удали SSL 3.0 / TLS 1.0 / 1.1 — это закрывает понижение версии удалением. Наборы: только обмен ECDHE, AEAD-шифры (AES-GCM, ChaCha20-Poly1305), без переноса ключа по RSA, без CBC — это даёт forward secrecy и убирает экспортные/POODLE-цели. Срыв: Strict-Transport-Security с длинным max-age, includeSubDomains, затем preload, плюс 301 с http на https. Операционная гигиена: отдавай полную цепочку сертификата (один листовой вызывает баг «работает в Chrome, падает в curl», потому что часть клиентов не построят путь доверия), и включи OCSP stapling, чтобы статус отзыва ехал с handshake. Проверяй с провода, не из файла: прогони SSL Labs или testssl.sh по живому эндпоинту, потому что деплой мог молча сохранить старую конфигурацию, которой нет в файле. Повторяющаяся дисциплина: состояние TLS — это то, что проверяешь на живом эндпоинте непрерывно, а не вычитываешь из задуманной конфигурации однократно.

Выбери лучший вариант

Отчёт пентеста говорит, что твой публичный сайт уязвим к понижению протокола, потому что TLS 1.0 и экспортный шифр всё ещё включены. Инженер предлагает: оставить все версии и наборы включёнными «для совместимости», но поднять сильные ECDHE-AEAD-наборы в начало порядка предпочтений сервера. Каково верное решение?

Викторина

Команда подтверждает, что их сайт показывает валидный сертификат и зелёный замок каждому пользователю. Почему этого недостаточно, чтобы заключить, что записанный трафик защищён, если приватный ключ сервера позже украдут?

Викторина

Почему редирект 301 с http:// на https:// не останавливает активного атакующего при SSL stripping, тогда как HSTS останавливает?

Расставь шаги по порядку

Упорядочи шаги handshake TLS 1.3, устанавливающего сессию с прямой секретностью:

  1. 1 Клиент шлёт ClientHello с поддерживаемыми версиями, наборами шифров и эфемерным ключом
  2. 2 Сервер отвечает своим сертификатом и своим эфемерным ключом
  3. 3 Сервер подписывает транскрипт handshake долговременным ключом, доказывая личность
  4. 4 Обе стороны выводят сессионный ключ из двух эфемерных долей
  5. 5 Эфемерные ключи отбрасываются; течёт AEAD-зашифрованный трафик приложения
Вспомните перед уходом
  1. 01
    Объясни forward secrecy: в чём конкретная разница между переносом ключа по RSA и ECDHE, когда атакующий крадёт долговременный приватный ключ сервера, записав месяцы трафика?
  2. 02
    Почему переупорядочивание наборов шифров не останавливает атаку понижения, а редирект 301 не останавливает SSL stripping — и что реально чинит каждое?
Итог

Handshake TLS делает три вещи сразу — аутентифицирует сервер, устанавливает зашифрованную сессию и даёт целостность — а зелёный замок лишь удостоверяет, что все три держались в момент handshake. Он ничего не говорит о трёх режимах отказа, о которых сеньор обязан рассуждать отдельно. Первое — forward secrecy: при переносе ключа по RSA украденный долговременный ключ расшифровывает все ранее записанные сессии («собери сейчас, расшифруй потом»); при ECDHE сессионный секрет берётся из выброшенных эфемерных ключей, так что украденный ключ не тронет прошлое. TLS 1.3 делает это обязательным; в 1.2 нужно ограничить наборы до ECDHE. Второе — понижение: активный человек посередине подменяет ClientHello, чтобы навязать слабую версию или экспортный набор, поэтому порядок шифров сервера неважен — фикс это удаление слабых версий и наборов, а не их переупорядочивание. Третье — срыв: открытый первый участок живёт вне TLS, так что редирект 301 не защитит запрос, который атакующий перехватывает до прибытия — только HSTS (в идеале с preload) переносит повышение в браузер. Плейбук усиления механический: минимум TLS 1.2, только ECDHE-AEAD, HSTS + preload, полная цепочка + OCSP stapling — прими именованный профиль (Mozilla) и проверь его на живом проводе через SSL Labs / testssl.sh, потому что файл конфигурации может лгать. Так что когда кто-то говорит «у нас HTTPS, мы защищены», сеньорские вопросы: каждый ли набор — ECDHE, нет ли в меню слабой версии и защищён ли самый первый участок через HSTS, а не редирект?

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 6 завершено
Связанные уроки

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.