Перейти к основному содержимому

Зачем внедрять сетевой разрыв: модель угроз и векторы атак

· 15 мин. чтения
Андрей Ганюшкин
Enterprise Architect | Author & Lead Maintainer of Netgap

Каждый разговор о логическом разрыве упирается в один вопрос: «А зачем? У нас есть firewall, WAF, TLS и патч-менеджмент». Вопрос правильный — разрыв стоит денег, добавляет задержку и усложняет архитектуру.

Короткий ответ

Все традиционные средства — firewall, WAF (Web Application Firewall), IPS (Intrusion Prevention System) — исходят из того, что сквозной путь от недоверенной зоны к доверенной существует, и ставят на этом пути фильтр. Само существование этого пути имеет три конкретных последствия:

  1. Байты атакующего доходят до кода доверенного сегмента. Их разбирают прошивка NIC, драйверы, сетевой стек ядра, TLS-библиотека — и всё это до того, как приложение проверит хоть один токен. Любая ошибка памяти в этой цепочке эксплуатируется удалённо и без аутентификации.
  2. Безопасность равна корректности каждого элемента на пути. Ошибка в фильтре, уязвимость в самом фильтре или одна лишняя строка в правиле — и путь открыт целиком. Это цепочка условий, которая со временем только удлиняется.
  3. Любой скомпрометированный узел на пути уже имеет доступ внутрь. Ему не нужно пробивать периметр — он его часть.

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

Периметр: три источника уязвимостей

Типовая схема публикации внутреннего API:

Здесь два источника уязвимостей, и ни один из них не является ошибкой конфигурации — оба следуют из архитектуры:

  1. Достижимость доверенного кода. Пакет клиента доезжает до сетевого интерфейса внутреннего сервиса. Значит, вся цепочка разбора на этой машине — от прошивки карты до парсера прикладного протокола — становится pre-auth-поверхностью атаки, доступной снаружи.
  2. Слушающий порт как готовая точка входа. Внутренний сервис принимает входящие соединения из DMZ. Скомпрометированному узлу DMZ не нужно искать вход — он открыт и разрешён правилом, которое нельзя убрать, потому что оно нужно приложению.

Разрыв в терминах каталога вариантов убирает оба: пакет извне не доезжает до доверенного сегмента, а входящих соединений в него нет вообще. Посредник остаётся, но через него проходит не трафик, а узкое сообщение фиксированной схемы, и соединение внутрь всегда открывает доверенная сторона.

Настоящая атакуемая поверхность: всё, что разбирает пакет до приложения

Когда мы говорим «входящий запрос», мы представляем обработчик в приложении: он проверит токен, валидирует тело, отклонит лишнее. Но до первой строки прикладного кода пакет проходит длинную цепочку программ, и каждая из них разбирает недоверенные байты до любой аутентификации.

Голое железо — минимальный случай: Прошивка сетевой карты (разбор кадров, offload вроде TSO/LRO и контрольных сумм), драйвер в ядре с DMA и кольцевыми буферами, сетевой стек ОС — ARP, IP с фрагментацией и сборкой, ICMP, конечный автомат TCP с опциями и расширениями, — затем conntrack и netfilter, TLS-библиотека и парсер прикладного протокола: HTTP/1.1, у HTTP/2 — мультиплексор кадров и сжатие заголовков HPACK, у gRPC — ещё и десериализация protobuf до перехватчиков авторизации, у WebSocket — разбор фреймов и маскирования, у HTTP/3 — весь транспорт QUIC, переехавший в user space. Сотни тысяч строк преимущественно C-кода, и все они трогают байты атакующего раньше, чем приложение узнаёт о существовании запроса.

Виртуализация удлиняет цепочку: Пакеты обрабатывают драйвер, стек и виртуальный коммутатор хоста, затем виртуальное устройство гипервизора — virtio-net, vmxnet3 или эмуляция e1000, — и только потом начинается путь по гостевой ОС: снова драйвер, снова стек. Каждый слой — отдельная, независимо написанная реализация разбора одних и тех же байтов.

Худший и при этом самый типовой случай: Docker на виртуалке:

