Кратко

  • TLS 1.2 разрешал новое рукопожатие внутри существующего соединения, но не связывал его с конкретным предыдущим рукопожатием.
  • RFC 5746 сохранял значения verify_data последнего рукопожатия и требовал проверить их при повторном согласовании.

Два правильных рукопожатия и один ложный разговор

Атака начиналась с законного TLS-соединения, которое злоумышленник устанавливал с сервером. Он отправлял выбранные им прикладные байты, а затем пропускал через это уже защищённое соединение рукопожатие жертвы. Для жертвы это выглядело как начальное рукопожатие; сервер мог принять его за повторное согласование соединения злоумышленника.

Поздний защищённый трафик жертвы злоумышленник читать не мог. Но уже доставленный префикс никуда не исчезал. Сервер мог увидеть байты злоумышленника, за которыми следовали аутентифицированные данные жертвы, и обработать их как одно прикладное взаимодействие. В HTTPS приложение без чёткой границы состояний могло связать ввод злоумышленника с учётными данными или cookie, отправленными позже.

Это не было взломом шифра или подделкой сертификата. У каждого рукопожатия могли быть корректные сообщения Finished. Не хватало доказательства, что новое рукопожатие продолжает именно прежнюю историю.

Предыдущий Finished становится состоянием соединения

RFC 5746 добавил флаг secure_renegotiation и сохранил для каждого соединения клиентское и серверное verify_data непосредственно предыдущего рукопожатия. Эти значения относились к активному соединению, а не просто к записи возобновляемой сессии.

Расширение renegotiation_info типа 0xff01 переносило историю в следующий обмен. В начальном рукопожатии поле renegotiated_connection было пустым. При повторном согласовании клиент отправлял сохранённое client_verify_data; сервер сравнивал его со своим состоянием и возвращал объединённые клиентское и серверное значения. Клиент проверял результат. Отсутствие обязательного расширения или несовпадение приводило к фатальному прерыванию рукопожатия.

Иными словами, одного успешного завершения было уже недостаточно. Рукопожатие должно было быть правильным преемником именно этого соединения.

Сигнал совместимости, который не был шифром

Некоторые старые реализации могли отвергать неизвестные расширения. Поэтому RFC 5746 определил TLS_EMPTY_RENEGOTIATION_INFO_SCSV со значением 0x00,0xFF в списке наборов шифров. Старые реализации должны были игнорировать неизвестные наборы, и клиент мог объявить поддержку без надёжного обработчика расширений.

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

От исправления к удалению

TLS 1.3 выбрал другую границу: повторное согласование запрещено. Поздний ClientHello в соединении TLS 1.3 должен обрабатываться как неожиданное сообщение. Соединение, установленное по более ранней версии, при получении TLS-1.3-ClientHello во время повторного согласования обязано сохранить прежнюю версию и не может так перейти на TLS 1.3. KeyUpdate и аутентификация после рукопожатия — отдельные механизмы, а не разновидности повторного согласования.

Для TLS 1.2 RFC 9325 требует, чтобы клиент и сервер поддерживали renegotiation_info; если сервер не подтверждает расширение, клиент обязан завершить соединение. RFC 5746 не решил все проблемы, связанные с несколькими рукопожатиями; RFC 9325 отдельно рассматривает расширенный главный секрет и связанный класс тройного рукопожатия. Его исторический результат уже: временная непрерывность стала аутентифицированным свойством протокола, а не предположением приложения.

Источники