Использование Dask, XGBoost и Stacked Ensembling для защиты от вездесущей формы кибератаки

Репозиторий GitHub

Презентация PDF

Модуль № 4 буткемпа Metis по науке о данных и машинному обучению официально завершен! Этот модуль посвящен классификации, подмножеству обучения с учителем. И для нашего финального проекта нас попросили определить проблему, которую можно решить с помощью методов классификации, и оценить жизнеспособность решения этой проблемы с помощью созданных нами моделей классификации.

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

Этот проект был ценным опытом по нескольким причинам. Это заставило меня ознакомиться с новыми инструментами, такими как Dask, RAPIDS/cuML и поддержкой графического процессора XGBoost, если я хотел работать с выбранным набором данных. Кроме того, до этого я не подходил к проблеме моделирования с точки зрения чистой предсказательной силы и нашел процесс настройки и итерации в погоне за лучшей производительностью приятным. Наконец, данные, с которыми я работал, было довольно сложно понять. Я в некоторой степени разбираюсь в сетевой безопасности, но, конечно, не настолько, чтобы понимать журналы необработанного трафика. Работа с таким чужим набором данных и получение информации из него само по себе было интересной задачей. И поскольку я так многому научился во время этой работы, я надеюсь, что другие смогут извлечь уроки из этой статьи. Давайте погрузимся прямо в!

Обоснование

Что такое DDoS-атака?

Атака Распределенный отказ в обслуживании (DDoS) — это распространенная, почти повсеместная форма кибератаки. Его цель — нарушить работу организации, заполнив сеть, подключенную к Интернету службу или техническую инфраструктуру, окружающую цель, нежелательным трафиком. Объем трафика, направленного на цель, может серьезно ограничить доступность или полностью отключить ее.

DDoS-атаки эффективны отчасти потому, что они используют подключенные к Интернету устройства, которые уже были скомпрометированы вредоносным ПО. Злоумышленник использует уже имеющиеся уязвимости в десятках, сотнях, тысячах или даже миллионах устройств для получения удаленного управления. Эти устройства-боты (или зомби) затем организуются в ботнет и направляются злоумышленником к целевой службе. Этот процесс удаленной оркестровки — это то, на что ссылается распределенная часть распределенного отказа в обслуживании. С распространением предметов домашнего обихода, подключенных к Интернету, таких как холодильники и термостаты, у злоумышленников появилось еще больше устройств со слабым шифрованием, которые они могут использовать в своих атаках.

Почему DDoS-атаки — это проблема, которую стоит решить?

DDoS-атаки — это проблема, которой стоит уделить приоритетное внимание по ряду причин. Они являются одними из наиболее распространенных форм кибератак, потому что они относительно недороги в инициировании и обслуживании — по одной оценке, стоимость поддержки DDoS-атаки среднего размера в течение одного месяца составляла 311 долларов в даркнете.

Кроме того, DDoS-атаки используются национальными государствами, хакерскими группами и киберпреступниками по причинам, варьирующимся от политических до идеологических, финансовых и потому что я могу. Атаки были направлены на интернет-гигантов, таких как Google и Amazon, финансовые учреждения, игровые компании, федеральные агентства, прессу и некоммерческие организации.

Что касается ущерба, который могут нанести DDoS-атаки, его трудно оценить количественно. Но организация должна учитывать стоимость различных последствий, таких как следующие:

  • Длительное время простоя службы
  • Ограничения производительности для сотрудников
  • Утечки данных
  • Репутационный ущерб
  • Потеря SEO-рейтинга
  • Атаки с целью выкупа/вымогательства

Опрос «Лаборатории Касперского 2017 года» показал, что стоимость выдерживания DDoS-атаки для малого и среднего бизнеса составила 120 тысяч долларов. Для организаций уровня предприятия эта цифра выросла до 2 миллионов долларов. Кроме того, исследование 2018 года оценило стоимость простоя для крупной организации где-то в диапазоне от 300 до 540 тысяч долларов. А Отчет IBM за 2020 год показал, что средняя глобальная стоимость утечки данных составила 3,86 миллиона долларов США, в то время как в среднем по США она составила сногсшибательные 8,46 миллиона долларов.