Цепочка разбора пакета в Docker на виртуалке: уязвимости каждого слоя и оценка вероятности того, что в слое есть ещё не найденная

Закрытый код больше не сокращает эту поверхность. Возражение «у нас проприетарный стек, исходников нет, искать там уязвимости слишком дорого» держалось на одной экономике: ручной реверс-инжиниринг бинарника — это месяцы работы редкого специалиста. Именно эту цену и убирают инструменты автоматического и AI-анализа: языковая модель поверх декомпилятора восстанавливает из машинного кода структуры, форматы и логику разбора, а дальше связка «символьное исполнение плюс фаззинг с направляющей моделью» ищет ошибку без единой строки исходников. Закрытость меняет не наличие уязвимости, а только то, кто найдёт её первым и станет ли находка публичной.

Важно: закрытые исходники больше не защитная мера

Уязвимости находят машины, и уже не в лабораторных условиях:

  • ноябрь 2024 — агент Big Sleep (Google Project Zero и DeepMind) самостоятельно нашёл ранее неизвестный stack buffer underflow в SQLite; Google описал это как первый публичный случай, когда ИИ-агент обнаружил ранее неизвестную уязвимость памяти в широко используемом ПО. В июле 2025 тот же агент нашёл в SQLite CVE-2025-6965 — повреждение памяти, о котором, по оценке Google, на тот момент знали только атакующие;
  • октябрь 2024 — OSS-Fuzz с фаззинг-таргетами, сгенерированными языковой моделью, нашёл в OpenSSL CVE-2024-9143: выход за границы буфера в низкоуровневом API эллиптических кривых GF(2^m), пролежавший в открытом и годами аудируемом коде около двух десятилетий;
  • август 2025 — в финале DARPA AI Cyber Challenge автономные системы нашли около трёх четвертей специально внедрённых уязвимостей и вдобавок 18 ранее неизвестных в реальных open-source-проектах, причём сами же сгенерировали к ним патчи;
  • бинарники — не исключение: ещё на DARPA Cyber Grand Challenge в 2016 году машины находили и эксплуатировали уязвимости в бинарниках без исходников в реальном времени, на скорости соревнования. Сегодня к бинарному фаззингу и символьному исполнению добавились языковые модели, работающие с декомпилированным кодом.

Для схемы выше это значит, что закрытые слои — прошивка NIC, management-движок, виртуальное устройство гипервизора — не «безопаснее по умолчанию», а всего лишь хуже освещены: там меньше публичных CVE, а не меньше ошибок. Broadpwn в прошивке Broadcom (CVE-2017-9417) и обход аутентификации в Intel AMT (CVE-2017-5689) добыли ручным реверсом закрытых бинарников ещё до всякого AI — просто это стоило месяцев работы редкого специалиста. Теперь дешевле и дешевеет дальше, а значит, ставка «нас не будут ковырять, там же нет исходников» перестала быть ставкой на что-либо. Не зависит от стоимости анализа только одно: если байты до слоя не доходят, разбирать его нечем и незачем.

Лаг обновлений — вторая половина проблемы. Против 0-day патч-менеджмент не работает по определению, но и после выхода патча гонка не заканчивается:

  • цепочку в Ivanti Connect Secure эксплуатировали с начала декабря 2023-го, патчи начали выходить в конце января 2024-го — окно в недели;
  • патч ядра или гипервизора — это перезагрузка или миграция ВМ, а окно обслуживания в 24/7-инфраструктуре планируют неделями;
  • прошивки сетевых карт и BMC на практике не обновляются годами;
  • ESXiArgs в феврале 2023-го массово шифровал серверы через CVE-2021-21974 — патч к ней вышел двумя годами ранее.

Что здесь меняет разрыв. У всей цепочки — от прошивки NIC до парсера прикладного протокола — одно общее условие эксплуатации: атакующий должен доставить свой пакет к машине. В контуре с разрывом у машин доверенного сегмента такой достижимости нет: маршрута из недоверенной зоны к ним не существует, исходящее соединение к шлюзу открывает сам исполнитель. Цепочка разбора перестаёт быть pre-auth-поверхностью — не потому, что уязвимости кончились, а потому, что байты до них не долетают. Гонка «эксплойт против патча» остаётся только на шлюзе — узле, спроектированном как расходный: минимальная поставка, нет секретов, нет маршрута внутрь.

