Skip to main content
Сервер MCP — это третья сторона, к которой получает доступ ассистент. В одном предложении это и есть вся проблема безопасности, и каждое решение на этой странице следует из неё. Omniflow находится по обе стороны протокола. Он клиент серверов, которые вы регистрируете, и он сервер, к которому может подключиться ваш клиент. Модель угроз между этими ролями перевёрнута, и выводы не симметричны.
Каждое соединение выключено, пока владелец его не включит, дотягивается только до тех хостов и инструментов, которые владелец назвал, и попадает в журнал аудита независимо от того, удалось оно или было отклонено.
В этой сборке не подключена ни одна из сторон. Реестр, списки разрешённых инструментов, проверка схем, ограничения и поверхность аудита настоящие — через них и будет идти соединение, — а пакеты клиента и сервера реализованы и покрыты модульными тестами. Но ни один процесс не публикует собственный сервер MCP и ни одна функция не вызывает зарегистрированный инструмент, потому что функции AI, которые делали бы это, тоже не подключены. Тот же разбор со стороны моделей — в Провайдеры AI и локальные модели.

Omniflow как клиент

Обнаружение — это не авторизация

Регистрация сервера и включение его инструментов — два разных решения. Обнаружение считывает то, что сервер объявляет о себе, — его инструменты, ресурсы, промпты и версию протокола — и сохраняет это, чтобы вы могли посмотреть. Ничто не становится вызываемым, пока вы не внесёте его в список разрешений поштучно. Повторное обнаружение обновляет описания и схемы и не трогает ваши решения. Если бы оно заново включало инструмент, который вы выключили, обнаружение было бы способом расширить доступ без чьего-либо согласия.

Каждый инструмент отображается на право доступа, которое у вас уже есть

Инструмент непригоден к использованию, пока вы не сопоставите его с правом доступа Omniflow, и проверка выполняется против собственного набора прав запрашивающего оператора, а не полномочий сервера. Оператор поддержки не дотянется до инструмента, сопоставленного с finance.write, — ни через ассистента, ни через что-либо ещё. Панель отклоняет несуществующее право доступа, поэтому опечатка превращается в сообщение о поле, а не в инструмент, который никто не может вызвать.

Проверяются и аргументы, и результаты

Каждый вход и выход инструмента проверяется по JSON Schema, объявленной сервером, в том подмножестве, которое Omniflow действительно способен обеспечить. Схема, использующая конструкцию вне этого подмножества, делает инструмент непригодным к использованию уже во время обнаружения, с указанием причины, вместо того чтобы молча остаться без проверки в момент вызова. Проверять путь внутрь и не проверять путь наружу означало бы оставить неконтролируемым как раз интересное направление: результат — это та часть, которой управляет атакующий. Результат, не соответствующий объявленному контракту, отклоняется, а не передаётся дальше — это тот случай, когда сервер подменили. Объекты по умолчанию закрыты. Аргумент, который инструмент никогда не объявлял, не пересылается, потому что клиент, пересылающий неизвестные аргументы, — это именно то, чем попытка запутать инструмент такой аргумент и добавляет.

Ничто внешнее не записывается без человека

Инструмент, помеченный вами как записывающий, требует явного подтверждения и указанной причины при каждом вызове. Предпросмотр показывает сервер, эндпоинт, инструмент, проверенные аргументы, право доступа, которым авторизован вызов, и то, меняется ли что-нибудь за пределами Omniflow, — потому что подтверждение, которое не говорит, что произойдёт, — это кликанье не глядя. Флаг записи ставите вы, а не считывается из собственной аннотации сервера. Сервер, желающий избежать запроса на подтверждение, просто не выставил бы эту подсказку.

Всё, что возвращает сервер, — это данные

