Detección de dominios DGA o tarea de prueba para el puesto de ingeniero ML interno / Sudo Null IT News

En este artículo veremos una tarea simple que una empresa utiliza como tarea de prueba para los aprendices para el puesto de ingeniero de ML. Se trata de detectar dominios DGA, tarea que se resuelve utilizando herramientas básicas de aprendizaje automático. Le mostraremos cómo solucionarlo utilizando los métodos más sencillos. El conocimiento de algoritmos complejos es importante, pero es más importante comprender los conceptos básicos y poder aplicarlos en la práctica para demostrar con éxito sus habilidades.

DGA (Algoritmo de generación de dominio) es un algoritmo que genera automáticamente nombres de dominio, a menudo utilizado por los atacantes para evitar el bloqueo y comunicarse con los servidores de comando y control.

Las especificaciones técnicas incluidas. datos de pruebapara lo cual fue necesario generar predicciones, y datos de validacióndonde fue necesario demostrar métricas en el formato:

True Positive (TP): False Positive (FP): False Negative (FN): True Negative (TN): Accuracy: Precision: Recall: F1 Score:

A veces las empresas no facilitan datos de formación y quieren evaluar tu capacidad para encontrar soluciones por tu cuenta. Esto incluye:

  1. Entendiendo el problema: Formulación clara de la tarea.

  2. Metodología: Desarrollo de un plan de acción y selección de métodos.

  3. Pensamiento crítico: Análisis de datos e hipótesis.

  4. Habilidades practicas: Aplicar conceptos básicos de aprendizaje automático.

Es importante demostrar iniciativa y capacidad para trabajar con información limitada. En nuestro caso, los dominios de las empresas existentes se pueden encontrar en kaggley necesitaremos generar dominios inexistentes nosotros mismos.

Los datos diversos y de alta calidad permiten a los algoritmos identificar patrones, hacer predicciones y tomar decisiones informadas. Por tanto, sin buenos datos, es imposible lograr resultados exitosos en el aprendizaje automático. Es importante crear datos de alta calidad para entrenar el modelo y garantizar su eficacia y precisión. Necesitamos centrarnos en crear datos como este:

  1. Escribamos funciones para generar cadenas aleatorias y nombres de dominio. Función generate_random_string genera una cadena de una longitud determinada con letras y, opcionalmente, números. Función generate_domain_names crea una lista de nombres de dominio con diferentes patrones.

    def generate_random_string(length, use_digits=True):
      """
      Генерирует случайную строку заданной длины, включающую буквы и опционально цифры.
    
      :param length: Длина строки
      :param use_digits: Включать ли цифры в строку
      :return: Случайная строка
      """
      characters = string.ascii_lowercase
      if use_digits:
          characters += string.digits
      return ''.join(random.choice(characters) for _ in range(length)) 
    
    def generate_domain_names(count):
        """
        Генерирует список доменных имён с различными паттернами и TLD.
    
        :param count: Количество доменных имён для генерации
        :return: Список сгенерированных доменных имён
        """
        tlds = ('.com', '.ru', '.net', '.org', '.de', '.edu', '.gov', '.io', '.shop', '.co', '.nl', '.fr', '.space', '.online', '.top', '.info')
    
        def generate_domain_name():
            tld = random.choice(tlds)
            patterns = (
                lambda: generate_random_string(random.randint(5, 10), use_digits=False) + '-' + generate_random_string(random.randint(5, 10), use_digits=False),
                lambda: generate_random_string(random.randint(8, 12), use_digits=False),
                lambda: generate_random_string(random.randint(5, 7), use_digits=False) + '-' + generate_random_string(random.randint(2, 4), use_digits=True),
                lambda: generate_random_string(random.randint(4, 6), use_digits=False) + generate_random_string(random.randint(3, 5), use_digits=False),
                lambda: generate_random_string(random.randint(3, 5), use_digits=False) + '-' + generate_random_string(random.randint(3, 5), use_digits=False),
            )
            domain_pattern = random.choice(patterns)
            return domain_pattern() + tld
    
        domain_list = (generate_domain_name() for _ in range(count))
        return domain_list
  2. El código carga tres archivos CSV, procesa los datos eliminando la columna '1' y agregando 'is_dga' con el valor 0. Genera 1 millón de nombres de dominio DGA, los fusiona con part_df y baraja el DataFrame resultante.

    try:
      logging.info('Загрузка данных')
      part_df = pd.read_csv('top-1m.csv')
      df_val = pd.read_csv('val_df.csv')
      df_test = pd.read_csv('test_df.csv')
      logging.info('Данные успешно загружены.')
    except Exception as e:
      logging.error(f'Ошибка при загрузке данных: {e}')
    
    logging.info('Обработка данных')
    part_df = part_df.drop('1', axis=1)
    part_df.rename(columns={'google.com': 'domain'}, inplace=True)
    part_df('is_dga') = 0
    list_dga = df_val(df_val.is_dga == 1).domain.tolist()
    generated_domains = generate_domain_names(1000000)
    part_df_dga = pd.DataFrame({
        'domain': generated_domains,
        'is_dga': (1) * len(generated_domains)
    })
    df = pd.concat((part_df, part_df_dga), ignore_index=True)
    df = df.sample(frac=1).reset_index(drop=True)
  3. Excluimos dominios de los conjuntos de validación y prueba, luego equilibramos las clases seleccionando 500.000 ejemplos para cada una de ellas. El conjunto equilibrado resultante se baraja y los índices se reinician.

    # Исключение доменов из валидационного и тестового наборов
    train_set = set(df.domain.tolist())
    val_set = set(df_val.domain.tolist())
    test_set = set(df_test.domain.tolist())
    intersection_val = train_set.intersection(val_set)
    intersection_test = train_set.intersection(test_set)
    if intersection_val or intersection_test:
      df = df(~df('domain').isin(intersection_val | intersection_test))
    
    
    # Балансировка классов до одинакового числа примеров
    logging.info('Балансировка классов')
    df_train_0 = df(df('is_dga') == 0)
    df_train_1 = df(df('is_dga') == 1)
    num_samples_per_class = 500000
    df_train_0_sampled = df_train_0.sample(n=num_samples_per_class, random_state=42)
    df_train_1_sampled = df_train_1.sample(n=num_samples_per_class, random_state=42)
    df_balanced = pd.concat((df_train_0_sampled, df_train_1_sampled))
    df_train = df_balanced.sample(frac=1, random_state=42).reset_index(drop=True)
    
  4. Creamos y entrenamos el modelo usando una canalización que incluye vectorización usando TfidfVectorizer y regresión logística. Después del entrenamiento, el modelo se guarda en un archivo. model_pipeline.pkl

    logging.info('Создание и обучение модели')
    
    model_pipeline = Pipeline((
        ("vectorizer", TfidfVectorizer(tokenizer=n_grams, token_pattern=None)),
        ("model", LogisticRegression(solver="saga", n_jobs=-1, random_state=12345))
    ))
    
    model_pipeline.fit(df_train('domain'), df_train('is_dga'))
    logging.info('Сохранение модели')
    joblib_file = "model_pipeline.pkl"
    joblib.dump(model_pipeline, joblib_file)
    logging.info(f'Модель сохранена в {joblib_file}')

