Машинное обучение вместо DPI. Строим классификатор трафика

image

Вряд ли можно представить мир современных сетевых технологий без DPI (deep packet inspection – глубокий анализ пакетов). На нём держатся системы обнаружения сетевых атак, львиная доля политик безопасности корпоративных сетей, шейпинг и блокировка пользовательского трафика оператором связи – да-да, чтобы выполнять требования Роскомнадзора, средства DPI обязан иметь каждый провайдер.

И всё-таки, при всей своей востребованности, DPI имеет некоторые недостатки. Главный из них в том, что средствам DPI необходимо видеть полезную нагрузку анализируемых пакетов. А что делать, когда клиент использует шифрование? Или, например, если у нас нет DPI здесь и сейчас, но в перспективе потребуется проводить какой-то анализ текущего по сети трафика – тогда нам остаётся только сохранять всю полезную нагрузку для последующего анализа, что очень неудобно.

В данной статье я хочу предложить альтернативный способ решения одной из главных задач DPI – определения протокола прикладного уровня – на основе очень маленького количества информации, при этом не сверяясь со списком широко известных портов (well-known ports) и не глядя в полезную нагрузку пакетов. Вообще.

Подход к решению

Во-первых, определимся с объектом классификации — для какой сущности мы будем определять протокол прикладного уровня?

В средствах DPI, как правило, объектом классификации выступает поток трафика транспортного уровня – это совокупность IP-пакетов, у которых совпадает протокол транспортного уровня, а также неупорядоченная пара endpoint-ов: <(ip источника, порт источника), (ip назначения, порт назначения)>. Мы тоже будем работать именно с такими потоками.

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

Прежде, чем определять эти метрики, введём парочку определений:
  • Клиент – инициатор TCP-соединения либо отправитель первой UDP-дейтаграммы потока, в зависимости от протокола транспортного уровня;
  • Сервер – принимающая сторона TCP-соединения либо адресат первой UDP-дейтаграммы потока, в зависимости от протокола транспортного уровня.
  • Порция данных – совокупность полезной нагрузки прикладного уровня, которая передавалась от одной стороны другой (от клиента к серверу или наоборот), и при этом не прерывалась полезной нагрузкой с другой стороны.

Последнее определение требует некоторого пояснения, так что мой внутренний Микеланджело спешит на помощь:

image
В данном примере после TCP-рукопожатия клиент начинает передавать полезную нагрузку – следовательно, начинается порция данных со стороны клиента. Пока сервер не посылает никакой полезной нагрузки в ответ, а всего лишь шлёт ACK, порция данных со стороны клиента продолжается. Когда сервер ощущает необходимость передать какую-то нагрузку прикладного уровня, порция данных клиента заканчивается, и начинается порция данных сервера. Как несложно понять, вся передача полезной нагрузки представляет собой чередование порций данных то с одной, то с другой стороны. При очень интенсивном обмене данных с обеих сторон порции данных могут вырождаться в отдельные IP-пакеты.

Статистические метрики потока

Все наши статистические характеристики потока будут плясать вокруг четырёх рядов чисел:
  • Последовательность размеров сегментов транспортного уровня (TCP или UDP), отправленных со стороны клиента;
  • Последовательность размеров сегментов транспортного уровня, отправленных со стороны сервера;
  • Последовательность размеров порций данных, отправленных со стороны клиента;
  • Последовательность размеров порций данных, отправленных со стороны сервера;

Для представленного на рисунке выше короткого примера это ряды будут иметь следующие значения:
  • Размеры сегментов со стороны клиента: [220, 520]
  • Размеры сегментов со стороны сервера: [720, 420]
  • Размеры порций данных со стороны клиента: [700]
  • Размеры порций данных со стороны сервера: [1100]

Эти 4 ряда чисел (как мы увидим впоследствии) очень хорошо характеризуют поток данных, и на их основе можно достаточно точно угадать протокол прикладного уровня.