к сведению

Да, конечно, остется еще "клиентский код исполнителя" и там тоже могут быть уязвимости, об этом будет позже в "Остаточный риск: клиентский код исполнителя".

Модель угроз

Что защищаем. Доверенный сегмент: внутренние API, базы данных, технологические сети АСУ ТП, системы с персональными данными или гостайной — всё, компрометация чего стоит несоизмеримо дороже недоступности.

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

ПротивникВозможностиЦель
Внешний атакующийСканирование, эксплуатация периметра, 0-day в сетевом стеке и гипервизоре, переиспользование одного эксплойта на однотипных узлах путиПервичный доступ и продвижение хоп за хопом
Закрепившийся в DMZПолный контроль над узлом DMZ, произвольный трафик и украденные креды из негоПродвижение внутрь
Внутри доверенного сегментаКонтроль над внутренним узломКанал C2, эксфильтрация
Цепочка поставокЗакладка в периметровом ПО или устройствеСкрытый доступ через «доверенный» компонент

Допущения. Сетевые стеки, гипервизоры и контейнерная сеть содержат неизвестные уязвимости — это статистика, а не пессимизм. Конфигурации содержат ошибки. Сигнатурные средства не детектируют то, чего ещё нет в сигнатурах.

Если этих допущений в вашей модели нет — например, вы готовы принять риск 0-day в сетевом стеке доверенной машины — разрыв вам, скорее всего, не нужен.

Три вектора, которые закрывает только разрыв

1. RCE в сетевом стеке ОС — до всякой аутентификации

Как выполняется. Атакующий отправляет серию специально сформированных пакетов на IP-адрес машины. Пример — CVE-2024-38063 в tcpip.sys: ошибка обработки IPv6-фрагментов срабатывает в коде ядра, разбирающем заголовки. Ни рукопожатия, ни аутентификации, ни даже открытого порта не требуется — обработчик в ядре читает заголовки раньше, чем решается, кому отдать пакет. Того же класса SACK Panic и Bad Neighbor (CVE-2020-16898).

Что даёт. Исполнение кода в контексте ядра, то есть полный контроль над машиной доверенного сегмента, минуя все прикладные проверки. Логи приложения при этом пусты: приложение запроса не видело.

Почему периметр не спасает. Firewall обязан пропускать трафик к опубликованному сервису — пакет доходит до интерфейса, а этого достаточно. WAF разбирает L7 и до L3-эксплойта не доходит. Host-based фильтр в этой цепочке — ещё один потребитель тех же байтов, стоящий над уязвимым кодом.

Как защищает разрыв. Условие эксплуатации — доставить пакет к интерфейсу машины. В контуре с разрывом машины доверенного сегмента нет ни в одной таблице маршрутизации со стороны недоверенной зоны: отдельная адресация, отсутствие маршрута и трансляции, интерфейс не смотрит наружу. Единственный сетевой обмен — исходящая TCP-сессия исполнителя к шлюзу; ответные пакеты принимаются только в рамках уже установленной сессии (conntrack ESTABLISHED), инициированной изнутри. Эксплойт остаётся рабочим, но недоставляемым: у атакующего нет ни одного пути отправить байты в стек ядра доверенной машины.

2. Разведка и lateral movement по разрешённому правилу

Как выполняется. Атакующий на узле DMZ пользуется правилом, которое там есть по определению: DMZ → internal-api:443. Через него он сканирует внутренний диапазон, снимает баннеры и версии, эксплуатирует внутренние сервисы (патчатся реже периметра) или переиспользует украденный у прокси токен и клиентский сертификат mTLS. Для файрвола весь этот трафик легитимен — он соответствует разрешающему правилу.

Что даёт. Точку входа в доверенный сегмент и карту целей внутри него.

Почему периметр не спасает. Правило нужно приложению, поэтому его нельзя убрать — можно только сузить. Микросегментация уменьшает список целей, но разрешённые ей потоки остаются сквозными путями. IDS замечает аномалию постфактум и не всегда.

