Почему Wireshark расшифровывает только одну сторону TLS-трафика: решение проблемы

Представьте ситуацию: вы настроили TLS-шифрование между клиентом и сервером, собрали ключи через SSLKEYLOGFILE, захватили трафик через tcpdump… но в Wireshark видите расшифрованными только сообщения от клиента. Серверные ответы остаются непонятными символами. Это частая ошибка при анализе TLS, и её причина – не в кривых сертификатах, а в деталях работы Wireshark и фильтрации пакетов.

Давайте разбираться, как заставить Wireshark показывать оба направления трафика. Кстати, я сам наступил на эти грабли, когда впервые работал с Python-клиентом и самоподписанными сертификатами.

Шаг 1: Настройки TCP в Wireshark, о которых забывают

Wireshark не просто “читает” пакеты – он пытается собрать из них цепочки (сессии). Для TLS это критично, так как ключи привязываются к конкретному соединению. Вот что нужно проверить:

Откройте Edit → Preferences → Protocols → TCP

Убедитесь, что активны:

  • Allow subdissector to reassemble TCP streams
  • Reassemble out-of-order segments (появилось в версии 3.0)

Почему это важно? Если сегменты TCP приходят не по порядку (а в реальных сетях так бывает часто), Wireshark не сможет правильно собрать TLS-записи без второго пункта. Кстати, первый параметр включён по умолчанию, но после обновлений иногда сбрасывается.

Совет: Если вы видите в логе Wireshark сообщения типа «TLS segment not part of a SSL/TLS connection», первым делом проверьте эти галочки.

Шаг 2: Фильтрация пакетов в tcpdump — ловушка динамических портов

Типичная ошибка: запуск tcpdump на сервере с фильтром по конкретному порту. Например:

tcpdump -i eth0 port 8500 -w server.pcap

Казалось бы, логично – сервер слушает 8500. Но клиент подключается с динамического порта (например, 51234), и ответные пакеты сервера будут отправляться именно на него! Фильтр port 8500 захватывает только входящие соединения, но не исходящие.

Решение – использовать фильтр для TLS-трафика независимо от портов. Вот рабочий пример:

tcpdump -i any 'tcp[((tcp[12:1] & 0xf0) >> 2):1] & 0x80 = 0 && (tcp[((tcp[12:1] & 0xf0) >> 2):1] >= 0x14 && tcp[((tcp[12:1] & 0xf0) >> 2):1] 

Разберём магию этого фильтра:

  • tcp[((tcp[12:1] & 0xf0) >> 2):1] – вычисляет смещение для поля «Content Type» в TLS-записи
  • 0x14-0x17 – соответствуют типам Change Cipher Spec, Alert, Handshake, Application Data

Этот фильтр захватывает все TLS-пакеты, независимо от портов. После применения вы увидите в Wireshark оба направления трафика.

Дополнительные нюансы: когда ничего не помогает

Если проблема остаётся, проверьте:

1. Совпадение версий TLS: Сервер и клиент должны использовать одинаковые версии протокола. В Python-коде из примера стоит ssl.PROTOCOL_TLS_SERVER, что позволяет согласовать версию автоматически, но лучше явно указать:

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.maximum_version = ssl.TLSVersion.TLSv1_3

2. Полный:

При использовании LD_PRELOAD некоторые библиотеки (например, OpenSSL) могут кэшировать сессии. Добавьте в код сервера:

context.options |= ssl.OP_NO_TICKET  # Отключает TLS Session Tickets

3. Время жизни ключей:

В файле SSLKEYLOGFILE должны быть записи для обоих направлений. Если вы видите только CLIENT_HANDSHAKE_TRAFFIC_SECRET и SERVER_HANDSHAKE_TRAFFIC_SECRET, но нет TRAFFIC_SECRET_0 – проблема в экспорте ключей. Попробуйте явно указать поддержку KEYLOG в OpenSSL:

export OPENSSL_CONF=/dev/null  # Отключает системной конфиг, если мешает

Когда всё настроено правильно, Wireshark покажет расшифрованные данные в обоих направлениях. Если остались проблемы – напишите в комментах, помогу разобраться!

Добавить комментарий

Все поля обязательны к заполнению. Ваш адрес email не будет виден никому.

Новое
Интересное