Привет Хабр! Меня зовут Татьяна Ошуркова, я главный аналитик департамента ИТ корпоративного, инвестиционного и депозитарного бизнеса Росбанка и автор телеграм-канала IT Talks.
Совсем недавно я провела несколько вебинаров на тему использования машинного обучения в сфере информационной безопасности и теперь хочу поделиться с вами данной тематикой в нескольких статьях. В первой части будут рассмотрены данные, которые будут использоваться, их анализ и предварительная подготовка. Во второй части будет описано обучения моделей, а также анализ их работы и выводы, полученные в результате.
Важно отметить, что пример, разобранный в практической части данной статьи, носит обучающий характер и предназначен для демонстрации принципов работы. Применение данного примера в реальных проектах требует дополнительных настроек и адаптации к конкретным условиям.
Одним из важных компонентов системы информационной безопасности являются системы обнаружения вторжений. Давайте для начала немного погрузимся в теорию о поговорим о том, что это такое.
Система обнаружения вторжений (сокр. СОВ, англ. Intrusion Detection System (IDS)) — программный продукт или устройство, предназначенные для выявления несанкционированной и вредоносной активности в компьютерной сети или на отдельном хосте. Существуют различные виды данных:
Сетевая СОВ (Network-based IDS, NIDS) – анализ сетевого трафика.
Основанная на протоколе СОВ (Protocol-based IDS, PIDS) – система проверки данных, передаваемых по протоколу HTTP/HTTPS.
Основанная на прикладных протоколах СОВ (Application Protocol-based IDS, APIDS) – система обнаружения вторжений, контролирующая пакеты, передаваемые по определенному протоколу прикладного уровня.
Узловая СОВ (Host-based IDS, HIDS) – контроль работы отдельных устройств.
Гибридная СОВ (Hybrid IDS) – сочетание двух и более вышеперечисленных типов.
В зависимости от вида системы, она имеет определенную архитектуру, логику работы и выполняет свои задачи. Из всех вышеперечисленных видов наиболее распространенными являются две: сетевая и узловая СОВ. Также нередко встречаются гибридные системы, которые представляют из себя сочетание двух и более вышеперечисленных типов.
Кроме разделения по видам, СОВ разделяются по принципу работы. Выделяют два принципа, на которых может базироваться логика их работы:
Обнаружение вторжений на основе аномалий – в данном случае СОВ сравнивает активность в сети или на хосте с моделью корректного, доверенного поведения контролируемых элементов и фиксирует отклонения от нее. Этот метод позволяет выявлять новые угрозы.
Обнаружение вторжений на основе сигнатур – метод, при котором СОВ сравнивает проверяемые данные с известными образцами сигнатур атаки и создает оповещение безопасности в случае их совпадения. Так можно выявлять вторжения, которые основаны на ранее известных способах проникновения.
Перейдем к машинному обучению. Для начала поговорим о том, какие виды обучения могут преимущественно использоваться в зависимости от того, по какому принципу работает СОВ:
Обнаружение вторжений на основе аномалий – используется контролируемое обучение.
Обнаружение вторжений на основе сигнатур – используется неконтролируемое обучение.
Также могут использоваться: ансамблевое обучение, трансферное обучение (TL) и оптимизацию гиперпараметров.
Далее давайте перейдем к практике и попробуем создать систему обнаружения вторжений с использованием машинного обучения. Мы обучим ряд моделей и посмотрим на результаты их работы. После анализа эффективности моделей мы сделаем выводы, которые позволят нам судить о возможности использования различных моделей и в целом СОВ с применением машинного обучения на практике.
Первым шагом определим алгоритм работы будущей системы, начиная первых шагов сбора данных и заканчивая предсказаниями их оценками. Алгоритм работы систем обнаружения вторжений с использованием машинного обучения будет включать следующие шаги:
Сбор данных: на этом этапе собираются данные о сетевом трафике.
Предварительная обработка данных: данные подвергаются предварительной обработке для подготовки их к анализу машинного обучения.
Обучение модели: модель обучается на нормальной сетевой активности, чтобы научиться распознавать обычное поведение сети.
Обнаружение аномалий: после обучения модели она применяется к новым данным для обнаружения аномального или вредоносного поведения в сети.
Оценка результатов.
Сбор данных
Перейдем к первому шагу и поговорим об исходных данных. Мы будем использовать датасет из открытого доступа «Network Intrusion Detection». «Network Intrusion Detection» – набор данных, содержащий информацию о сетевом трафике, используемую для обнаружения вторжений. Он часто используется для задач классификации, где необходимо определить, является ли сетевая активность нормальной или представляет потенциальную угрозу безопасности (аномалия или вторжение). Включает в себя множество данных, например:
Протоколы (protocol_type): TCP, UDP, ICMP.
Сервисы (service): ftp_data, http, smtp, domain и т.д.
Флаги соединения (flag): SF, S0, REJ, RSTO, SH.
Для того, чтобы перейти ко второму шагу, построим две круговые диаграммы (рисунок 1). На первой диаграмме изображено процентное соотношение видов протоколов в исходных данных, а на второй процентное соотношение видов сетевых соединение, которые относятся к атакам и нормальным соединениям.
Данные графики можно использовать для преданализа данных. По первому график можно сделать выводы о типах соединения или любых других необходимых характеристиках данных, а вторая диаграмма позволяет понять, на сколько равномерно распределено количество различных видов соединений, что важно для обучения моделей и имеет значительную роль в последующей работе с данными. Ниже представлен фрагмент кода для данного этапа.
def pie_plot(df, cols_list, rows, cols):
fig, axes = plt.subplots(rows, cols, figsize=(10, 5))
for ax, col in zip(axes.ravel(), cols_list):
counts = df[col].value_counts()
ax.pie(counts, labels=counts.index, autopct='%1.0f%%', startangle=90, textprops={'fontsize': 15})
ax.set_title(col, fontsize=15)
ax.axis('equal')
plt.tight_layout()
plt.show()
pie_plot(data, ['protocol_type', 'class'], 1, 2)
Предварительная подготовка данных
Далее перейдем ко второму шагу – предварительной подготовке данных. Данный шаг будет разделен на три этапа: выделение категориальных признаков, далее масштабирование и категориальное кодирование признаков.
Для масштабирования будем использовать класс RobustScaler из модуля preprocessing библиотеки Scikit-learn для того, чтобы уменьшить выбросы и сделать данные более равномерными. Данный класс используется для стандартизации признаков путем удаления медианы и масштабирования данных в соответствии с межквартильным размахом. Ниже представлен фрагмент данных до и после масштабирования. На примере столбца scr_bytes видно, что значения признаки изменились и, говоря простым языком, стали «ближе» к друг другу.
Original values:
duration src_bytes dst_bytes
0 0 491 0
1 0 146 0
2 0 0 0
3 0 232 8153
4 0 199 420
Scaled values:
duration src_bytes dst_bytes
0 0.0 1.602151 0.000000
1 0.0 0.365591 0.000000
2 0.0 -0.157706 0.000000
3 0.0 0.673835 15.375766
4 0.0 0.555556 0.792079
Стоит сказать, что также необходимо подготовить значения для целевой переменной. Для этого мы выполним замену данных в столбце class следующим образом: все значения типа normal, что значит соединение было нормальным, и атаки не было, будут содержать цифру 0, иначе 1.
Далее перейдем к кодированию категориальных признаков. Для начала необходимы выделить эти признаки из всего датасета. Категориальными будут являться признаки, значения которых представляют собой категории или группы, а не числовые значения. Они характеризуются конечным и, как правило, небольшим числом возможных значений. Для кодирования применим функцию get_dummies из библиотеки pandas, которая используется для преобразования категориальных переменных в набор бинарных (dummy) переменных. Это важно для подготовки данных к использованию в моделях машинного обучения, так как многие алгоритмы требуют числовых входных данных. В результате категориальные признаки будут разделены на несколько отдельных столбцов, каждый из которых будет соответствовать определенной категории. Ниже представлен фрагмент данных до и после кодирования категориальных признаков. На примере признака protocol_type видно, что было создано три столбца с соответствующими типами протокола и бинарными значениями.
Before encoding:
0 tcp
1 udp
2 tcp
3 tcp
4 tcp
...
25187 tcp
25188 tcp
25189 tcp
25190 tcp
25191 tcp
Name: protocol_type, Length: 25192, dtype: object
Columns after encoding:
protocol_type_icmp protocol_type_tcp protocol_type_udp
0 False True False
1 False False True
2 False True False
3 False True False
4 False True False
... ... ... ...
25187 False True False
25188 False True False
25189 False True False
25190 False True False
25191 False True False
Ниже представлен фрагмент кода, где содержится предварительная подготовка данных.
def do_scl(df_num, cols):
print("Original values:\n", df_num)
scaler = RobustScaler()
scaler_temp = scaler.fit_transform(df_num)
std_df = pd.DataFrame(scaler_temp, columns =cols)
print("\nScaled values:\n", std_df)
return std_df
cat_cols = ['protocol_type','service','flag', 'class']
def process(dataframe):
df_num = dataframe.drop(cat_cols, axis=1)
num_cols = df_num.columns
scaled_df = do_scl(df_num, num_cols)
dataframe.drop(labels=num_cols, axis="columns", inplace=True)
dataframe[num_cols] = scaled_df[num_cols]
dataframe.loc[dataframe['class'] == "normal", "class"] = 0
dataframe.loc[dataframe['class'] != 0, "class"] = 1
print("Before encoding:")
print(dataframe['protocol_type'])
dataframe = pd.get_dummies(dataframe, columns = ['protocol_type', 'service', 'flag'])
print("\nColumns after encoding:")
print(dataframe.filter(regex='^protocol_type_'))
return dataframe
scaled_train = process(data)
Далее на этапе предварительной подготовки данных поговорим о целевой переменной и выборке значений. Первым шагом в данном фрагменте создаем массив со значениями целевой переменной.
Целевая переменная – это признак class, в котором содержится тип соединения: нормальный или атака. Далее преобразуем типы данных целевой переменной в целые числа для корректной работы моделей.
Следующим шагом разделяем данные на обучающие и тестовые наборы для классификации. Для этого используем функцию train_test_split из библиотеки Scikit-learn. Данная функция используется для разделения массивов или матриц данных на случайные тренировочные и тестовые подмножества. В параметр test_size передается значение 0.2, размер тестовой выборки будет 20 процентов, а обучающей 80 процентов.
Ниже представлен фрагмент кода, где реализованы шаги из описания выше.
y = scaled_train['class'].values
y = y.astype('int')
x_train, x_test, y_train, y_test = \
train_test_split(x, y, test_size=0.2, random_state=42)
x_train_reduced, x_test_reduced, y_train_reduced, y_test_reduced = \
train_test_split(x_reduced, y, test_size=0.2, random_state=42)
После того, как закончен предварительный анализ и подготовка данных, мы переходим к обучению моделей и далее к их оценке. Продолжение будет во второй части, где мы подробно рассмотрим различные модели, их характеристики, разберем процесс обучения, оценки и сделаем выводы о разработке системы обнаружения вторжений с использованием машинного обучения.