tls · network · pqc · debugging
Белый экран на новых смартфонах: TLS, постквантовые ключи и невидимый сбой
Разбор реального сетевого инцидента: почему новые телефоны видели белый экран, старые устройства работали, а nginx не писал ничего в логи.
Однажды у билетной системы одного публичного объекта начались странные жалобы: на новых iPhone и свежих Android сайт то открывается, то зависает белым экраном. Старые устройства работают. В логах nginx пусто, будто проблемные пользователи вообще не приходили.
Первая версия со стороны провайдера системы была ожидаемая: блокировки, DPI, мобильные операторы, что-то где-то режется. Версия удобная, но проверяемая. Я решил не спорить словами и разобрать ситуацию по пакетам.
Первый факт: сервер ничего не видит
В access-логе и error-логе nginx нет обращений с проблемных телефонов. Это важный сигнал: если бы запрос дошел до веб-сервера и тот его отверг, мы бы увидели хотя бы след ошибки.
Значит, проблема происходит раньше HTTP. Где-то на TCP/TLS-этапе.
tcpdump показывает место обрыва
Снимаю tcpdump на сервере во время реального сбоя. Картина повторяется в каждом соединении:
client -> server: SYN
server -> client: SYN-ACK
client -> server: ACK
client -> server: TLS ClientHello, segment 1, about 1388 bytes
server -> client: ACK
client -> server: ... nothing
TCP-рукопожатие проходит. Первая часть TLS ClientHello доходит. Вторая часть того же ClientHello не приходит вообще.
Без полного ClientHello сервер не может продолжить TLS-рукопожатие, браузер не получает страницу, пользователь видит белый экран.
Почему ClientHello вдруг стал большим
На новых клиентах TLS ClientHello может включать гибридный постквантовый обмен ключами вроде X25519MLKEM768. В отличие от классического X25519, где публичный ключ маленький, ML-KEM добавляет примерно килобайт данных. Первое сообщение клиента становится заметно больше и может перестать помещаться в один сетевой сегмент.
Старые устройства отправляют маленький ClientHello, он проходит одним куском. Новые отправляют большой, он режется на два сегмента. И где-то в пути второй сегмент тихо исчезает.
Может, виноват сервер?
Есть известный класс багов: сервер или промежуточный компонент получает фрагментированный ClientHello, но не умеет корректно дочитать его до конца. Этому посвящен проект tldr.fail: он проверяет, ломается ли сервер на большом и специально разбитом ClientHello.
В нашем случае проверка с чистой сети дала нормальный результат: сервер корректно отвечал ServerHello. Дополнительные проверки с проводного интернета и внешнего VPS тоже проходили стабильно.
Еще важнее: tcpdump снимался на сервере до nginx и до прикладной логики. Второй сегмент не появлялся на сетевой карте. Значит, сервер его не ронял. Он его просто не получал.
Почему это похоже на middlebox
Сбой проявлялся избирательно: в основном на мобильных сетях и новых устройствах. Сервер не знает, что клиент сидит в мобильной сети, если пакет уже пришел. Он видит IP, TCP и TLS. Если бы сервер ломался на большом ClientHello, похожее поведение было бы с разных сетей.
А здесь симптом указывает на путь: между телефоном и сервером есть оборудование, которое плохо переносит большой или фрагментированный ClientHello и молча теряет продолжение.
До CDN:
телефон -> мобильная сеть -> middlebox -> origin
^ второй сегмент ClientHello пропал
После CDN:
телефон -> мобильная сеть -> CDN edge -> origin
^ TLS завершается ближе к клиенту
Почему CDN помогает
Серверного фикса в такой ситуации обычно нет: размер ClientHello задает клиент, а чужое сетевое оборудование мы не контролируем.
Рабочее решение - вынести TLS-терминацию на CDN:
- edge-узлы CDN стоят на хорошо проверенных маршрутах;
- CDN-инфраструктура уже готова к большим TLS ClientHello;
- TLS завершается ближе к пользователю, а опасный участок до origin исчезает;
- крупные CDN давно занимаются постквантовой совместимостью на TLS-уровне.
После перевода сайта за CDN симптом ушел.
Почему логи молчат
Это неприятная часть таких инцидентов: классические web-логи не видят проблему. HTTP-запроса не было, nginx ничего не обработал, application log тоже пустой.
Что помогает мониторить такие случаи:
tcpdumpили eBPF на сетевом уровне;- синтетические проверки из реальных мобильных сетей;
- Prometheus
blackbox_exporterс разных точек; - Network Error Logging, если вы готовы принимать браузерные отчеты;
- внешние чекеры вроде Cloudflare Radar Post-Quantum checker.
Мораль
- Версия “это провайдер блокирует” может быть верной, но ее надо доказывать пакетами.
- Если в nginx нет логов, это не значит, что пользователь ошибается. Возможно, HTTP просто не начался.
- Новая криптография может ломаться не в математике, а в старом сетевом железе.
- Если проблема в чужом пути, иногда правильный ремонт - не чинить origin, а перенести точку входа.
Ссылки
- tldr.fail - тест и описание класса багов с большим ClientHello.
- Cloudflare: The state of the post-quantum Internet.
- Cloudflare Radar: Post-Quantum checker.
- Google Security Blog: A new path for Kyber on the web.
- NIST FIPS 203: ML-KEM.
- Network Error Logging, MDN.