Как защищает разрыв. Pull-модель: в доверенном сегменте нет ни одного слушающего порта, доступного из недоверенной зоны, а inbound-политика — deny all без исключения «для приложения». Исполнитель сам держит исходящий long-poll к шлюзу и получает задачи в ответах этой сессии. Для узла DMZ это означает: SYN в сторону сегмента не имеет маршрута, сканирование не возвращает ни одного открытого порта, украденный токен бесполезен — им нельзя инициировать соединение туда, куда нет пути. Класс атак «нашёл порт → определил версию → проэксплуатировал» не затрудняется, а исчезает: у него нет первого шага.

3. Цепочка hop-by-hop: одна уязвимость доводит до доверенного сегмента

Как выполняется. У атакующего есть 0-day в компоненте, который стоит на каждой машине контура: сетевой стек ядра, TLS-библиотека, реализация HTTP/2. Это типично — узлы разворачивают из одного образа, с одной сборкой ядра и одним base image. Дальше он идёт по цепочке достижимости, и каждый шаг разрешён потому, что предыдущий узел обязан общаться с последующим:

  1. эксплойт против внешнего балансировщика — получен код на узле, смотрящем в интернет;
  2. с него тот же эксплойт против прокси/WAF в DMZ — узел достижим, балансировщик обязан к нему обращаться;
  3. с прокси тот же эксплойт против внутреннего сервиса — правило DMZ → internal-api:443 существует по построению.

Что даёт. Три хопа, один эксплойт, ни одного украденного пароля — и RCE на машине доверенного сегмента. Ни на одном шаге не понадобилось обходить аутентификацию: уязвимый код лежит ниже неё.

Почему периметр не спасает. Каждый рубеж — это ещё одна машина с тем же уязвимым слоем, то есть ещё один хоп, а не барьер. Разные вендоры помогают только если уязвимость не в общем слое; ядро, libc и TLS общие почти всегда. Правила не мешают: атакующий использует ровно те потоки, которые разрешены для работы приложения.

Как защищает разрыв. Цепочка обрывается на последнем узле недоверенной зоны — на шлюзе. Следующего хопа просто нет: у шлюза нет маршрута в доверенный сегмент, нет его адресов и нет права инициировать соединение; сессию всегда открывает исполнитель изнутри и сам забирает задачи.

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

Остаточный риск: клиентский код исполнителя. Ноля здесь нет. Захватив шлюз, атакующий контролирует ответы, которые получает исполнитель, и может атаковать его клиентскую часть — TLS-клиент, HTTP-клиент, десериализатор JSON или protobuf. Но риск не тот же самый, и разница измеримая:

  • Инициативу теряет атакующий. Он не может обратиться к исполнителю: адреса нет, слушающих портов нет, сканирование ничего не возвращает. Он может только отвечать на запрос, который исполнитель сделал сам, — нет выбора момента, нет параллельных попыток по всем машинам сегмента, нет повторного захода после падения процесса.
  • Поверхность у клиента меньше. Сервер разбирает запрос произвольной формы от произвольного отправителя: слушающий сокет и accept, конкурентные соединения, маршрутизацию, состояние сессий, таблицы HPACK и стримы, апгрейды протокола. Отсюда и класс серверных ошибок — request smuggling, CONTINUATION flood, десинхронизация. Клиент разбирает ответ на собственный запрос: один известный эндпоинт, ожидаемый content-type, лимиты длины и таймаута, схема, по которой ответ можно провалидировать до использования.
  • Серверный код исследуют несравнимо активнее. Его может фаззить любой в интернете, поэтому туда направлены сканеры, bug bounty и массовая эксплуатация, и подавляющая часть pre-auth RCE — про серверные компоненты. Клиентскую часть исполнителя в этой позиции способен атаковать только тот, кто уже захватил шлюз, — а значит, и цена, и заметность атаки другие.

Поэтому pull не убирает риск, а меняет его класс: вместо pre-auth RCE, доступного любому в интернете и продолжаемого хоп за хопом, остаётся post-compromise атака на узкий клиентский парсер, доступная только тому, кто уже потратил 0-day на расходный шлюз. Снижается она обычными средствами: минимальный клиент, жёсткие лимиты на размер и время ответа, строгая валидация ответа по схеме.