Достаточно сказать, что DDoS-атаки представляют собой вездесущую угрозу для самых разных организаций. Они чрезвычайно дорогостоящи и мешают работе, а также могут нанести долгосрочный ущерб. Не только команда ИТ-безопасности организации должна сосредоточиться на этой проблеме на тактическом уровне, но и руководители должны принимать во внимание стратегическую угрозу, которую DDoS-атаки представляют для их организаций, и думать о том, какие инструменты у них есть для защиты от них.

Обзор

Цель проекта

Учитывая проблему, которую мы хотели решить, моя цель в этом проекте заключалась в следующем: создать модель бинарной классификации, которая смягчила бы последствия DDoS-атаки на деятельность организации, правильно идентифицируя законный/безопасный трафик от вредоносного трафика. Если бы трафик был законным, он был бы передан службам организации. Если бы он был вредоносным, его бы заблокировали. Это обеспечит доступность сервиса для реальных пользователей и защитит от вышеупомянутых повреждений, которые наносят DDoS-атаки.

Показатели успеха

С точки зрения бизнеса, наши показатели успеха были двоякими:

  1. Минимизируйте скорость, с которой модель ошибочно идентифицировала законный трафик как вредоносный (в идеале до 0, иначе мы делаем за него работу атак!)
  2. Смягчите последствия DDoS-атаки, правильно идентифицируя вредоносный трафик с вероятностью 90% или выше (мы готовы принять незначительное снижение производительности службы, если они остаются в сети, а законный трафик не блокируется).

С точки зрения статистики, наши показатели успеха были следующими:

  1. Отзыв: как можно ближе к 1,0.
  2. Точность: больше или равна 0,9.
  3. F2: поскольку для нас показатель отзыва важнее, чем точность, мы будем использовать показатель F2 в качестве метрики путеводной звезды, которая информирует о выборе модели.

Данные

Набор данных для этого проекта использовался с разрешения Канадского института кибербезопасности Университета Нью-Брансуика. Набор данных оценки DDoS, предоставленный Институтом, содержит текущие данные о широком спектре типов атак DDoS. Для целей этого проекта мы сосредоточились на одном типе: DrDoS_LDAP. Этот конкретный набор данных содержал свыше 2,1 миллиона наблюдений и более 80 признаков. Он также был сильно несбалансирован в отношении вредоносного трафика — 99,7% наблюдений были помечены как вредоносные, и только 0,03% — как безопасные. Это, очевидно, создало некоторые проблемы в процессе моделирования, но соответствует тому, что мы ожидаем увидеть в реальном сценарии — DDoS-атаки работают, направляя огромные объемы нежелательного трафика на цель, поэтому миллионы вредоносных журналов по сравнению с малое количество доброкачественных имеет смысл для того, что организация может ожидать увидеть во время атаки.

Набор данных был создан CIC на локальном испытательном стенде, что означает, что это смоделированная атака, происходящая в контролируемой среде. Доброкачественный фоновый трафик также был автоматизирован. Извлечение признаков из необработанных файлов .PCAP было выполнено с использованием инструмента, разработанного CIC собственными силами, под названием CICFlowMeter. Выражаем благодарность и признательность студентам и сотрудникам UNB/CIC за то, что сделали этот набор данных общедоступным.

Код, моделирование и результаты

Код

Обработка данных

Для очистки этого набора данных не требовалось много работы. Самым большим препятствием, с которым я столкнулся при работе с этими данными, был их размер!

Моя первая попытка прочитать эти данные в Pandas на моем Macbook привела к сбою, поэтому я переключился на свой ПК. Он имеет 32 ГБ памяти, 16-ядерный процессор Intel 9900K и графический процессор NVIDIA 2080ti. Это синий экран, когда я пытался загрузить этот набор данных.

За этим последовало долгое и поучительное путешествие по кроличьей норе об ограничениях Pandas для одного ядра ЦП и о том, как это решить. Введите Даск. Dask позволяет распараллелить популярные библиотеки Python, такие как NumPy, Pandas и scikit-learn, на нескольких ядрах ЦП. Он разбивает рабочую нагрузку на более мелкие части, передает их разным ядрам ЦП для параллельного выполнения работы, повторно собирает результаты и возвращает их в ожидаемом формате. Вот как я читаю этот набор данных в pandas с помощью Dask:

from dask import dataframe as dd
ddf = dd.read_csv(r"FILEPATH", dtype={'SimillarHTTP': 'object'},blocksize='64MB')
df = ddf.compute()

Просто как тот!

Как только я прочитал фрейм данных, мне пришлось очистить имена столбцов:

df.columns = df.columns.str.replace(' ', '')

Отбросьте все функции, которые не имеют значения в реальном сценарии:

df.drop(columns=['FlowID', 'SourceIP', 'DestinationIP', 'Timestamp', 'SimillarHTTP', 'SourcePort', 'DestinationPort'], inplace=True)

Избавьтесь от любых значений NaN или Infinity:

pd.options.mode.use_inf_as_na = True
df.dropna(inplace=True)

И закодировать метки:

labels = pd.get_dummies(df['Label'])

Моделирование

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

X = df.drop(columns=["Label"], axis=1)
y = labels.BENIGN
X_train_val, X_test, y_train_val, y_test = train_test_split(X, y, test_size = .2, stratify=y, random_state=1)
X_train, X_val, y_train, y_val = train_test_split(X_train_val, y_train_val, test_size=.25, stratify=y_train_val, random_state=1)

И поскольку набор данных был очень большим, я быстро понял, что подгонка моделей требует много времени. К счастью, Даск прикрыл меня и здесь!

import joblib
from dask.distributed import Client

client = Client(processes=False)

Joblib — это конвейерная библиотека для Python, предоставляющая некоторые возможности параллелизма. scikit-learn построен на базе joblib. Аргумент ключевого слова n_jobs, который вы можете передать многим моделям scikit-learn, поддерживается joblib.

Dask прекрасно интегрируется с joblib и может использоваться для предоставления альтернативного бэкенда joblib для моделей scikit-learn:

with joblib.parallel_backend("dask"):
    lr.fit(X_train_scaled, y_train)

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

Я использовал модель логистической регрессии в качестве отправной точки и использовал первоначальные результаты для дальнейших итераций:

y_val_preds = lr2.predict(X_val_scaled)
print("Precision: {}, Recall: {}, F2 Score: {}".format(precision_score(y_val, y_val_preds), recall_score(y_val, y_val_preds), fbeta_score(y_val, y_val_preds, beta=2.0)))
group_names = ['True Neg', 'False Pos', 'False Neg', 'True Pos']
group_counts = ["{0:0.0f}".format(value) for value in lr_confusion.flatten()]
group_percentages = ["{0:0.4%}".format(value) for value in lr_confusion.flatten()/np.sum(lr_confusion)]
labels = [f"{v1}\n{v2}\n{v3}" for v1, v2, v3 in zip(group_names,group_counts,group_percentages)]
labels = np.asarray(labels).reshape(2,2)
sns.heatmap(lr_confusion,cmap='rocket_r', annot=labels, fmt='', square=True, xticklabels=['DrDoS_LDAP', 'Benign'], yticklabels=['DrDoS_LDAP', 'Benign'])
plt.xlabel('Predicted Traffic Type')
plt.ylabel('Actual Traffic Type')
plt.title('Logistic Regression Confusion Matrix');

Базовая логистическая регрессия

  • F2: .921
  • Отзыв: 0,921
  • Точность: 0,918

Неплохо для первого прохождения.

Поскольку я хотел учесть дисбаланс классов, я попытался передать «сбалансированный» в аргументы class_weights в качестве второй итерации с помощью логистической регрессии. Это делает относительные веса классов, представленных в наших метках, обратно пропорциональными количеству наблюдений в этом классе (вредоносный имеет вес 0,03, доброкачественный имеет вес 0,97 в этом примере). Это эффективно наказывает модель за ошибки в классе меньшинства (доброкачественные) гораздо строже, чем за ошибки в классе большинства (злонамеренные). Результаты матрицы путаницы подтверждают это:

Взвешенная логистическая регрессия

F2: .954

Отзыв: 0,990

Точность: 0,831

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

Базовый случайный лес

with joblib.parallel_backend("dask"):
    rf.fit(X_train, y_train)

F2: .990

