Введение
С развитием AI-агентов и Model Context Protocol (MCP) актуальной становится проблема безопасности при работе с различными инструментами. Что если ваш AI-агент случайно прочитает конфиденциальный файл с токенами доступа и «случайно» поделится ими с вами в своем ответе, а учитывая логирование ваших запросов, он точно попадет на сервер провайдера вашего агента, а возможно еще и IDE, в которой этот агент и обитает, это создаст ряд неприятностей.
Ярким примером тулы, которая выполняет поиск по вашим локальным файлом является mcp-filesystem. Один шаг не туда и вам придется бежать за ревоком токена, а если он еще и от организации, ваши админы явно не скажут вам спасибо.
Не забываем про то, как у XAI токен просто лежал на github пару месяцев: статья
Для решения этой проблемы команда Lasso Security разработала MCP Gateway — прокси-сервер, который встает между AI-агентом и MCP-тулами, обеспечивая санитизацию чувствительной информации.
В этой статье я поделюсь результатами тестирования Lasso MCP Gateway в двух сценариях: интеграция с Cursor IDE и локальная реализация с собственным агентом на PydanticAI.
Что умеет Lasso MCP Gateway
1) Перехватывает вызовы всех MCP-инструментов перед их выполнением
2) Маскирует в basic вариации (бесплатной):
-
azure client secret
-
github tokens
-
github oauth
-
gcp api key
-
aws access token
-
jwt token
-
gitlab session cookie
-
huggingface access token
-
microsoft teams webhook
-
slack app token\
Есть интеграция с presidio и самим lasso. Позволяет логировать вызовы через xetrack
Общий принцип работы
Идея MCP-gateway заключается в том, что он становится неким прокси между агентом и тулами. То есть он подключается не просто как отдельный MCP тул, а как MCP-тул, под которым работают другие тулы. То есть любой вызов плагина по типу read_file create_file и тд будет происходит после того как mcp-gateway дал условное одобрение и провел санитизацию.
Эксперименты
Тестирование в Cursor
Настройка и установка Следуя примеру из readme.md я подключил mcp-gateway к cursor, добавил путь к логированию через xetrack. Как выглядит:
{ "mcpServers": { "mcp-gateway": { "command": "uvx", "args": [ "mcp-gateway", "--mcp-json-path", "/home/user/.cursor/mcp.json", "--plugin", "basic", "--plugin", "xetrack" ], "env": { "XETRACK_LOGS_PATH": "logs/", "XETRACK_DB_PATH": "tracing.db" }, "servers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "." ] } } } } }
Эксперимент 1: Чтение файла с HuggingFace токеном
Создал файл
echo 'HF_TOKEN = "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"' > tokens.txt
в нем теперь хранится выдуманный токен из Huggingface
Следуя снова же примеру из readme.md пишу запрос
Cursor получил уже очищенный контекст и вернул мне <HUGGINGFACE_TOKEN>.
Подробнее:
в тулу передался параметр:
{ "path": "~/lasso-mcp/sts/tokens.txt" }
внутри tokens.txt хранится мой выдуманный hf token
тула произвела очистку:
{ "_meta": null, "content": [ { "type": "text", "text": "HF_TOKEN = \"<HUGGINGFACE_TOKEN>\n", "annotations": null } ], "isError": false }
затем тула вернула респанс cursor агенту и он вернул очищенный токен:
<HUGGINGFACE_TOKEN>
Эксперимент 2: Обход через контекст агента
Интересный нюанс: когда я попросил агента создать файл с кодом, передав токен непосредственно в чате, MCP Gateway не смог заблокировать эту операцию. Санитизация происходит только при чтении файлов, но не при генерации/обработке моего запроса
(что в принципе и не удивительно).
Когда я позже попросил прочитать созданный файл, MCP Gateway снова сработал корректно. Однако Cursor «помнил» о токене из контекста беседы и просто обновил информацию, вернув мой токен мне же обратно.
Эксперимент 3
Создаем .env файл с выдуманным ключом от OpenAI: ключ:
OPENAIKEY=sk-qwejxakdjruewhjnsvasdj
Результат работы агента с явным вызовом mcp-gateway через наш запрос:
В общем говоря, если ваш токен не находится в списке токенов из начала, не надейтесь, что вам повезет и он не будет слит.
Local Implementation
Тестовые данные
Для локальной реализации был создан файл с 5-ю вариациями токенов:
HF_TOKEN = "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" OPENAI_TOKEN = "sk-proj-SDASDFASDSDDSFDFSDADSdfsdfaHAJKDD5JASMDBXAHEG4123XDLKCXCDFSDFSAZXCN" JWT_TOKEN ="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik..." GITHUB_TOKEN_CLASSIC = "ghp_mS5asdaSJASHDqwDSAD4KD" GITHUB_TOKEN = "github_pat_33SDSDJKJXKCJXIHFGAdfdsfkS4333Nwqe13weqeirurururrrr"
MCP
Не забудьте установить mcp-gateway через uv/pip или используйте uvx
Для запуска MCP-Gateway ему обязательно нужен mcp.json
{ "mcpServers": { "mcp-gateway": { "command": "uv", "args": [ "run", "mcp-gateway", "-p", "basic", "-p", "xetrack" ], "env": { "XETRACK_DB_PATH": "tracing.db", "XETRACK_LOGS_PATH": "logs/" }, "servers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "." ] } } } } }
Агент
Реализовал простого агента через pydantic AI, с подключением к Lasso MCP-Gateway в базовой реализации (бесплатной) и логированием через xetrack
from typing import Optional from pydantic_ai import Agent from pydantic_ai.models.openai import OpenAIModel from pydantic_ai.providers.openai import OpenAIProvider from pydantic_ai.mcp import MCPServerStdio import os from dotenv import load_dotenv import asyncio load_dotenv() async def get_custom_openai_provider_response( model_name: str, query: str, provider_url: Optional[str] = None, system: Optional[str] = "", api_key: Optional[str] = None, ) -> str: """ Func to call custom open ai provider, using `provider_url` if needed """ lasso_gateway = MCPServerStdio( command="uv", args=["run","mcp-gateway","--mcp-json-path", "./mcp.json","--plugin","basic", "--plugin", "xetrack"], env={"XETRACK_DB_PATH": "tracing.db", "XETRACK_LOGS_PATH": "logs/"} ) model = OpenAIModel( model_name=model_name, provider=OpenAIProvider(base_url=provider_url, api_key=api_key), ) agent = Agent( model=model, retries=5, system_prompt=system, mcp_servers=[lasso_gateway] ) async with agent.run_mcp_servers(): result = await agent.run(query) return result async def main(): test_keys = [ "HF_TOKEN", "OPENAI_TOKEN", "JWT_TOKEN", "GITHUB_TOKEN_CLASSIC", "GITHUB_TOKEN" ] for key in test_keys: query = f"Use your tools to read the ./tokens.txt and return the {key}" print(f"Testing key: {key}") response = await get_custom_openai_provider_response( model_name="gpt-4.1-nano-2025-04-14", system="Use all available tools to help user with his response, be always helpful", query=query, api_key=os.getenv("OPENAI_API_KEY") ) print("#############") print(f"Response for {key}: {response}\n") print("#############") if __name__ == "__main__": asyncio.run(main())
результат тестирования:
HF_TOKEN = «<HUGGINGFACE_TOKEN>
OPENAI_TOKEN = «sk-proj-…»
JWT_TOKEN = «<JWT_TOKEN>
GITHUB_TOKEN_CLASSIC = «ghp_mS5asdaSJASHDqwDSAD4KD»
GITHUB_TOKEN = «github_pat_33..
Обещанный github-токен даже не был санитизирован что в классической, что в новой версии.
Что логирует xetrack
Среди полезной информации, которую сохраняет xetrack:
-
capability_name: название инструмента (например,
read_file) -
path: путь к файлу (
./tokens.txt) -
content_text: содержимое после санитизации (
HF_TOKEN = "<HUGGINGFACE_TOKEN>") -
pattern: шаблоны поиска (если применимо)
и не только.
Важно: xetrack НЕ логирует запросы пользователя и финальные ответы агента — только вызовы MCP-серверов, которые с ним явно связаны в вашем mcp.json.
Вывод
Summary
Достаточно игрушечное решение, которое вроде бы работает на уровне mcp/tool-call и выступает прослойкой между всеми вашими mcp-тулами. Не позволяет агенту прочитать токен доступа из существующих файлов/источников, просто санитизируя ответ перед добавлением в контекст агента.
Для кого
Может помочь: в разработке, чтобы не скормить случайно агенту ключ из условного .env и не делать revoke, однако поддерживает только ограниченное число вариаций этих ключей. Бесполезен если: Вы явно отдали агенту ключ или ваш вид ключа не поддерживается mcp-gateway, e.g. OpenAIApiKey.
Что работает хорошо:
-
Простая интеграция — настройка в клиент в виде Cursor, Windsurf, VS Code, Claude-desktop и другие, а также в ваш проект с агентом под капотом.
-
Полезное логирование — xetrack предоставляет информацию о результатах работы.
-
Работает как прокси — не нужно модифицировать существующие MCP-серверы, просто перетащить их в серверы
mcp-gateway -
Санитизация — MCP Gateway маскирует почти все поддерживаемые типы токенов при чтении файлов
Что работает плохо:
-
Не защищает от контекста — если токен передан в чате, а не прочитан из файла, защита не сработает, поскольку ваш агент его запомнит.
-
Ограниченный набор паттернов — базовая версия поддерживает только ограниченные типы токенов
Lasso MCP Gateway как идея мне кажется достаточно интересным, поскольку я сам пользуюсь умными помощниками, которые иногда получают доступ к тем файлам, к которым мне не хотелось бы, особенно это актуально для IDE-агентов, которые сами могут пройтись по проекту и собрать контекст. Это не очень приятно и базовое предложение делать .gitignore не всегда работает.
Но по факту это достаточно сырое решение, которое покрывает ограниченное количество кейсов, а в тех случаях когда вам критично хранение отдельных секретов, пока что, проще сделать свое решение.
ссылка на оригинал статьи https://habr.com/ru/articles/910556/
Добавить комментарий