Граф связей: что это такое и как построить с нуля — понятное руководство

Граф связей: что это такое и как построить с нуля — понятное руководство

Граф связей — штука, которая в какой-то момент спасает почти любой анализ: расследование инцидента, OSINT, поиск мошеннических цепочек, разбор зависимостей в инфраструктуре, даже ревизию бизнес-процессов. Пока у вас десять строк в таблице, всё терпимо. Когда данных становится сотни и тысячи, мозг перестаёт удерживать картину — и вот тут граф начинает реально работать как «второй монитор» для мышления.

Что такое граф связей и чем он отличается от "просто схемы"

Граф связей — это модель данных, где есть сущности (узлы) и связи между ними (рёбра). Важный момент: граф не рисуется ради красоты. Он строится по правилам, которые позволяют однозначно отвечать на вопросы типа «кто с кем связан», «через кого проходит путь», «какие узлы самые "влиятельные"», «где кластер» и так далее.

"Просто схема" обычно статичная: квадратики, стрелочки, поясняющие подписи, но без строгих правил идентификации, без атрибутов, без возможности машинного анализа. Граф же можно:

  • автоматически расширять (подгружать новые данные и связи);
  • считать метрики (центральность, расстояния, компоненты связности);
  • искать паттерны (например, одинаковые структуры взаимодействий);
  • хранить и версионировать как данные, а не как картинку.
Критерий "Схема" Граф связей
Идентификаторы часто условные ("Иван", "сервер 1") строгие ID, стабильные ключи
Атрибуты на словах или на полях часть модели (в узлах и связях)
Аналитика почти всегда вручную алгоритмы, запросы, метрики
Масштабирование быстро превращается в "лапшу" можно фильтровать, агрегировать, кластеризовать

Из чего состоит: объекты, отношения, атрибуты

Чтобы граф был полезен, стоит заранее договориться о трёх вещах: какие у вас объекты, какие у них отношения и какие атрибуты вы храните. Это как минимальная грамматика, без которой любой граф расползается в хаос.

1) Объекты (узлы) — "кто/что" в вашем мире данных. Примеры для кибербезопасности и расследований:

  • аккаунт, пользователь, роль;
  • IP-адрес, домен, хост, контейнер, сервис;
  • файл, хеш, процесс;
  • компания, человек, телефон, email (в OSINT).

2) Отношения (рёбра) — "как связаны". Тут полезно сразу задавать типы связей, иначе потом будет "связан-связан" и непонятно чем:

  • пользователь AUTHENTICATED_TO хост;
  • хост RESOLVES_TO IP;
  • домен HOSTS сервис;
  • аккаунт OWNS email/телефон.

3) Атрибуты — свойства узлов и связей. Важно: атрибуты бывают не только у узлов, но и у рёбер. Например, связь "вход на сервер" может иметь время, метод, источник, успешность.

Практичный лайфхак: фиксируйте обязательный минимум атрибутов с самого начала:

  • для узла: id, type, label (человекочитаемое имя), опционально source и confidence;
  • для связи: type, from, to, опционально timestamp, weight, evidence.

Визуализация графа связей в кибербезопасности: узлы (пользователи, IP-адреса, серверы) соединены направленными линиями взаимодействий. Показана кластеризация данных и центральный узловой элемент атаки.
Пример визуализации инцидента. На графе видно, как разрозненные события собираются в цепочку. В центре — скомпрометированный узел (IP-адрес или сервер), который выступает «хабом», связывая внешние угрозы с внутренними пользователями. Разные цвета обозначают типы сущностей (User, IP, Incident).


Минимальный алгоритм построения (сбор данных → нормализация → визуализация)

Если строить "по-взрослому", можно уйти в бесконечные методологии. Но базовый рабочий конвейер на практике выглядит довольно приземлённо. Важнее всего — не инструменты, а дисциплина шагов.

Шаг 1. Сбор данных

Определите, откуда берутся факты. Это могут быть логи, выгрузки из CRM, результаты сканов, OSINT-заметки, тикеты, отчёты. На этом этапе не пытайтесь сразу сделать красиво — просто собирайте наблюдения.

  • Фиксируйте первоисточник (хотя бы названием файла/системы).
  • Сохраняйте "сырые" значения как есть (даже если там бардак).
  • Отдельно записывайте, что именно считается фактом (например: "email найден в профиле", "IP был источником подключения").