Отзыв: 0,990

Точность: 0,990

Random Forest отлично справился с этой задачей без какой-либо настройки параметров! Из-за этого я потратил несколько дополнительных итераций на изучение различных методов устранения дисбаланса классов в наших данных и того, как это повлияет на результаты Random Forest. Я пробовал сбалансированное взвешивание классов, балансировку весов классов на уровне начальной загрузки и выборку большинства (вредоносных) классов с помощью пакета Imbalanced-Learn. Эти методы либо вообще не имели большого значения, либо увеличивали скорость, с которой модель правильно идентифицировала доброкачественный трафик, в то же время сильно препятствуя ее способности идентифицировать вредоносный трафик:

Случайный лес с взвешиванием классов Bootstrap

F2 .984

Отзыв: 0,984

Точность: 0,987

Не большая разница с базовым.

Случайный лес с передискретизацией данных (несбалансированное обучение)

F2: .871

Отзыв: 1.0

Точность: 0,574

Идеальная производительность на безвредном трафике, низкая производительность на вредоносном трафике. Стоит сдать.

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

XGBoost также имеет дополнительное преимущество Поддержка графического процессора. Графические процессоры (GPU) в первую очередь предназначены для компьютерных игр, но, поскольку они предназначены для визуализации сложных трехмерных пространств с высокой частотой кадров в режиме реального времени, GPU хорошо подходят для ресурсоемких процессов, таких как подгонка моделей машинного обучения на больших компьютерах. наборы данных. NVIDIA разработала целый набор программных библиотек для выполнения конвейеров обработки данных и аналитики исключительно на графических процессорах под названием RAPIDS. Насколько я могу судить, на данный момент RAPIDS доступен только на машинах с Linux/Ubuntu, но есть несколько способов заставить его работать на Windows. Однако для этого проекта я не пошел в кроличью нору и был счастлив иметь встроенную поддержку графического процессора в XGBoost, что сократило время подбора моей модели примерно с 8 минут для Random Forest с помощью scikit-learn до менее 20 секунд с XGBoost. .

gbm = xgb.XGBClassifier(
                        n_estimators=100000,
                        max_depth=6,
                        objective="binary:logistic",
                        learning_rate=.1,
                        subsample=1,
                        scale_pos_weight=99,
                        min_child_weight=1,
                        colsample_bytree=1,
                        tree_method='gpu_hist',
                        use_label_encoder=False
                        )
eval_set=[(X_train,y_train),(X_val,y_val)] 
fit_model = gbm.fit( 
                    X_train, y_train, 
                    eval_set=eval_set,
                    eval_metric='auc',
                    early_stopping_rounds=20,
                    verbose=True 
                   )

Базовый XGBoost

F2: .986

Отзыв: 0,996

Точность: 0,946

Помимо отличной скорости модели XGBoost, готовые результаты были столь же хороши. Настолько, что последующие итерации с перенастроенными параметрами почти не изменили результаты.

В качестве дополнительного бонуса XGBoost также предлагает нам возможность относительно легко отображать важность функций:

fig,ax2 = plt.subplots(figsize=(8,11))
xgb.plot_importance(gbm, importance_type='gain', ax=ax2)

Если для параметра Important_type установлено значение gain, это выводит важность каждого признака с точки зрения прироста информации, что означает, насколько велика прогностическая сила модели, когда данный признак находится в своем подмножестве. Как видите, функция MinPacketLength имела исключительно большое значение:

Определенно стоит исследовать дальше, но интерпретируемость не была целью этого проекта, поэтому мы остановимся на этом.

Ансамбль

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

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

Существует 3 варианта ансамбля: Жесткое голосование, Мягкое голосование и Сложенное голосование.

Ансамбль жесткого голосования

model_names = ["lr2", "rf", "imb_rf3", "gbm"]

model_vars = [eval(n) for n in model_names]
model_list = list(zip(model_names, model_vars))
voting_classifier = VotingClassifier(estimators=model_list,
                                    voting='hard',
                                    n_jobs=-1)

Жесткое голосование — это когда каждая модель выполняет двоичную классификацию данных, которые она получает, а ансамблевая модель подсчитывает голоса, чтобы вывести окончательный результат. Вот как выступил ансамбль Hard Voting:

F2: .993

Отзыв: 0,993

Точность: 0,993

Ансамбль мягкого голосования

soft_voting_classifier = VotingClassifier(estimators=model_list,
                                    voting='soft',
                                    n_jobs=-1)

Мягкое голосование — это когда каждая модель выводит вероятность классификации вредоносного/доброкачественного. Затем модель ансамбля объединяет эти вероятности и делает свой окончательный прогноз на основе того, какая из них высока. Вот как выступил ансамбль Soft Voting:

F2: .974

Напомним: 1.0

Точность: 0,883

Составной ансамбль

stacked = StackingClassifier(estimators=model_list, n_jobs=-1)

Объединение с накоплением — это когда выходные данные каждой модели используются в качестве признаков в метаклассификаторе. По умолчанию в scikit-learn метаклассификатором является логистическая регрессия, но это может быть любой метод, который вы выберете. Stacked Ensembling использует мудрость всех моделей в своем репертуаре и выводит окончательный прогноз. Лучше всего дать модели классификатора с накоплением, которые «покрывают» слабые стороны друг друга, а не работают друг против друга для достижения наилучших результатов. Стекирование — это очень мощный метод сборки, но он требует больших вычислительных ресурсов. Даже при распараллеливании с помощью Dask этой модели потребовалось около 60 минут, чтобы поместиться на моем рабочем столе с 16-ядерным процессором Intel 9900K с тактовой частотой 5,0 ГГц. Однако результаты того стоили:

F2: 1.0

Отзыв: 1.0

Точность: 1,0

Не может быть лучше, чем это. Победа в Stacked Ensemble Classifier. Давайте посмотрим, как это работает на финальном наборе задержек:

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

Выводы

Резюмируя основные выводы из этого проекта:

  1. Доказано, что классификация является жизнеспособным решением этой проблемы.
  2. И Random Forest, и XGBoost хорошо работают из коробки.
  3. Составной ансамбль с логистической регрессией, случайным лесом и XGBoost в целом работает лучше всего.
  4. Функция MinPacketLength заслуживает дальнейшего анализа и понимания.

Проверка решения

Всегда лучше подумать с практической точки зрения о том, как ваше предлагаемое решение может быть проверено. В данном случае сложно сказать. Наш набор данных был создан в контролируемой среде, поэтому мы ожидаем, что данные реального мира будут немного более хаотичными. Но как мы будем тестировать эту модель на реальных данных, если не запускать ее в производство во время DDoS-атаки?

Во-первых, мы могли бы протестировать его в нормальных условиях и посмотреть, насколько хорошо он работает с точки зрения определения регулярного законного трафика. Мы также можем провести тестирование с помощью синтетических атак, подобно тому, как был создан набор данных. Мы также можем сравнить модель с решениями поставщиков (например, Cloudflare, Akamai) и в качестве последнего шага фактически развернуть модель и оценить ее производительность во время атаки.

Дальнейшая работа

Как и во всех проектах, здесь есть области для улучшения и дальнейших итераций. Во-первых, в этом проекте рассматривался только один тип DDoS-атаки. Существует много разновидностей атак (и у CIC есть наборы данных по многим из них), которые имеют разные характеристики трафика. Было бы целесообразно добавить больше типов вредоносных данных и оценить производительность, что, вероятно, сделало бы модель более устойчивой к реальным сценариям.

Второй областью для улучшения были сами модели. Я хотел попробовать ансамбль с самого начала, поэтому я не тратил столько времени на настройку гиперпараметров, сколько мог бы. Использование GridSearchCV для оптимизации моих моделей, возможно, повысило производительность до такой степени, что сборка стала не нужна. Кроме того, я мог бы попробовать различные методы повторной выборки данных, такие как избыточная выборка с использованием пакета несбалансированного обучения, или попробовать другие методы классификации, такие как Наивный Байес.

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

Заключительные мысли

Надеюсь, вам понравилось узнавать о DDoS-атаках и о том, как можно использовать различные методы классификации и библиотеки Python для решения этой проблемы! Мне очень понравился проект, и я очень доволен результатами. Напишите, если у вас есть какие-либо вопросы или комментарии!