Включив фантазию, сформулируем статистические характеристики потока данных, отталкиваясь от этих 4 рядов чисел:
  1. Средний размер пакета со стороны клиента
  2. Стандартное отклонение размера пакета со стороны клиента
  3. Средний размер пакета со стороны сервера
  4. Стандартное отклонение размера пакета со стороны сервера
  5. Средний размер порции данных со стороны клиента
  6. Стандартное отклонение размера порции данных со стороны клиента
  7. Средний размер порции данных со стороны сервера
  8. Стандартное отклонение размера порции данных со стороны сервера
  9. Среднее число пакетов на порцию данных со стороны клиента
  10. Среднее число пакетов на порцию данных со стороны сервера
  11. КПД клиента – количество переданной нагрузки прикладного уровня, делённое на общее количество переданной нагрузки прикладного и транспортного уровня
  12. КПД сервера
  13. Соотношение байт – во сколько раз клиент передал больше байт, чем сервер
  14. Соотношение полезной нагрузки – во сколько раз клиент передал больше байт, чем сервер
  15. Соотношение пакетов – во сколько раз клиент передал больше пакетов, чем сервер
  16. Общее количество переданных байт со стороны клиента
  17. Общее количество переданной нагрузки прикладного уровня со стороны клиента
  18. Общее количество переданных сегментов транспортного уровня со стороны клиента
  19. Общее количество переданных порций данных со стороны клиента
  20. Общее количество переданных байт со стороны сервера
  21. Общее количество переданной нагрузки прикладного уровня со стороны сервера
  22. Общее количество переданных сегментов транспортного уровня со стороны сервера
  23. Общее количество переданных порций данных со стороны сервера
  24. Размер первого сегмента транспортного уровня со стороны клиента
  25. Размер второго сегмента транспортного уровня со стороны клиента
  26. Размер первого сегмента транспортного уровня со стороны сервера
  27. Размер второго сегмента транспортного уровня со стороны сервера
  28. Размер первой порции данных со стороны клиента
  29. Размер второй порции данных со стороны клиента
  30. Размер первой порции данных со стороны сервера
  31. Размер второй порции данных со стороны сервера
  32. Тип протокола транспортного уровня (0 — UDP, 1 — TCP)

Теперь надо пояснить несколько моментов.

Во-первых, признаки вида «общее количество Х» сами по себе нестабильны, потому что их значение зависит от того, как долго мы наблюдаем за потоком. Чем больше наблюдаем, тем, естественно, больше будет передано и байт, и прикладной нагрузки, и сегментов, и порций данных. Чтобы добавить определённости в эти показатели, мы в будущем будем рассматривать не весь поток целиком от начала наблюдения и до конца, а некоторый срез каждого потока по первым N сегментам транспортного уровня. При рассчёте всех перечисленных метрик будем использовать только первые N сегментов.

Во-вторых, может быть непонятно, зачем нам в показателях размер первого и второго сегмента и порции данных с каждой стороны. Как показано в одной из научных работ на эту тему[1] (ссылка в источниках), размеры первых нескольких IP-пакетов могут нести много информации об используемом протоколе. Так что лишними эти показатели не будут.

Теперь определимся, как мы будем пытаться угадать протокол прикладного уровня конкретного потока, имея на руках рассчитанные статистические метрики. Здесь нам поможет машинное обучение. Рассматриваемая задача – это в своей классической формулировке задача классификации объектов на несколько классов.

У каждого объекта целых 32 характеристики, причём релевантность каждой из них имеющейся у объекта метке класса на данном этапе исследования остаётся под вопросом. Поэтому разумно будет выбрать популярный алгоритм машинного обучения «Случайный Лес» («Random Forest»), поскольку он слабо чувствителен к шумам и корреляции признаков (а некоторые наши статистические метрики, скорее всего, сильно коррелируют между собой).

