Безопасность MCP в продакшене
Что уже требует спецификация MCP, что на самом деле сломали CVE кодовых агентов 2026 года и какие меры выдерживают: проверка аудитории, фиксация инструментов, песочницы, политика egress и аудит.
- MCP
- Безопасность
- IAM
- ИИ-агенты
- OAuth
MCP-сервер — это не очередной API. Это канал инструкций, который модель читает и по которому действует. Каждое имя инструмента, каждое описание параметра и каждая строка, возвращённая инструментом, попадают в то же окно контекста, что и запрос пользователя, а модель не умеет надёжно отличать данные от команд. Одно это свойство делает безопасность Model Context Protocol отдельной дисциплиной, не сводимой к безопасности API, и объясняет, почему инциденты 2026 года повторяются так однообразно.

Это эксплуатационное руководство, а не эссе по моделированию угроз: что уже требует спецификация, что на самом деле сломали инциденты 2026 года и какие меры выдерживают продакшен — с командами для проверки каждой.
Что MCP меняет в модели угроз
В обычной интеграции разработчик решает, какой вызов, с какими аргументами и в какой точке кода выполняется. В MCP клиент передаёт модели определения инструментов всех подключённых серверов, и модель сама выбирает, что вызвать и с какими параметрами. OWASP формулирует следствие прямо: MCP сводит в одну точку инъекцию промпта, риск цепочки поставок и проблему «запутанного заместителя».[owaspcs]
- Метаданные инструмента ведут себя почти как код. Описания и JSON Schema модель читает как руководство к действию. Отравленное поле
descriptionближе к внедрённому коду, чем к документации. - Вывод инструмента — недоверенный ввод. Веб-страница, заголовок issue или строка из базы возвращаются в контекст с тем же статусом, что и системный промпт.
- Модель видит все серверы одновременно. Описание вредоносного сервера может изменить то, как агент пользуется другим, доверенным сервером. Это tool shadowing, и проверка «сервер за сервером» его не находит.[owaspcs]
Спецификация прямо говорит, что не решает это за вас: авторизация для реализаций MCP ОПЦИОНАЛЬНА; при HTTP-транспорте её СЛЕДУЕТ соблюдать, а реализациям на stdio предписано брать учётные данные из окружения.[spec-auth] Всё остальное — песочницы, целостность инструментов, контроль исходящего трафика — остаётся на том, кто выпускает хост, клиент или сервер.
Инциденты 2026 года, задающие планку
Два самых наглядных случая года пришли не из экзотических лабораторных стендов, а из конфигураций по умолчанию самих вендоров. 5 августа 2026 года на Black Hat USA компания Novee Security проверила кодовые агенты Anthropic, Google и OpenAI в тех конфигурациях, которые эти вендоры поставляют по умолчанию. Из работы вышли два CVE, оба исправлены. То, что каждый из них реально позволял, различается очень сильно, и в этом главная польза.[ghsa-google][ghsa-anthropic]
| Случай | Что отказало на самом деле | Исправлено в |
|---|---|---|
| CVE-2026-12537 — Gemini CLI, CVSS v4 10.0 по оценке CNA[cve12537] | Инъекция команд ОС в запускателе контейнеров, достижимая через подготовленный файл .gemini/.env. Код выполнялся на хосте headless-платформы CI до старта песочницы. | Gemini CLI 0.39.1 · run-gemini-cli 0.1.22[ghsa-google] |
| CVE-2026-54316 — Claude Code, CVSS v4 6.0 (NVD v3.1: 9.1)[cve54316] | huggingface.co был предварительно одобрен как «голое» имя хоста для WebFetch, поэтому любой путь на этом домене — включая репозитории под контролем атакующего — одобрялся автоматически. HuggingFace учитывает такие запросы на своей стороне, поэтому счётчики загрузок превратились во внеполосный скрытый канал для вывода данных, доступных агенту: файлов, переменных окружения, вывода команд. Затронута только конфиденциальность, выполнения кода нет. Затронуты версии ≥ 0.2.54 и < 2.1.163. | Claude Code 2.1.163[ghsa-anthropic] |
| OpenAI Codex — ни CVE, ни новой версии | Два прохода Codex делили один checkout, поэтому первый мог записать AGENTS.md — файл, который второй загружал как собственные инструкции. Исправлено на уровне workflow, а не продукта. | Разделение проходов; руководство теперь относит файлы инструкций репозитория к недоверенному вводу[codex] |
Закономерность стоит сформулировать точно, потому что она выходит далеко за пределы этих трёх продуктов: отказ произошёл в «обвязке» (harness), а не в модели. Один компонент пометил значение безопасным, а следующий выполнил над ним действие с большими правами. По данным публикаций от 7 августа 2026 года ни один из CVE не значился в каталоге KEV агентства CISA, и публичные источники не показывают применения этих цепочек против реальной цели — важен класс дефекта, а не активная кампания.
Читайте это вместе с операционной моделью из DevOps, MLOps и LLMOps: если вы не можете назвать точный набор, породивший действие — модель, промпт, схему инструмента, политику, — вы не объясните инцидент и не сможете доверять откату.
Базовый уровень, который спецификация уже требует
Удивительно большая часть работы по «безопасности MCP» — это просто корректно сделанный OAuth. Ревизия 2025-06-18 переклассифицировала MCP-сервер в чистый resource server OAuth 2.1, а ревизия 2025-11-25 уточнила обнаружение и согласие.[spec-log] Если вы держите удалённый MCP-сервер, дальше идут не рекомендации.[oauth21][rfc9700][nsa]
Обнаружение: RFC 9728, а не README
MCP-серверы ОБЯЗАНЫ реализовать OAuth 2.0 Protected Resource Metadata и объявить хотя бы одну запись в authorization_servers; клиенты ОБЯЗАНЫ использовать это для обнаружения. На неаутентифицированный запрос сервер отвечает 401 с заголовком WWW-Authenticate, указывающим на документ метаданных.[spec-auth][rfc9728]
curl -sSI https://mcp.example.com/mcp | grep -i '^www-authenticate'
# www-authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
curl -sS https://mcp.example.com/.well-known/oauth-protected-resource \
| jq '{resource, authorization_servers, scopes_supported}'
curl -sS https://auth.example.com/.well-known/oauth-authorization-server \
| jq '{issuer, token_endpoint, code_challenge_methods_supported}'Привязка к аудитории: мера, которая останавливает повторное использование токенов
Клиенты ОБЯЗАНЫ передавать параметр resource из RFC 8707 и в запросе авторизации, и в запросе токена, указывая каноническую URI целевого сервера, и делать это независимо от поддержки со стороны сервера авторизации.[rfc8707] Сервер ОБЯЗАН проверить, что предъявленный токен выпущен именно для него, и отклонить его в противном случае.[spec-auth]
Token passthrough — принять токен, выпущенный не для вас, и переслать его дальше — прямо запрещён. Обоснование в спецификации эксплуатационное: он разрушает журнал аудита, обходит ограничения частоты и проверки, зависящие от аудитории, и превращает ваш сервер в прокси для любого, у кого есть украденный токен.[spec-sec] Если ваш MCP-сервер обращается к вышестоящему API, он выступает там клиентом и нуждается в собственном токене.
CANONICAL = "https://mcp.example.com/mcp" # RFC 8707 resource identifier
def authorize(token: dict) -> None:
aud = token.get("aud")
aud = aud if isinstance(aud, list) else [aud]
if CANONICAL not in aud:
raise Unauthorized("token audience does not include this server") # 401
if not required_scopes(token).issuperset(scopes_for_current_tool()):
raise Forbidden("insufficient scope") # 403
# Never forward `token` upstream. Mint a separate one for the upstream API.Регистрация клиентов: CIMD становится рекомендуемым вариантом
Ревизия 2025-11-25 добавила OAuth Client ID Metadata Documents как рекомендуемый механизм регистрации клиентов (SEP-991), а также поддержку OpenID Connect Discovery и инкрементальное согласие на scope через WWW-Authenticate.[spec-log][cimd] В CIMD client_id — это HTTPS-URL, ведущий на небольшой JSON-документ с описанием клиента; сервер авторизации загружает его, проверяет зарегистрированные redirect URI и применяет собственную политику — без предварительных договорённостей и без вороха одноразовых регистраций, которые порождает Dynamic Client Registration.
Это важно, потому что DCR — один из ингредиентов атаки «запутанного заместителя». Когда проксирующий MCP-сервер использует статический client ID у стороннего сервера авторизации, плюс разрешает динамическую регистрацию, плюс сторона выставляет cookie согласия, атакующий может зарегистрировать клиент со своим redirect_uri, воспользоваться cookie жертвы, пропустить экран согласия и забрать код авторизации. Мера защиты — согласие на каждого клиента, хранимое на сервере и проверяемое до стороннего потока, точное сравнение redirect URI и одноразовый state.[spec-sec]
Ничего нового в инженерии идентичности здесь нет — это та же дисциплина, что и проектирование масштабируемого IAM, с теми же компромиссами, что и при выборе между Keycloak и Entra ID. Переиспользуйте сервер авторизации, который вы уже эксплуатируете и аудируете; не позволяйте каждому MCP-серверу отращивать свой.
Десять режимов отказа и мера, закрывающая каждый
OWASP MCP Top 10 — живой документ в статусе беты (v0.1), следующий выпуск запланирован на октябрь 2026 года, так что это скорее чек-лист, чем цель сертификации.[owasp10]
| Риск OWASP MCP | Мера, которая его закрывает |
|---|---|
| MCP01 Неправильное обращение с токенами и утечка секретов | Короткоживущие токены на каждый сервер и каждого пользователя в хранилище учётных данных ОС — не в файлах конфигурации. Ротация refresh-токенов для публичных клиентов.[spec-auth] |
| MCP02 Повышение привилегий из-за расползания scope | Начинать с минимального набора scope и повышать их постепенно через вызовы WWW-Authenticate scope="…". Никаких *, никаких сборных scope, не публиковать весь каталог в scopes_supported.[spec-sec] |
| MCP03 Отравление инструментов (rug pull, схема, shadowing) | Зафиксировать SHA-256 канонического JSON из имени, описания и входной схемы в момент одобрения; пересчитывать перед каждым выполнением и отказывать при расхождении.[owaspcs] |
| MCP04 Цепочка поставок и подмена зависимостей | Фиксировать версии и digest, проверять контрольные суммы или подписи, сканировать зависимости, проверять тайпсквоттинг перед установкой.[owaspcs] |
| MCP05 Инъекция и выполнение команд | Никакой shell-интерполяции строк от модели: только массивы аргументов, строгая JSON Schema с additionalProperties: false и ограничениями pattern. |
| MCP06 Подмена потока намерения / контекстная инъекция | Считать любой ответ инструмента данными: удалять разметку, похожую на инструкции, предпочитать структурированное извлечение сырому HTML, оповещать о повелительных конструкциях в выводе.[owaspcs] |
| MCP07 Недостаточные аутентификация и авторизация | OAuth 2.1 с PKCE, проверка аудитории, авторизация на каждый запрос. Сессии НЕ ДОЛЖНЫ использоваться для аутентификации.[spec-sec] |
| MCP08 Отсутствие аудита и телеметрии | Журналировать каждый вызов с полными параметрами, идентичностью вызывающего, хешем инструмента и correlation ID; маскировать секреты; отправлять в SIEM. |
| MCP09 Теневые MCP-серверы | Реестр одобренных серверов, политика исходящего трафика, блокирующая остальные, регулярные проверки рабочих машин и CI-образов. |
| MCP10 Инъекция контекста и избыточный обмен | Ограничивать контекст задачей и арендатором; данные, полученные в одной сессии, не должны переживать её. |
Укрепление по слоям
Фиксируйте определения инструментов — или примите rug pull
Rug pull работает потому, что инструмент одобряют один раз и больше не смотрят. Исправление механическое: хешировать определение при одобрении и проверять перед каждым вызовом.
import hashlib, json
def tool_fingerprint(tool: dict) -> str:
canonical = json.dumps(
{"name": tool["name"],
"description": tool.get("description", ""),
"inputSchema": tool.get("inputSchema", {})},
sort_keys=True, separators=(",", ":"), ensure_ascii=False)
return hashlib.sha256(canonical.encode()).hexdigest()
if tool_fingerprint(live_tool) != PINNED[server_id][live_tool["name"]]:
raise ToolDefinitionDrift(server_id, live_tool["name"]) # fail closedОтказывать «в закрытую». Инструмент, чьё описание изменилось после одобрения, — это новый инструмент, и решение по нему должен принять человек, а не молчаливый повтор.
Изолируйте локальные серверы всерьёз
Локальный MCP-сервер — это бинарник, работающий с правами клиента. Указание спецификации для конфигурации «в один клик» недвусмысленно: показать точную команду без сокращений, предупредить, что она выполняется с правами клиента, и запустить её в песочнице с минимальным доступом к файловой системе и сети.[spec-sec] Локально предпочтителен stdio, поскольку он ограничивает досягаемость процессом клиента. Если без HTTP не обойтись, слушайте на 127.0.0.1 — никогда на 0.0.0.0 — требуйте токен и проверяйте заголовок Host при каждом запросе.[owaspcs]
docker run --rm -i \
--network none \
--read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m \
--cap-drop ALL --security-opt no-new-privileges \
--pids-limit 128 --memory 512m --cpus 1 \
--user 10001:10001 \
-v "$PWD/workspace:/workspace:ro" \
ghcr.io/example/mcp-server@sha256:<digest>Digest @sha256: — это контроль цепочки поставок, а не вопрос вкуса: тег можно перенаправить, digest — нет. Если вы и так запускаете нагрузки в Kubernetes, здесь работает та же логика радиуса поражения, что и в случаях, когда Kubernetes не нужен: контейнер на каждый MCP-сервер оправдан, control plane на каждый MCP-сервер — нет.
Контролируйте исходящий трафик: SSRF живёт в процедуре обнаружения
Этот пункт легко пропустить, потому что он бьёт по клиенту, а не по серверу. Во время OAuth-обнаружения клиент загружает URL, которые ему даёт сервер: адрес resource_metadata из WWW-Authenticate, записи authorization_servers, затем эндпоинты из метаданных сервера авторизации. Вредоносный сервер может направить любой из них на http://169.254.169.254/ и прочитать учётные данные облачного инстанса через ваш клиент.[spec-sec]
Клиентам СЛЕДУЕТ требовать HTTPS вне loopback, блокировать приватные и link-local диапазоны (10/8, 172.16/12, 192.168/16, 127/8, 169.254/16, fc00::/7, fe80::/10), применять те же правила к каждому переходу редиректа и выводить серверные развёртывания через egress-прокси. Спецификация прямо предостерегает от самописной проверки IP — восьмеричные, шестнадцатеричные и IPv4-mapped IPv6 записи обходят большинство самодельных парсеров — и от DNS rebinding между проверкой и использованием.[spec-sec][owaspssrf]
Кодовые агенты в CI: самая ценная цель, которая у вас есть
Агент в CI обладает правом записи в репозиторий и доступом к секретам workflow, а его вход — это то, что незнакомый человек написал в issue. Именно эту конфигурацию атаковала работа с Black Hat.
on:
issues:
types: [opened]
permissions:
contents: read
issues: read
id-token: none
jobs:
triage:
runs-on: ubuntu-latest
environment: agent-sandbox
timeout-minutes: 10
steps:
- uses: actions/checkout@<commit-sha>
with: { persist-credentials: false }
- run: printf '%s' "$ISSUE_BODY" > /tmp/untrusted.md
env:
ISSUE_BODY: ${{ github.event.issue.body }}
- run: agent-cli review --input /tmp/untrusted.md --sandbox read-only --no-network- Никогда не интерполируйте
${{ github.event.* }}в строкуrun:. Передавайте через переменную окружения или файл. - Файлы инструкций репозитория — недоверенный ввод. Так теперь говорит и собственное руководство OpenAI, после того как два прохода Codex с общим checkout позволили первому записать инструкции второго.[codex]
- Запускайте агента последним шагом. То же руководство предупреждает, что иначе он может оставить файлы для последующих привилегированных шагов.[codex]
- Разносите проходы по разным job с разными checkout, чтобы один не мог записать вход другого.
- Обновите обвязку. Gemini CLI 0.39.1, run-gemini-cli 0.1.22, Claude Code 2.1.163 — затем проверьте каждый workflow, который может запустить посторонний.
Аудит: каждый вызов должен восстанавливаться
OWASP выделяет отсутствие телеметрии в отдельный риск, и именно он превращает локализованный инцидент в бесконечный.[owasp10] Минимальная запись на вызов: аутентифицированная идентичность вызывающего, сервер и инструмент, проверенный отпечаток инструмента, полные параметры с маскированными секретами, путь принятия решения (автоматически, по политике, человеком), результат, задержка и correlation ID на весь прогон агента. Оповещайте о впервые встреченных инструментах, повышениях scope и конструкциях, похожих на инструкции, в выводе инструментов.
Два структурных решения делают это управляемым, когда серверов больше горстки. Первое: поставить шлюз впереди — одна точка, где действуют allow-листы, изоляция по серверам, политика egress и журналирование, лучше, чем та же политика, переписанная в каждом хосте. Второе: держаться скучного решения там, где можно — довод из скучных облачных архитектур здесь применим буквально: меньше подвижных частей, меньше границ доверия, меньше мест, где спрячется «запутанный заместитель». Та же сдержанность окупается уже при сборке стека агентов.
Проверочный прогон, который можно сделать сегодня
Девять проверок: 1) неаутентифицированный запрос отклоняется с рабочим указателем; 2) метаданные защищённого ресурса опубликованы; 3) токен для другой аудитории отклоняется; 4) обычный HTTP не принимается; 5) локальные серверы не слушают на всех интерфейсах; 6) определения инструментов совпадают с одобренными; 7) образы зафиксированы по digest; 8) данные события не интерполируются в shell-шаги; 9) версии обвязки не ниже исправленных.
# 1
curl -sS -o /dev/null -w '%{http_code}\n' https://mcp.example.com/mcp
# 2
curl -sSf https://mcp.example.com/.well-known/oauth-protected-resource | jq -e '.authorization_servers[0]'
# 3
curl -sS -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $TOKEN_FOR_OTHER_AUDIENCE" \
https://mcp.example.com/mcp
# 4
curl -sS -o /dev/null -w '%{http_code}\n' http://mcp.example.com/mcp
# 5
ss -ltnp | grep -E '0\.0\.0\.0|\[::\]'
# 6
mcp-inspect list-tools --server internal | sha256sum
# 7
grep -rEn 'image:\s*\S+:(latest|main|v?[0-9]+)\s*$' deploy/
# 8
grep -rn 'github\.event\.\(issue\|comment\|pull_request\)' .github/workflows/ | grep 'run:'
# 9
gemini --version; claude --versionПроверка 3 чаще всего проваливается с первого раза. Она же — та, по которой спецификация высказывается наименее двусмысленно.
Что сделать на этой неделе
MCP стоит внедрять. Протокол снимает настоящий класс интеграционной рутины, а ревизия 2025-11-25 перенесла важные решения по безопасности из фольклора в текст спецификации. Но поставляется он как возможность, а не как control plane. Значимые меры вам уже знакомы по работе с идентичностью и цепочкой поставок — их надо применить к границе, которую большинство команд ещё не провело.
В порядке приоритета: обновить обвязку; сделать проверку аудитории обязательной; зафиксировать определения инструментов и digest образов; изолировать локальные серверы и закрыть исходящий трафик; поставить человеческий контроль перед всем разрушительным. В ближайший год обожгутся не те, кто внедрил MCP, а те, кто внедрил его и оставил обвязку в настройках по умолчанию.
Частые вопросы
Безопасен ли MCP по умолчанию?
Нет, и спецификация этого не утверждает. Авторизация для реализаций MCP опциональна, а песочницы, целостность инструментов и сетевая политика остаются на том, кто строит хост, клиент или сервер. Локально установленный MCP-сервер работает с теми же правами, что и запустивший его клиент.
Что такое отравление инструментов в MCP?
Это манипуляция инструментами, от которых зависит агент, чтобы изменить поведение модели. OWASP объединяет здесь три подтехники: rug pull, когда описание одобренного инструмента меняют задним числом; отравление схемы, когда вводит в заблуждение само определение интерфейса; и tool shadowing, когда описание вредоносного сервера меняет использование инструментов другого, доверенного сервера. Зафиксированный хеш имени, описания и входной схемы, проверяемый перед каждым вызовом, закрывает первые две техники и выявляет третью.
Обязательно ли использовать OAuth для MCP-сервера?
Для удалённых HTTP-транспортов — фактически да. Спецификация предписывает HTTP-реализациям следовать её модели OAuth 2.1: сервер выступает resource server, обязан реализовать RFC 9728 и обязан проверять, что предъявленные токены выпущены для него. Локальные stdio-серверы берут учётные данные из окружения, но им по-прежнему нужны песочница и контроль согласия.
Что такое token passthrough и почему он запрещён?
Это приём токена, выпущенного не для вас, и его пересылка без изменений дальше по цепочке. Спецификация MCP прямо это запрещает: ломается журнал аудита, обходятся проверки, зависящие от аудитории токена, а любой обладатель украденного токена получает ваш сервер в качестве прокси для эксфильтрации. MCP-сервер, обращающийся к вышестоящему API, обязан получить для него собственный токен.
Эксплуатируются ли уязвимости MCP в реальных атаках?
На момент публикации ни CVE-2026-12537, ни CVE-2026-54316 не значатся в каталоге KEV агентства CISA, и публичные материалы не показывают применения этих цепочек против цели. Публичный репозиторий с воспроизведением проблемы Claude Code существует с июня 2026 года, так что ограничивающим фактором стоит считать скорость установки обновлений, а не доступность эксплойта.
Как не дать вредоносному MCP-серверу добраться до внутренней сети?
Уязвимая точка — процедура OAuth-обнаружения: клиент загружает URL, которые ему даёт сервер, поэтому вредоносный сервер может направить их на эндпоинты облачных метаданных или внутренние сервисы. Требуйте HTTPS вне loopback, блокируйте приватные и link-local диапазоны, применяйте ту же проверку к каждому переходу редиректа и выводите серверные клиенты через egress-прокси. Не пишите проверку IP вручную.
Что такое Client ID Metadata Documents (CIMD)?
CIMD — механизм регистрации клиентов, добавленный как рекомендуемый вариант в ревизии MCP 2025-11-25 по SEP-991. client_id представляет собой HTTPS-URL, ведущий на JSON-документ с описанием клиента и его redirect URI, который сервер авторизации загружает и проверяет по требованию. Это избавляет и от «зашитых» client ID, и от вороха одноразовых регистраций Dynamic Client Registration.
Источники и дальнейшее чтение
Текст спецификации, RFC IETF, материалы проектов OWASP, информационный бюллетень АНБ по кибербезопасности и официальные бюллетени вендоров, использованные в этой статье.
- Model Context Protocol — Authorization (specification 2025-11-25)
- Model Context Protocol — Security Best Practices
- Model Context Protocol — Key Changes, revision 2025-11-25
- SEP-991 — OAuth Client ID Metadata Documents for MCP
- RFC 9728 — OAuth 2.0 Protected Resource Metadata
- RFC 8707 — Resource Indicators for OAuth 2.0
- RFC 9700 — Best Current Practice for OAuth 2.0 Security
- OAuth 2.1 — draft-ietf-oauth-v2-1-13
- OWASP MCP Top 10 (beta, v0.1)
- OWASP Cheat Sheet Series — MCP Security
- OWASP Cheat Sheet Series — SSRF Prevention
- NSA Artificial Intelligence Security Center — CSI: MCP Security Design Considerations for AI-Driven Automation
- GHSA-fg94-h982-f3mm — Out-of-Band Data Exfiltration via Pre-Approved HuggingFace Domain in WebFetch
- NVD — CVE-2026-54316
- GHSA-wpqr-6v78-jr5g — Google run-gemini-cli security advisory
- NVD — CVE-2026-12537
- OpenAI — codex-action security guidance
Было полезно?