От чего разрыв не защищает

  • Атаки через валидный запрос. Сам по себе разрыв контролирует форму обмена, а не смысл содержимого: SQL-инъекция, укладывающаяся в схему моста, будет доставлена и выполнена. Если в точке разрыва вся полезная нагрузка разобрана в структуру и её можно проверить до попадания внутрь, если в этой точке реализован строгий форматно-логический контроль (ФЛК) — allow-list операций и полей, типы, длины, диапазоны, регулярные выражения и перечисления значений, отказ по умолчанию для всего неописанного, — то запросы с инъекциями, обходом путей и неожиданными полями не проходят границу вообще. Также, ФЛК не спасает от нагрузки, валидной по схеме, но опасной по смыслу (например, легитимного по формату идентификатора чужого объекта): это уровень авторизации в приложении.
  • Компрометацию доверенного сегмента изнутри. Инсайдер, флешка, закладка в зависимости — разрыв сузит каналы наружу, но не предотвратит компрометацию.
  • Компрометацию самого исполнителя. Его целостность обеспечивают поставка и контроль среды, а не архитектура разрыва.
  • Уязвимости посредника и средств защиты. Шлюз в недоверенной зоне — такая же программа, разбирающая недоверенный ввод, как firewall или WAF, и у него есть свой интерфейс управления. Разрыв не делает его неуязвимым; он лишь ограничивает, что даёт его захват.
  • Отказ в обслуживании. Шлюз доступен извне и может быть перегружен: разрыв защищает изоляцию, а не доступность.
  • Социальную инженерию и всё, что не проходит через сетевую границу.

Когда разрыв оправдан, а когда избыточен

Признак контураРазрыв оправданРазрыв избыточен
Цена компрометацииКИИ, АСУ ТП, гостайна, финансовое ядро, ПДнобычное веб-приложение, потери восполнимы
Модель угроз0-day, закрепление в DMZмассовые автоматические атаки
Главный рискдостижимость сегмента извнеуязвимости самого приложения
Комплаенстребуется отсутствие прямой связности сегментовдостаточно стандартной сегментации
Характер обменаконечный набор операций, описываемых схемойпроизвольный трафик, WebSocket, стриминг
Требования к задержкедесятки миллисекунд допустимыкритична минимальная задержка
Приоритетизоляция важнее доступностидоступность важнее изоляции

Практический маркер в пользу разрыва — регуляторное требование об отсутствии прямой связности: разрыв закрывает его архитектурно, а не компенсирующими мерами. Против — если основной риск лежит в коде приложения: тогда бюджет разумнее вложить в безопасную разработку, а разрыв только добавит задержку и эксплуатационную сложность (её я измерял в исследовании MVP).

Итог

  1. Атакуемая поверхность опубликованного сервиса — не его код, а девять слоёв разбора недоверенных байтов под ним: прошивка NIC, драйверы, стек ОС, гипервизор, контейнерная сеть, TLS, парсер прикладного протокола (HTTP/1.1, HTTP/2, gRPC, WebSocket, QUIC). Все они pre-auth, во всех регулярно находят RCE, патчи доходят с лагом от недель до лет.
  2. Единственное общее условие эксплуатации всей этой цепочки — достижимость машины по сети. Разрыв убирает именно её, а не фильтрует трафик: маршрута нет, слушающих портов нет, соединение открывает доверенная сторона.
  3. Поэтому разрыв закрывает то, до чего фильтры структурно не дотягиваются: 0-day в сетевом стеке и гипервизоре, разведку, lateral movement из DMZ. Главное следствие — цепочка «хоп за хопом одним эксплойтом» обрывается на шлюзе: дальше нет ни маршрута, ни слушающего порта. Остаточный риск смещается в клиентский код исполнителя — поверхность меньше, инициатива не у атакующего, и добраться до неё можно только после захвата шлюза.
  4. Всё остальное — WAF, патчи, сегментация, безопасная разработка — по-прежнему нужно.

Как этот паттерн реализуется и во что обходится — в каталоге вариантов, посте о направлениях развития и небольшом исследовании MVP.