Данный алгоритм работает по принципу «обучение с учителем». Это значит, что нам нужна некоторая выборка объектов, для которой уже известны метки классов. Эту выборку мы разделим в пропорции 1 к 2 на обучающую и проверочную. На обучающей выборке мы проведём обучение модели (в нашем случае обучение заключается в построении набора решающих деревьев), а на проверочной будем оценивать, насколько хорошо модель справляется с поставленной задачей.

Время жатвы

С методологией проведения эксперимента теперь всё понятно, осталось найти сам трафик, характеристики которого будем рассчитывать.

Никакой провайдер, конечно, не пустит нас в свою сеть собирать трафик – кто там знает, какие данные мы будем собирать? Обойдёмся кустарными условиями. В течение нескольких дней я проводил захват трафика своего собственного компьютера, попутно делая обычные дела – сидел в ВК, смотрел видео на Youtube, сделал несколько звонков в Скайпе, скачал парочку торрентов. В результате получилось более 3 Гб захваченного трафика.

Теперь вспомним, что кроме самого трафика нам также нужно для каждого захваченного потока достоверно определить переносимый им протокол прикладного уровня. И вот тут нам помогут средства DPI, конкретно – библиотека nDPI. В комплекте с этой библиотекой поставляется пример анализатора трафика под названием ndpiReader – он нам и пригодится. Приятный бонус заключается в том, что это приложение само за нас разделит все наши сырые дампы трафика на потоки транспортного уровня, так что нам этим даже не придётся заниматься.

Но всё-таки покопаться в недрах pcap-файлов будет нужно. Для этого используем библиотеку dpkt. К огромному сожалению, её не портировали на третью версию Python, так что скрипт обработки pcap-файлов напишем на второй версии языка.

Собирая всё выше изложенные воедино, напишем скрипт, который преобразовывает PCAP-файлы в таблицу признаков и экспортирует её в формат CSV. Ссылка на репозиторий с этим скриптом в конце статьи.

What time is it? Classification time!

Да-да, наконец-то мы добрались до того, ради чего всё затевалось!

Но перед тем, как с корабля на бал запихивать всю таблицу признаков в первый попавшийся Random Forest, рассмотрим данные немного внимательнее. Вот список прикладных протоколов и количество потоков каждого протокола, которое имеется в нашей выборке:

image

Первое, что хочется сделать – слить весь трафик SSL_No_Cert в SSL, потому что это по сути одно и то же.

Второе – выкинуть трафик Apple, NTP, Unencrypted_Jabber и Unknown. У первых трёх протоколов слишком мало потоков, чтобы проводить классификацию, а под Unknown может скрываться всё что угодно.

Теперь разделим наши данные на обучающую и проверочную выборки. Получим такой расклад:

image

В качестве параметров алгоритма Random Forest после нескольких экспериментов были выбраны следующие значения:
  • Число деревьев: 27
  • Критерий: энтропия
  • Максимальная глубина дерева: 9

Вспомним, что некоторые наши статистические характеристики обязывают нас фиксировать объем рассматриваемого трафика для каждого потока. Для начала возьмём срез трафика побольше – 1000 первых сегментов в каждом потоке. Проверим, можно ли вообще предсказать по нашим статистическим метрикам протокол прикладного уровня. Барабанная дробь!

image

Выше представлена таблица точности и полноты предсказаний по каждому классу. Точность для класса K – это доля предсказаний вида «объект X принадлежит классу K», которые оказались верными. Полнота для класса K – количество объектов X, которые распознаны классификатором как принадлежащие классу K, делённое на общее количество принадлежащих классу K объектов. Ф-мера – среднее гармоническое полноты и точности.

Ну и чуть более простая таблица:

image

Здесь в каждой ячейке указано число, которое означает количество случаев, когда поток трафика, принадлежащий указанному в заголовке строки классу, был классифицирован как принадлежащий указанному в заголовке столбца классу. То есть, числа на главной диагонали – правильные классификации, числа вне её – разного рода ошибки.

В целом, кажется, что классификатор справляется со своей работой неплохо. За исключением редких ошибок, все предсказания оказываются верны.