Результаты инструментов заключаются в ограждение, помечаются как данные и сканируются на формы, которые принимает непрямая инъекция промпта: переопределение инструкций, заявления о роли, директивы для инструментов, попытки эксфильтрации и скрытый текст. Находки показываются вам и фиксируются. Сканирование — вторичная защита. Структурная состоит в том, что содержимое подаётся модели как цитата, а не как инструкция, и это меняет то, чем оно является, а не пытается угадать, что в нём сказано. У содержимого, несущего разделитель ограждения, он удаляется — выход, который модель может отменить, не является границей, — а символы нулевой ширины вырезаются, потому что здесь они существуют, чтобы прятать инструкции от человека, читающего тот же текст, что и модель. Собственное поле instructions сервера сохраняется для показа и никогда не передаётся модели. Удалённая сторона, способная писать ваш системный промпт, управляет вашим ассистентом.

Ограничения и исходящий трафик

Исходящий трафик проверяется перед каждым запросом, а не однократно при регистрации. Имя хоста разрешается в другой адрес, чем час назад, и сервер, который после одобрения перенацеливается на эндпоинт облачных метаданных, — это хрестоматийный вариант такой атаки. Loopback-, частные и link-local-адреса отклоняются, если вы явно не разрешите частную сеть — что законно требуется самостоятельно размещённому серверу и что выключено по умолчанию, потому что это же и способ превратить соединение во внутренний сканер портов. Проверка выполняется дважды. Перед запросом имя хоста разрешается, чтобы отказ вернулся сообщением, по которому можно действовать. Затем дозвонщик проверяет адрес, к которому действительно подключается, уже после разрешения имени, поэтому ответ DNS, изменившийся между двумя моментами, не проскочит; записи внутреннего IPv4-адреса в виде IPv4-mapped IPv6 оцениваются как сам этот адрес. Перенаправление выполняется только на собственный хост эндпоинта или хост из списка разрешённых, по HTTPS, если не включено разрешение частной сети, и каждый шаг проходит через ту же проверку. HTTP-прокси из переменных окружения для этих соединений не используются. Эндпоинты без шифрования отклоняются валидацией на Go и ограничением на уровне таблицы. Правило, живущее только в коде приложения, — это правило, которое можно обойти одним скриптом миграции.

Когда сервер ведёт себя плохо

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

Omniflow как сервер

Собственный сервер публикует административные инструменты, ресурсы и операционную документацию с областью действия по правам доступа. Каждый вызов авторизуется как человек. Соединение несёт токен, который разрешается в оператора, и инструмент, которым этот оператор не мог бы воспользоваться в панели, — это инструмент, которым он не может воспользоваться и здесь. Служебной личности с собственными правами доступа не существует; она была бы обходом RBAC с более приятным интерфейсом. Инструменты, недоступные оператору, не перечисляются. Перечень, который рекламирует то, чего человек получить не может, учит ассистента спрашивать снова и снова. Только чтение, пока вы не скажете иное дважды. Мутирующим инструментам нужен AllowMutations и конкретный инструмент, названный в списке разрешений. «Подключите ассистента к своей админ-панели» не должно быть одним переключателем, который заодно означает «и разрешите ему делать возвраты». Каждая мутация идемпотентна по ключу. Мутирующий инструмент требует ключ идемпотентности, и повтор с тем же ключом возвращает первый результат, ничего не выполняя. Streamable HTTP делает повторы, а повторённый транспорт не должен превращать один возврат в два. Тот же ключ с другими аргументами отклоняется: это ошибка вызывающей стороны, а не повтор, и возврат первого результата молча ответил бы на вопрос, которого никто не задавал.

Что записывается

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

Где это настраивается

Настройки → AI и MCP. Регистрация сервера, внесение его инструментов в список разрешений, сопоставление каждого с правом доступа, пометка тех, что пишут, и чтение журнала активности происходят там. Учётные данные доступны только на запись: поле всегда пустое, потому что API никогда не возвращает значение, а если оставить его пустым, сохранённое значение сохраняется.