Защита MCP-серверов: OAuth, привязка к аудитории и запутанный заместитель
- Понять, почему удалённый (HTTP) MCP-сервер является сервером ресурсов OAuth 2.1, а не просто эндпоинтом с API-ключом
- Проследить рукопожатие обнаружения: 401 → Protected Resource Metadata → Authorization Server Metadata → токен
- Объяснить привязку токена к аудитории (RFC 8707) и почему она не даёт токену одного сервиса работать на другом
- Назвать ловушку запутанного заместителя и единственное правило, которое её закрывает: никогда не пробрасывать токен клиента в вышестоящий API
- Применить короткий чек-лист усиления безопасности перед тем, как выставить MCP-сервер в интернет
MCP превратился из новинки в способ по умолчанию, которым агенты добираются до инструментов — а значит, MCP-серверы теперь стоят перед реальными данными и реальными действиями. Локальный сервер, который вы запускаете через STDIO, доверяет своему окружению: он читает учётные данные из переменных окружения, и защищать сетевую границу не нужно. В тот момент, когда вы делаете тот же сервер удалённым (HTTP), любой, кто может дотянуться до URL, может попытаться его вызвать. Это превращает задачу в проблему авторизации, и спецификация MCP отвечает на неё OAuth 2.1 — а не самодельной схемой с API-ключами.
Эта страница о удалённом случае. Если ваш сервер работает только через STDIO, спецификация прямо говорит: не следуйте потоку OAuth — берите учётные данные из окружения и идите дальше.
Три роли
OAuth разбивает задачу на три стороны. MCP аккуратно ложится на них:
Ключевой сдвиг в мышлении: MCP-сервер сам никогда не обрабатывает вход. Он лишь проверяет токены, выданные кем-то другим. Именно это разделение позволяет поставить готового поставщика идентификации перед сервером, который вы написали.
Рукопожатие обнаружения
Клиент не должен заранее быть настроен на то, где аутентифицироваться. MCP делает обнаружение автоматическим, управляемым 401:
- Самый первый запрос уходит пустым. Сервер отклоняет его с HTTP 401 Unauthorized и заголовком WWW-Authenticate, указывающим на URL его resource-metadata.
- Он делает GET на /.well-known/oauth-protected-resource у сервера. Поле authorization_servers в документе называет как минимум один сервер авторизации, который клиент может использовать.
- Он делает GET на /.well-known/oauth-authorization-server у AS, чтобы узнать эндпоинты authorize и token и поддерживаемые возможности.
- Если у клиента нет client ID для этого AS, он может сделать POST на /register, чтобы получить его без участия человека — это критично, потому что клиент не может знать каждый MCP-сервер заранее.
- Клиент генерирует верификатор/challenge PKCE, открывает браузер по URL authorize, включая параметр resource, пользователь даёт согласие, и клиент обменивает возвращённый код (вместе с верификатором) на токен доступа.
- Теперь каждый запрос несёт Authorization: Bearer <token>. Сервер проверяет его и отвечает.
Обратите внимание: на стороне клиента нет захардкоженной конфигурации аутентификации — 401 запускает всё остальное. В этом весь смысл: агент может подключиться к серверу, который он никогда не видел, и разобраться, как аутентифицироваться.
Привязка к аудитории: несущее правило
Вот тот сценарий отказа, ради предотвращения которого существует привязка к аудитории. Скажем, у пользователя есть токен, выданный для calendar.example.com. Вредоносный (или просто небрежный) MCP-сервер на evil.example.com обманом заставляет клиент отправить этот токен ему. Если evil его примет, он может тут же вызвать API календаря от имени пользователя. Токен одного сервиса сработал на другом. Граница безопасности OAuth только что рухнула.
Решение — Resource Indicators (RFC 8707):
- Как в запросе на авторизацию, так и в запросе на токен клиент ОБЯЗАН включить параметр resource, установленный в канонический URI MCP-сервера, который он намеревается вызвать — например resource=https://mcp.example.com. Он отправляет его, даже если не уверен, что AS его поддерживает.
- Когда это поддерживается, AS помечает токен так, чтобы он был действителен только для этого конкретного сервера ресурсов.
- Прежде чем выполнять любую работу, MCP-сервер ОБЯЗАН убедиться, что токен был выдан именно ЕМУ — проверяя claim аудитории (RFC 9068). Токен, отчеканенный для кого-то другого, получает 401, точка.
Параметр resource в запросе на авторизацию (URL-кодированный)
&resource=https%3A%2F%2Fmcp.example.com
Канонические URI строги: https://mcp.example.com и https://mcp.example.com:8443/mcp допустимы; mcp.example.com (без схемы) и https://mcp.example.com#frag (фрагмент) — нет. Для совместимости предпочитайте форму без завершающего слэша.
Запутанный заместитель: никогда не пробрасывайте токен
Это та ошибка, которая превращает благонамеренный MCP-сервер в прокси атакующего. Это та же самая проблема запутанного заместителя из безопасности агентов, заострённая до одного конкретного правила.
MCP-серверу часто нужно вызвать вышестоящий API (GitHub, сервис базы данных, другой SaaS). Возникает соблазн взять токен, который передал вам клиент, и переслать его наверх. Не делайте этого. Спецификация категорична: MCP-сервер НЕ ДОЛЖЕН пробрасывать токен, полученный от клиента.
Почему это опасно: токен клиента был выдан с вашим сервером в качестве аудитории. Если вы перешлёте его, вышестоящий API может доверять ему так, будто он пришёл от вас, или предположить, что вы уже проверили его — и теперь токен, ограниченный одним переходом, выполняет работу за два перехода, вне чьей-либо модели согласия.
- Если ваш MCP-сервер вызывает вышестоящий API, он действует как ОТДЕЛЬНЫЙ OAuth-клиент для этого API и получает СВОЙ СОБСТВЕННЫЙ токен от вышестоящего сервера авторизации. Два независимых токена, две независимые аудитории. Токен клиента останавливается у вашей двери.
Предполётный чек-лист усиления безопасности
Прежде чем удалённый MCP-сервер коснётся публичного интернета:
- Все эндпоинты AS ДОЛЖНЫ работать по HTTPS. Redirect URI ДОЛЖНЫ быть HTTPS или localhost — ничего иного.
- Отклоняйте любой токен, выданный не специально для этого сервера. Это единственная проверка, которая останавливает переиспользование токенов между сервисами.
- Клиенты ДОЛЖНЫ использовать PKCE, чтобы перехваченный код авторизации был бесполезен без соответствующего верификатора.
- AS ДОЛЖЕН точно сопоставлять redirect URI с предварительно зарегистрированными значениями, а клиенты ДОЛЖНЫ использовать и проверять параметр state — оба защищают от фишинга через открытые редиректы.
- Выдавайте короткоживущие токены доступа, чтобы ограничить ущерб от утечки; для публичных клиентов ротируйте refresh-токены. Храните токены безопасно и никогда не логируйте их.
- Токены идут в заголовок Authorization, никогда в строку запроса, где они попали бы в логи и referrer'ы.
- Привязка к аудитории — это транспортный шлюз; всё равно применяйте наименьшие привилегии, песочницу и человека в контуре из /docs/security/securing-agents. Аутентификация говорит КТО — она не говорит, что запрос безопасен.
Проверьте себя
Проверьте себя
0/4Источники и дополнительное чтение
- Спецификация авторизации MCP (2025-06-18) — нормативный поток, роли и требования MUST/SHOULD, которые резюмирует эта страница.
- Рекомендации по безопасности MCP — проброс токена, запутанный заместитель и почему они запрещены.
- RFC 8707 — Resource Indicators for OAuth 2.0 — параметр
resourceи привязка к аудитории. - RFC 9728 — OAuth 2.0 Protected Resource Metadata — как сервер ресурсов объявляет свои серверы авторизации.
- RFC 8414 — OAuth 2.0 Authorization Server Metadata и RFC 7591 — Dynamic Client Registration.
- Черновик OAuth 2.1 — PKCE, безопасность коммуникаций и требования к обращению с токенами.
- Связанное на AILmanac: Защита агентов и инструментов · Внедрение промптов · MCP в Claude Code.