Алгоритм машинного обучения «Случайный Лес» хорош ещё и тем, что он может дать нам информацию о том, какие признаки оказались более полезными, чем другие. Из любопытства взглянем на самые важные признаки:

image

Топ стабильно занимают признаки, завязанные на статистические характеристики клиентской стороны. Отсюда можно сделать вывод, что именно клиенты разных приложений отличаются между собой больше всего, а серверы – меньше.

От сервера затесался только размер второго пакета. Это легко объясняется тем, что по размеру второго пакета можно очень просто выделить SSL из всего остального трафика, ведь во втором пакете сервера передаётся сертификат.

Приятно удивляет то, что тип протокола транспортного уровня входит всего лишь в ТОП-10 самых важных признаков – хотя, казалось бы, очень много информации в этом признаке содержится! А значит, в наших статистических метриках информации намного больше.

Ещё хочется отметить, что при изменении семени генератора псевдослучайных чисел топ самых важных признаков несколько меняется. Общим остаётся о, что завязанные на клиента метрики стабильно вверху, но их порядок меняется. А протокол транспортного уровня прыгает между первым и десятым местом.

Теперь самое интересное. До этого мы брали срез по первым 1000 сегментам каждого потока, и на основе этого среза рассчитывали статистические метрики. Возникает закономерный вопрос: а насколько можно уменьшить этот срез и всё равно получать хорошие показатели классификации?

Ответ обескураживает. Можно опускать количество сегментов в каждом потоке до… трёх. Не включая TCP handshake, разумеется, ведь он никакой информации не несёт.

Ниже представлены значения полноты и точности для каждого класса, таблица реальных и предсказанных классов и топ характеристик при построении и проверке модели всего лишь на первых трёх сегментах транспортного уровня (для TCP это на самом деле 6 сегментов, три первые отбрасываются).

image

image

image

Внимательный читатель может заметить, что суммарное количество записей в выборке стало меньше по сравнению с прошлым случаем. Дело в том, что для расчёта большинства характеристик нужно, чтобы была хотя бы одна порция данных со стороны клиента и хотя бы одна со стороны сервера. Если выясняется, что в первых 3 сегментах передавал только клиент или только сервер, такой поток отбрасывается ещё на этапе составления таблицы признаков.

Результат получился неожиданный тем, что с резким уменьшением количества сегментов в каждом потоке качество классификации не ухудшилось, а даже несколько улучшилось. Связать это можно с тем, что именно первые переданные сегменты несут наибольшую информацию о типе приложения. Поэтому при уменьшении количества сегментов наши статистические метрики начинают нести информацию только о первых сегментах, а не обо всех, и качество классификации несколько увеличивается.

Заключение

В данной статье я наглядно продемонстрировал, как можно производить достаточно качественный анализ трафика с целью определения протокола прикладного уровня без средств DPI. Что характерно, можно достаточно точно определить прикладной протокол уже по первым нескольким переданным сегментам транспортного уровня.

Весь разработанный код выложен в репозитории на github. В readme репозитория дана инструкция, как вы можете повторить полученные в статье результаты. В папке csv находятся таблицы признаков, полученные после обработки собранных PCAP-файлов. А вот сами файлы PCAP вы там не найдёте – прошу прощения, но конфиденциальность моих данных прежде всего.

В качестве наглядной иллюстрации работы классификатора в реальном времени я разработал несложное приложение на PyQt4. И даже записал видео с демонстрацией его работы.

Для чего можно применять изложенный подход? Например, для идентификации используемых протоколов прикладного уровня в тех условиях, когда полезная нагрузка зашифрована (привет, паранойя). Или для превентивного сбора статистики для анализа на случай, если такой анализ потребуется – ведь для определения протокола прикладного уровня достаточно иметь всего 256 байт информации на каждый поток (32 числа с плавающей точкой).

Благодарности

Хочу поблагодарить Даниила Прохорова и Надежду Трофлянину, с которыми мы занимались разработкой этой идеи, собирали трафик и потом выступали на VII Молодёжном Научном Форуме МТУСИ.