Шаг 2. Нормализация и "склейка" сущностей

Самый важный шаг, где граф либо становится сильным, либо превращается в кашу. Нормализация — это приведение данных к единым правилам: форматы, регистры, канонические значения. А "склейка" (entity resolution) — это когда вы решаете, что "Ivan Petrov", "Иван Петров" и "petrov_i" — один и тот же объект или всё-таки разные.

  • Задайте ключи: что делает объект уникальным (email? UUID? комбинация полей?).
  • Чистите мусор: лишние пробелы, разные написания, странные разделители.
  • Разделяйте "значение" и "метку": ID один, отображаемое имя — другое.
  • Если уверенность не 100%, храните confidence и не склеивайте насильно.

Шаг 3. Построение структуры графа

Теперь вы превращаете факты в узлы и связи. Удобно мыслить так: каждое утверждение превращается в ребро. Например: "аккаунт входил на хост" → связь AUTHENTICATED_TO. "домен резолвится в IP" → связь RESOLVES_TO.

Шаг 4. Визуализация

Визуализация нужна не только для "красоты", а для проверки здравого смысла: нет ли слишком толстых "узлов-монстров", не распался ли граф на отдельные островки, правильно ли направлены стрелки, не появились ли дубли.

  • Начинайте с фильтра: один кейс/период/подграф.
  • Раскрашивайте по типам сущностей (хотя бы логически, даже если инструмент делает это автоматически).
  • Смотрите на степени узлов: кто соединён со всеми подряд — часто это либо "хаб", либо ошибка нормализации.
[Место для скриншота: Граф "каша" vs Граф "кластеры"]

Шаг 5. Хранение и повторяемость

Если граф строится больше одного раза, он уже не "картинка", а процесс. Значит, нужны форматы хранения и правила обновления: что добавляем, что пересчитываем, как обрабатываем конфликтующие факты.

Практика: строим простой граф на Python (NetworkX)

Руками рисовать схемы долго. Настоящая мощь графов раскрывается, когда вы генерируете их скриптами. Вот пример на Python с библиотекой networkx, который связывает пользователя, IP-адрес и домен.


 import networkx as nx
 import matplotlib.pyplot as plt # Для вывода картинки
 
 # 1. Создаем пустой граф
 G = nx.DiGraph() # DiGraph = направленный граф (важно для связей "кто -> куда")
 
 # 2. Добавляем узлы с атрибутами (тип сущности критически важен!)
 G.add_node("ivan_admin", type="user", label="Ivan P.")
 G.add_node("192.168.1.55", type="ip", is_internal=True)
 G.add_node("malicious-site.com", type="domain", threat_level="high")
 
 # 3. Добавляем связи (ребра)
 # Иван зашел на IP
 G.add_edge("ivan_admin", "192.168.1.55", 
            relation="LOGON", 
            timestamp="2023-10-27T10:00:00")
 
 # Этот IP обратился к подозрительному домену
 G.add_edge("192.168.1.55", "malicious-site.com", 
            relation="DNS_REQUEST", 
            count=14)
 
 # 4. Простой анализ: найти кратчайший путь
 path = nx.shortest_path(G, source="ivan_admin", target="malicious-site.com")
 print(f"Цепочка атаки: {path}")
 # Вывод: Цепочка атаки: ['ivan_admin', '192.168.1.55', 'malicious-site.com']
 

Что здесь произошло? Мы не просто нарисовали линию. Мы создали структуру, которую можно запросить: «Покажи всех пользователей, которые находятся в 2 шагах от хакерского домена». В Excel вы бы искали это часами, граф выдает ответ за миллисекунды.

Частые ошибки (дубли сущностей, "грязные" связи, неверная направленность)

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

1) Дубли сущностей

Классика: один и тот же объект размазан на пять узлов ("example.com", "EXAMPLE.COM", "example.com/", " www.example.com" и т.д.).

  • Решение: канонизация (lowercase, trim, правила для доменов/телефонов), единый уникальный ключ.
  • Проверка: топ узлов по "похожести" меток, отчёт по возможным дублям.

2) "Грязные" связи

Сюда попадает всё, что превращает граф в шум: связи без типа, связи "на всякий случай", связи без контекста. Особенно опасно, когда ребро означает разные вещи в разных местах.

  • Решение: фиксированный набор типов связей + понятное описание, что означает каждый тип.
  • Трюк: храните доказательство/контекст в атрибуте evidence (например, "logon event 4624", "WHOIS", "тикет #123").

