Представьте, что вы пытаетесь получить доступ к веб-сайту вашего правительства. Вы просто хотели распечатать какую-то форму или прочитать об изменениях в законодательстве, но сайт не загружается. Не исключено, что серверы подвергаются кибератаке. Как мы можем предотвратить этот сценарий? Разумеется, с помощью методов машинного обучения. Они незаменимы в сфере кибербезопасности, улавливая нюансы данных, незаметные для человека. В этой короткой серии мы поговорим об обнаружении DDoS-атак с помощью новой технологии: квантовых вычислений.
Квантовые вычисления предлагают альтернативные способы вычислений с использованием квантовых явлений. В последнее время вокруг этой технологии много ажиотажа и появилось довольно много алгоритмов квантового машинного обучения (QML). Однако поиск и обучение моделей квантового машинного обучения, демонстрирующих высокий уровень производительности, — непростая задача. Найти модель QML, которая обеспечивает такой уровень производительности на реальном наборе данных, действительно выдающаяся задача. Давайте взглянем на одну из этих моделей и ее реализацию в PennyLane
и Tensorflow
. Мы будем обучать гибридную квантовую нейронную сеть (H-QNN) на наборе данных атак типа DDoS из статьи Квантовое машинное обучение для обнаружения вторжений распределенных атак типа «отказ в обслуживании: сравнительный обзор» [ 1]. Код в этой статье — это немного измененная версия из репозитория, приложенного к статье. В первой части мы сосредоточимся на предварительной обработке данных. Если вы хотите пропустить всю очистку данных, перейдите к следующей части, где мы реализуем и обучаем реальную модель на этих данных.
DDoS-атаки:
Распределенный отказ в обслуживании (DDoS) – это тип кибератаки, когда множество хостов пытаются подключиться к серверу жертвы, пока он не выйдет из строя и не сможет обработать законный запрос. . Это скоординированное действие из одной центральной точки, обычно выполняемое с помощью вредоносного программного обеспечения, которое заражает устройства неосведомленных владельцев:
Распознавание этого типа атаки важно на ранних этапах подключения к серверу. Это не позволяет злоумышленникам забирать ресурсы, перегружать и, наконец, закрывать веб-сайт или приложение. Нам нужны небольшие надежные модели, чтобы быстро классифицировать, например, пользовательский запрос как безопасный или потенциальный DDoS, не замедляя весь процесс.
Дополнительная литература: Что такое распределенная атака типа «отказ в обслуживании?»
Предварительная обработка данных:
Мы используем набор данных оценки DDoS (CIC-DDoS2019), который включает в себя результаты анализа сетевого трафика с помеченными потоками на основе метки времени, исходного и целевого IP-адресов, исходных и целевых портов, протоколов и атак. Все точки данных помечены как безопасные (бит данных представляет любую угрозу) и простой протокол обнаружения служб (тип DDoS-атаки). Чтобы загрузить данные:
- зайдите на веб-сайт набора данных и нажмите скачать
- отправьте свои данные, чтобы получить доступ
- перейдите в каталог
CSVs
, - скачать
CSV-01-122.zip
файл
После этого распакуйте и найдите в каталоге 01-12
DrDoS_SSDP.csv
файл. Это наши данные.
Прежде чем переходить к кодированию, убедитесь, что у вас настроена среда. Вот наш стек:
Начнем с импорта всех необходимых пакетов:
# Data processing:
import numpy as np
import pandas as pd
# Utils:
import sklearn.decomposition
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Visuals:
import seaborn as sns
import matplotlib.pyplot as plt
Далее настроим глобальные переменные:
# Set random seed:
GLOBAL_SEED = 419514
np.random.seed(GLOBAL_SEED)
# Computation data format:
comp_dtype = 'float32'
# Increase pandas printing limits
pd.set_option('display.max_columns', 90)
pd.set_option('display.width', 1000)
pd.set_option('display.max_rows', 90)
Посмотрим размер наших данных:
data_read_path = os.path.join(".", "data", "raw", "CSV-01-12", "01-12", "DrDoS_SSDP.csv") data_df = pd.read_csv(data_read_path) data_df.drop(labels=['Unnamed: 0'], axis=1, inplace=True) data_df.shape
(2611374, 87)
Это довольно обширный набор данных с более чем 2 миллионами точек данных и 86 столбцами, не считая меток набора данных. Для небольшой надежной модели нам нужно будет выбрать только несколько функций в качестве входных данных. Для этого избавимся от:
- Столбцы только с одним значением
- Нерелевантные столбцы — временная метка, порт назначения, идентификатор источника. Мы можем использовать более сложные методы для извлечения полезной информации (например, пометить точки данных по часам из временных меток, чтобы увидеть корреляцию времени суток с атаками), но для простоты мы оставим это. Однако читатель волен экспериментировать. Возможно, это поможет обучить модель.
- Столбцы со значениями NaN
- Коррелированные столбцы — мы избавимся от столбцов с коэффициентом корреляции выше определенного порога.
Давайте получим базовую информацию о каждом столбце. Поскольку результаты довольно обширны; мы напечатаем только несколько.
def first_unique_values(column): unique_values = column.value_counts().index.values if column.isna().sum()>0: unique_values = np.append(np.nan, unique_values) sorted_unique_values = unique_values[:min(5, unique_values.shape[0])] return sorted_unique_values def gen_basic_info_dataframe(df, typical_values = True): ''' Making DataFrame with: -number of unique values -data type -percentage of NaN values -list with max. 5 most common values in the column. If NaN occur in the column it's first element in this list, even if it's not the most common value. ''' basic_info=pd.DataFrame(df.nunique(), columns=['num_unique_values']) basic_info['data_type']= df.dtypes basic_info['NaN_percentage'] = (df.isna().sum())*100/df.shape[0] if typical_values: basic_info['typical_values'] = df.apply(first_unique_values, axis=0) return basic_info
basic_info = gen_basic_info_dataframe(data_df) basic_info[20:25]
Мы можем видеть некоторую функцию NaNs
в функции Flow Bytes/s
, мы исключим этот столбец из нашего обучающего ввода:
data_df.drop(labels=['Flow Bytes/s'], axis=1, inplace=True)
Теперь удалим столбцы с «неактуальной» информацией:
irrelevant_columns = [
'Flow ID',
' Source IP',
' Source Port',
' Destination IP',
' Destination Port',
' Timestamp',
'SimillarHTTP',
]
data_df.drop(labels=irrelevant_columns, axis=1, inplace=True)
После этого удалим столбцы с одним уникальным значением:
# get these columns names as a list: one_value_columns = basic_info[basic_info['num_unique_values']==1].index.tolist() print("Columns to drop:\n", one_value_columns) data_df.drop(labels=one_value_columns, axis=1, inplace=True)
Columns to drop: [' Bwd PSH Flags', ' Fwd URG Flags', ' Bwd URG Flags', 'FIN Flag Count', ' PSH Flag Count', ' ECE Flag Count', 'Fwd Avg Bytes/Bulk', ' Fwd Avg Packets/Bulk', ' Fwd Avg Bulk Rate', ' Bwd Avg Bytes/Bulk', ' Bwd Avg Packets/Bulk', 'Bwd Avg Bulk Rate']
Наконец, мы исключаем коррелированные столбцы. Сгенерируем корреляционную матрицу:
corr_matrix = data_df.corr(method='pearson')
Всю матрицу строить не будем, так как с таким количеством признаков она слишком шумная. Читатель может легко построить его с помощью этого кода:
fig, ax = plt.subplots(figsize=(25, 25))
sns.heatmap(corr_matrix,
square=True,
annot=True,
ax=ax)
plt.show()
Давайте сгенерируем пары для исключения из матрицы. Мы установим порог коэффициента корреляции на 0.94
:
threshold = 0.94 filtered_corr_matrix = corr_matrix[np.abs(corr_matrix)>threshold] unstack_f_cm = filtered_corr_matrix.unstack().dropna().reset_index() # remove self-correlations: corr_pairs = unstack_f_cm[unstack_f_cm['level_0']!=unstack_f_cm['level_1']].reset_index().drop(labels=['index'], axis=1) print(f'{len(corr_pairs)} correlations between columns with coeff. over {threshold}.') # set columns to drop: corr_columns_to_drop = [] for id, row in corr_pairs.iterrows(): if not (row['level_0'] in corr_columns_to_drop) and not (row['level_1'] in corr_columns_to_drop): corr_columns_to_drop.append(row['level_0']) print(f'{len(corr_columns_to_drop)} columns to drop.') data_df.drop(labels=corr_columns_to_drop, axis=1, inplace=True)
124 correlations between columns with coeff. over 0.94. 31 columns to drop.
Мы можем продолжить предварительную обработку данных, например. однократное кодирование столбцов с несколькими уникальными значениями, но давайте будем краткими. Теперь мы можем перейти к подготовке обучающих данных.
Подготовка тренировочных данных:
Во-первых, нам нужно уменьшить количество выборок в нашем наборе данных. Поскольку наша квантовая модель значительно медленнее, чем классическая нейронная сеть, а обучение всех данных заняло целую вечность, мы взяли 10,000
выборки из более чем 2,600,000
. Набор данных сильно несбалансирован (всего ~0.03%
точек данных, помеченных как BENIGN
), мы возьмем все образцы, помеченные как не представляющие угрозы, и случайным образом выберем остальные образцы, чтобы получить общее число 10,000
.
n_samples = 10000
Мы выберем случайным образом n
выборок из более чем 2 600 000 строк:
- все (
763
) образцы помечены как "BENIGN" n-763
образцов с тегом DrDoS_SSDP
# choose indices from both classes: benign_ids = data_df[data_df[' Label']=='BENIGN'].index ddos_ids = data_df[data_df[' Label']=='DrDoS_SSDP'].index ddos_samples_ids = np.random.choice(ddos_ids, (n_samples-763)) benign_samples_ids = np.array(benign_ids) # merge chosen indices: samples_ids = np.concatenate((ddos_samples_ids, benign_samples_ids)) selected_data_df = data_df.iloc[samples_ids] selected_data_df[' Label'].value_counts()
DrDoS_SSDP 9237 BENIGN 763 Name: Label, dtype: int64
Наконец, нам нужно изменить метки со строки на целые числа, где 0
s означает безопасную точку данных, а 1
s означает DDoS-атаку:
y = np.array(selected_data_df[' Label'].astype('category').cat.codes.astype(int))
Извлечение функций PCA:
Затем нам нужно уменьшить наши входные данные для модели. Наш набор данных, даже с ранее удаленными коррелированными столбцами, имеет 35
функций. Чтобы создать надежную квантовую модель, нам нужно выполнить извлечение признаков. С помощью PCA, установленного на 10,000
ранее выбранных образцах, мы можем перейти к 2
характеристикам в диапазонах от -1
до 1
.
n_features = 2 # Standardize all the features: x = StandardScaler().fit_transform(np.array(selected_data_df.drop(columns=[' Label'], inplace=False))) # PCA fitting and transforming the data: pca = sklearn.decomposition.PCA(n_components=n_features) pca.fit(x) x = pca.transform(x) # Normalize the output to the range (-1, +1): minmax_scale = MinMaxScaler((-1, 1)).fit(x) x = minmax_scale.transform(x) x = x.astype(comp_dtype) # Plot results: for k in range(0, 2): x_axis_data = x[np.array(y) == k, 0] y_axis_data = x[np.array(y) == k, 1] label = 'Benign' if k == 0 else 'DDoS' print(f'{label}: {x_axis_data.shape}') plt.scatter(x_axis_data, y_axis_data, label=label) plt.title("DDoS_SSDP Dataset (Dimensionality Reduced With PCA)") plt.legend() plt.show()
Benign: (763,) DDoS: (9237,)
Вот и все! В следующей части мы обучим на этих данных гибридную модель квантовой нейронной сети.
Краткое содержание:
В этой статье мы предварительно обработали табличный набор данных о кибербезопасности. Мы подготовили данные для обучения небольших квантовых моделей с удалением некоторых функций и выполнением анализа основных компонентов (PCA).
Использованная литература:
[1] Квантовое машинное обучение для обнаружения распределенных атак типа «отказ в обслуживании: сравнительный обзор», E. Д. Паярес и Х. К. Мартинес-Сантос, 2021 г.