Источники

Неправильно было бы не указать научные работы, из которых мы почерпнули вдохновение и несколько хороших идей:
  1. Internet Traffic Classification Demystified: On the Sources of the Discriminative Power
  2. SVM Based Network Traffic Classification Using Correlation Information
  3. Internet Traffic Classification Using Bayesian Analysis Techniques
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 22

    +2
    То есть, таким образом можно идентифицировать, что происходит внутри VPN?
    Что будет, если внутри VPN одновременно активно передают данные несколько приложений?
      0
      Если какое-то одно приложение передаёт заметно больше данных, чем все остальные, его теоретически можно будет выявить даже сквозь VPN. Если высокая активность у нескольких приложений, способ «в лоб» скорее всего не сработает, но всё равно можно попытаться что-то придумать и сделать какие-то выводы. К примеру, трафик HTTP имеет свойство передаваться всплесками, и если провести кластеризацию передаваемых пакетов во времени и скормить классификатору выявленные кластеры, вполне вероятно, что HTTP будет определён. Более равномерный трафик тоже можно как-нибудь вычленить как принадлежащий конкретному приложению — тут широкое поле для исследований.
    • UFO just landed and posted this here
        0
        Спасибо за дельные замечания, исправил введение.
        Можете рассказать поподробнее про блокировки без DPI? Например, сайтов по спискам Роскомнадзора.
        • UFO just landed and posted this here
            +1
            Не смог промолчать! Так сказать на больную мозоль =)

            Нужно:
            Linux + quagga + iproute2

            Реализация:
            Две таблицы маршрутизации, в default один маршрут в интернет, в zapret заносим все ip адреса запрещенных ресурсов, маршрутом в null. Запускаем bgpd на таблице zapret, аннонсируем все на маршрутизатор. Теперь трафик запрещенных ресурсов падает к нам на сервер. На маршрутизаторе, помещаем ip адрес сервера в policy route (d'link аля), чтобы он имел доступ к интернету. На сервере настраиваем squid для http и mitmproxy для https. В сквиде делаем так чтобы source ip сохранялся абоненский (мы у себя забили). Как то так собственно.
          0
          Дело всё в том, что быстро определить протокол прикладного уровня по шаблону, коих могут быть тысячи — задача очень ресурсоёмкая.

          А почему вы считайте Random Forest эффективным? С каждым новым классом сложность будет расти, а точность будет падать. Т.к. используется one vs rest, то считайте что для 100 классов у вас будет 100 отдельных моделей.

          К тому же на реальной задаче 27 деревьев это очень мало, вы точность кросс-валидацией проверяли?
          Ну и тренировать на данных, которые получены только от одного компьютера — неправильно.
          Уверен, что на реальной задаче результаты будут совсем другими.
            0
            Спасибо, про эффективность я сгоряча. Переместил акценты во введении.
            вы точность кросс-валидацией проверяли?
            Конечно, результаты примерно такие же (в посте я привёл лучшие результаты среди подвыборок).
            Ну и тренировать на данных, которые получены только от одного компьютера — неправильно.
            Строго говоря, компьютеров было два и доступ в Интернет они получали по-разному (один даже через 3G + Wi-Fi), но я понимаю, что репрезентативность здесь низкая. Собирал там, где была возможность. Если можете посоветовать, где можно достать дампы трафика побольше и с разных точек, буду благодарен.
              0
              Не совсем понял, как используется forest у автора, но насколько я помню, у алгоритмов на основе деревьев решений вполне себе «естественная» поддержка мультиклассовой классификации, без использования one-vs-all
                0
                Да, естественная. Использовал вполне традиционно — алгоритму для обучения даётся матрица Х «объект-признак» и вектор Y меток класса. В нашем случае объект — поток транспортного уровня, признаки — те самые 32 статистические метрики потока.
              0
              А есть ли ценность только в детекции типа трафика без его содержания? Думал-думал, но не очень понял — для чего это?
                0
                Для получения глобальной статистики канала или для действий по категориям: например, заблокировать торренты, или зажать их по скорости, а веб, наоборот, разжать. Впрочем, если вы скажете, что при нынешних скоростях это не так актуально, я с вами соглашусь.
                • UFO just landed and posted this here
                    0
                    Ну, например операторам большой тройки, резать канал, если идет скачка торентов, о чем зачастую написано в договоре.
                      0
                      Кроме всего выше перечисленного, это ещё и способ работать с шифрованным трафиком. Анализировать контент не получится, но вот понять хотя бы, что это за прикладной протокол — вполне.
                      В перспективе также можно придумать способ решать задачи, не решаемые при помощи DPI. Например, определять вредоносный трафик по таким вот характеристикам, не привязываясь к прикладному протоколу.
                      0
                      Интересная идея, я как раз сейчас плотно занимаюсь DPI, блокировками сайтов и нейронными сетями, но объединить это в один проект не пришло в голову.
                      В свое время, когда внедрял библиотеку nDPI, мне было интересно — как быстро (за сколько пакетов) библиотека определит протокол. Так вот, там тоже количество невелико. Точное (среднее) количество уже не вспомню, но с тех пор у меня стоит ограничение на анализ первых 64 пакетов потока, и это с большим запасом. Если за 64 пакета nDPI не определил протокол, то скорее всего уже и не определит. А чаще всего определение происходит за первые несколько пакетов. И вот тут интересно — а что будет работать быстрее — nDPI с его анализом пакетов, или «обученная машина»? Я думаю, что nDPI все-таки пошустрее. К тому же, для nDPI нужно хранить меньше данных для каждого потока.
                        0
                        Тоже склоняюсь ко мнению, что nDPI будет быстрее.
                        А какие данные надо хранить для nDPI на один поток?
                          0
                          Только самый минимум: src/dst IP, src/dst port, detected_protocol. Зависит, конечно, от конечной цели.
                            0
                            Ну так если мы делаем классификацию трафика «на месте», то точно такие же данные и после применения машинного обучения будут храниться. А вот если здесь и сейчас у нас нет DPI, но есть вероятность, что проанализировать трафик потребуется, то для nDPI придётся хранить по 64 пакета каждого потока. В случае применения машинного обучения, хранить надо только 256 байт на поток.
                              0
                              nDPI, насколько я понял его код (сильно глубоко не лез), анализирует пакеты вне контекста, то есть смотрит каждый пакет независимо от предыдущих. Поэтому ему не нужно хранить предыдущие пакеты — взял текущий, обработал, пошел дальше. при машинном обучении нужно хранить дополнительные данные, чтобы посчитать нужные параметры: средний размер пакета, количество пакетов и т.д. Впрочем, этих данных не много.
                        +1
                        Вы меня извините, но DPI это и классификация потоков даннных и управление ими. Причем без управления, по большей части, нет потребности и в классификации.
                        Опять ж непонятно, в первой части статьи упоминается, что метод рассматривается как средство для классификации шифрованного трафика, но при том трафик рассматрвиается нешифрованный. При том, что шифрованный трафик имеет свое поведение и указанные метрики не применимы к нему.

                        Кстати, такой метод классификации трафика уже применяется в DPI (естественно вкупе с традиционными методами).
                          +2
                          Я и не замахивался на решение всех задач DPI (в начале статьи сказано — «одной из главных задач»).
                          Данная статья — это «proof of concept», что такое вообще возможно — определить прикладной протокол, не видя полезной нагрузки. Трафик поэтому рассматривается нешифрованный. Для работы с шифрованным трафиком метод придётся изменять, не спорю.
                          Кстати, такой метод классификации трафика уже применяется в DPI
                          Конечно, но в открытом доступе информации о таких технологиях я не нашёл. Данная статья — моё собсвенное исследование на тему.

                        Only users with full accounts can post comments. Log in, please.