3) Неверная направленность

Направление ребра — это не декоративная стрелочка. Это смысл. "Пользователь → сервер" и "сервер → пользователь" могут давать разные результаты при поиске путей и запросах.

  • Решение: заранее решить, как читаются связи (обычно "субъект → объект действия").
  • Проверка: возьмите 10 случайных связей и проговорите их вслух. Если звучит криво — значит, модель кривовата.

4) Узлы-универсалы и "суперхабы"

Иногда появляется узел, который связан почти со всем: "Google", "Unknown", "localhost", "N/A", "Russia", "admin". Часть таких хабов может быть реальной, но чаще это следствие грязных данных.

  • Решение: выделять "служебные" значения в отдельный тип или вообще исключать из графа на уровне визуализации.
  • Проверка: сортировка узлов по степени (degree) и ручной аудит топ-20.

Инструменты и форматы

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

Форматы данных: как хранить

  • CSV — простой старт. Обычно делят на два файла: nodes.csv и edges.csv. Плюс: легко редактировать, дружит с Excel. Минус: вложенные атрибуты и сложные структуры неудобны.
  • JSON — гибко для атрибутов и вложенности. Минус: разные инструменты ожидают разные "диалекты" JSON.
  • GraphML — формат под графы, часто хорошо импортируется в визуализаторы. Плюс: структурированность и атрибуты. Минус: многословен, руками править не всегда приятно.

Пример правильной структуры JSON

Многие новички пытаются вкладывать объекты друг в друга. Не делайте так. Плоская структура «список узлов + список ребер» (как в примере ниже) — это золотой стандарт. Её понимают почти все библиотеки визуализации (D3.js, Cytoscape, Sigma.js).


 {
   "nodes": [
     {
       "id": "user_101",
       "type": "employee",
       "label": "Алексей С.",
       "department": "IT"
     },
     {
       "id": "server_db_01",
       "type": "host",
       "label": "Database Server Primary",
       "ip": "10.0.0.5"
     }
   ],
   "edges": [
     {
       "source": "user_101",
       "target": "server_db_01",
       "relation": "ACCESS_GRANTED",
       "weight": 1,
       "metadata": {
         "method": "SSH",
         "last_seen": "2023-12-01"
       }
     }
   ]
 }
 

Визуализация и анализ

  • Gephi — популярный визуализатор для сетевого анализа (кластеры, метрики, фильтры).
  • Graphviz — когда нужен быстрый рендер схемы из описания, полезно для отчётов.
  • Cytoscape — мощная визуализация графов, особенно когда хочется гибко управлять стилями и данными.

Хранение и запросы

  • Neo4j — один из самых известных вариантов для графового хранения и запросов.
  • TigerGraph — когда граф большой и нужны производительные вычисления.
  • JanusGraph — опция для тех, кто строит распределённое графовое хранилище.

Практика хранения "по-человечески"

  • Храните сырые данные отдельно от нормализованного графа (иначе вы потеряете контекст).
  • Держите словарь типов сущностей и связей (мини-справочник проекта).
  • Используйте стабильные ID (не "Иван", а person:sha256(...) или person:12345).
  • Если граф живой — продумайте версионирование (хотя бы датой снапшота) и правила дедупликации.

Если совсем коротко: граф связей — это не картинка, а способ держать реальность в руках, когда её слишком много. Начните с простого: узлы, связи, минимальные атрибуты, чистые ID. Дальше вы удивитесь, как быстро "разрозненные факты" превращаются в понятную карту.

Граф связей кибербезопасность пользователь Python JSON
Alt text
Обращаем внимание, что все материалы в этом блоге представляют личное мнение их авторов. Редакция SecurityLab.ru не несет ответственности за точность, полноту и достоверность опубликованных данных. Вся информация предоставлена «как есть» и может не соответствовать официальной позиции компании.

Узнайте о кибератаках первыми с Гарда TI Feeds

Подписка на актуальные индикаторы для ваших SIEM, SOAR/IRP, WAF, IPS/IDS, NGFW — 30 дней бесплатно!

Оформить подписку

Реклама. 16+ ООО «Гарда Технологии», ИНН 5260443081

Комнатный Блогер

Объясняю новую цифровую реальность