Представьте ситуацию: вы настроили 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 покажет расшифрованные данные в обоих направлениях. Если остались проблемы – напишите в комментах, помогу разобраться!