Toda nuestra tarea se reduce al hecho de que necesitamos dividir los dominios en N-gramas y vectorizarlos usando TF-IDF. Un N-grama es una secuencia de N elementos (palabras o caracteres) en un texto, pero en nuestra tarea los aplicamos a una sola palabra para extraer y analizar sílabas de dominio. TF-IDF (Término Frecuencia-Frecuencia de documento inversa) es un método que ayuda a evaluar la importancia de una palabra en un documento en comparación con otros documentos de una colección.

Por lo tanto, al combinar N-gramas y TF-IDF, podemos analizar dominios de manera eficiente e identificar sus características clave. Veamos el ejemplo de dominios existentes: texosmotr-auto.ru y pokerdomru.rudividámoslos en 4 gramos, sin tener en cuenta el dominio genérico (.ru)

  • Para texosmotr-auto.ru: “texo”, “exos”, “xosm”, “osmo”, “smot”, “motr”, “otr-“, “r-au”, “-aut”, “auto”

  • Para pokerdomru.ru: “empujar”, “oker”, “kerd”, “erdo”, “domr”, “omru”

Hemos analizado 4 gramos, pero ¿es necesario utilizar N-gramas fijos para todos los dominios? Por supuesto que no. Para cada dominio, se crean gramos 3D, 4D y 5D para revelar diferentes patrones de lenguaje y características estructurales. Este enfoque permite una mejor captura del contexto y aumenta la capacidad de descubrir características únicas que pueden ser útiles para la clasificación.

  • código para crear gramos 3D, 4D y 5D para un dominio

    def n_grams(domain):
      """
      Генерирует n-граммы для доменного имени.
    
      :param domain: Доменное имя
      :return: Список n-грамм
      """
      grams_list = ()
      # Размеры n-грамм
      n = (3, 4, 5)
      domain = domain.split('.')(0)
      for count_n in n:
          for i in range(len(domain)):
              if len(domain(i: count_n + i)) == count_n:
                  grams_list.append(domain(i: count_n + i))
      return grams_list

Todos los N-gramas resultantes deben ser vectorizados, y el método TF-IDF mencionado anteriormente nos ayudará con esto. Este enfoque evalúa la importancia de cada N-grama en el contexto de dominios convirtiendo datos textuales en forma numérica. La vectorización utilizando TF-IDF tiene en cuenta la frecuencia de aparición de N-gramas en cada dominio y su rareza en el conjunto general.

El último paso es entrenar nuestro modelo. Puede utilizar varios algoritmos que mejoran su métrica; sin embargo, elegí la regresión logística (LR) clásica porque es fácil de implementar, fácil de interpretar y, a menudo, produce buenos resultados; por ejemplo, obtuve las siguientes métricas en el conjunto de datos de validación:

True Positive (TP): 4605 False Positive (FP): 479 False Negative (FN): 413 True Negative (TN): 4503 Accuracy: 0.9108 Precision: 0.9058 Recall: 0.9177 F1 Score: 0.9117

Por lo tanto, comprender conceptos básicos como N-grams y TF-IDF abrirá sus oportunidades de solicitud y le permitirá presentarse con confianza en las pasantías. Estas habilidades proporcionarán una base sólida para su crecimiento profesional en aprendizaje automático y análisis de datos.

PD: se encuentra el código enviado para revisión a la empresa que proporcionó esta tarea de prueba Aquí.

Publicaciones Similares

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *