Перейти до вмісту

Машинний переклад. Авторитетним джерелом є англійська версія; перевірка носієм мови ще не виконана.

MCP

Керування серверами MCP за допомогою RFC 9728 та RFC 8707 — що насправді працює

Автор Olivares AI 9 хв читання

Ви підключаєте Claude Code до сервера MCP. Сервер вимагає автентифікації. Ваш клієнт відправляє запит; сервер відповідає зі 401 Unauthorized та заголовком WWW-Authenticate: Bearer, який містить URL resource_metadata. Далі відбувається процес OAuth 2.1, визначений трьома RFC та набором специфічних розширень MCP, які разом вирішують одну з складніших проблем у безпеці агентів: гарантують, що токен, отриманий для спілкування з цьому сервером MCP, не може бути використаний повторно з тим сервером.

У цьому дописі відслідковується весь процес — що говорить специфікація, що насправді надають RFC і де на практиці приховані складнощі у сфері безпеки.

Процес: від 401 до токена, прив’язаного до ресурсу

Модель авторизації MCP (ревізія 2025-11-25, збережена без змін у кандидаті на випуск — заморожена 2026-05-21 — для остаточної специфікації, запланованої на 2026-07-28) є двофазним процесом. Фаза 1 — виявлення: 401, що несе WWW-Authenticate: Bearer resource_metadata="...", повідомляє клієнту, що цей сервер захищений OAuth, і де знайти його метадані. Фаза 2 — авторизований доступ: використати метадані, виявити сервер авторизації, отримати токен, прив’язаний до цього конкретного сервера, і застосувати його.

Конкретні кроки:

  1. 401 + WWW-Authenticate: Сервер MCP відхиляє неаутентифікований запит. Параметр resource_metadata у виклику посилається на документ метаданих захищеного ресурсу сервера.

  2. PRM отримання (RFC 9728): Клієнт виконує GET на URL /.well-known/oauth-protected-resource. Відповідь є JSON-документом, що повідомляє канонічний URI ресурсу сервера, сервери авторизації, які його захищають, та області, які підтримує ресурс. Клієнт перевіряє, що поле resource у документі відповідає серверу, до якого він хотів звернутися — невідповідність є сигналом імітації і повинна бути відхилена.

  3. AS виявлення (RFC 8414): Використовуючи URL видавця з масиву authorization_servers PRM, клієнт проходить через заздалегідь відомі кандидати (/.well-known/oauth-authorization-server, потім /.well-known/openid-configuration), щоб отримати метадані сервера авторизації. Видавець у поверненому документі повинен бути байт-ідентичний видавцю, для якого він отриманий — не «еквівалент після нормалізації», не «достатньо близький». Байт-ідентичний.

  4. Отримання токена (RFC 8707): Клієнт запитує токен з кінцевої точки токена AS, включаючи resource=<canonical server URI> як у запиті на авторизацію, так і в запиті токена. Це прив’язує аудиторію токена до цього конкретного сервера MCP. AS видає токен, аудиторія якого — цей ресурс, і клієнт пред’являє його серверу.

  5. Авторизований доступ: Клієнт використовує токен для виклику методів читання-інспекції сервера MCP (tools/list, resources/list тощо). Токен підтверджує, що клієнт авторизований; індикатор ресурсу підтверджує, що токен виданий для цього сервера.

Що насправді надає RFC 9728

RFC 9728 (Метадані захищеного ресурсу) є механізмом виявлення. Він відповідає на питання: «який сервер авторизації захищає цей ресурс і що він очікує?» Він не автентифікує сервер, не перевіряє токен і не застосовує контроль доступу. Це окремі питання.

Документ PRM має одне обов’язкове поле (resource) та набір необов’язкових, серед яких authorization_servers є найважливішим. Специфікація MCP посилює необов’язковість у RFC: необхідний щонайменше один сервер авторизації. Документ PRM, який не містить жодного, вважається помилкою.

Поле resource є канонічним URI захищеного ресурсу. Клієнт повинен порівняти його з сервером, до якого він планував звернутися, і відхилити у разі невідповідності:

// RFC 9728 §3.3: Значення ресурсу PRM МАЄ ідентифікувати захищений ресурс
// , до якого звертається клієнт — просте порівняння рядків із
// індикатором ресурсу, до якого цей клієнт прив'язує свої токени.
if prm.Resource != c.resource {
    return authServerMetadata{}, fmt.Errorf(
        "mcp: oauth: protected resource metadata declares resource %q, "+
        "expected %q (RFC 9728 §3.3 reject)", prm.Resource, c.resource)
}

Ця перевірка запобігає класу атак, коли шкідливий або неправильно налаштований документ PRM претендує на представлення іншого ресурсу. Порівняння не нормалізується — це пряме порівняння рядків із канонічним URI ресурсу, який клієнт обчислив для наданого йому URL сервера.

Індикатори ресурсів RFC 8707 — чому має значення прив’язка токенів

Без індикаторів ресурсу, токен доступу, отриманий від AS, потенційно може бути використаний на будь-якому сервері ресурсів, який захищає AS. Якщо у вас є два MCP сервери — наприклад, сервер документації тільки для читання та пісочниця для виконання коду — обидва за одним постачальником ідентичності, токен, отриманий для одного, може бути представлений іншому. Це проблема “заплутаного заступника”.

RFC 8707 вирішує це, додаючи параметр resource до запитів авторизації та токенів. AS видає токен, аудиторія якого явно є URI цього ресурсу. Сервер ресурсів, що коректно перевіряє токени, відхиляє токен, якщо його аудиторія не відповідає його власній ідентичності.

На практиці запит токена включає індикатор ресурсу разом із грантом:

form := url.Values{
    "grant_type": {"client_credentials"},
    "resource":   {c.resource}, // RFC 8707 — прив’язати токен до аудиторії
}

Це з’являється у кожному типі гранту, який використовує конектор: облікові дані клієнта, викуп коду авторизації та ротація токена оновлення. Індикатор ресурсу не є необов’язковим — він присутній у кожному запиті токена, тому кожен токен за своєю конструкцією прив’язаний до аудиторії.

Перевірка ідентичності байтів видувача

Найбільш критична для безпеки перевірка в усьому потоці також є найпростіша для формулювання і найлегше помилитися: значення видувача у метаданих AS має бути байт-ідентичним з видувачем, який клієнт використав для створення відомого URL.

Не рівнозначне без урахування регістру. Не еквівалентне після нормалізації схеми. Не таке ж саме після видалення слешу в кінці. Байтово-ідентичне. Розділ 3.3 RFC 8414 чітко це визначає, і специфікація MCP успадковує цю вимогу.

// discoverASMetadata: ВІДХИЛЯЄ документ, чий issuer не
// ПОБАЙТОВО ІДЕНТИЧНИЙ issuer, для якого його було отримано (RFC 8414 §3.3).
// Несумісний документ є сигналом підміни.
if as.Issuer != issuer {
    return authServerMetadata{}, fmt.Errorf(
        "mcp: oauth: AS metadata at %s declares issuer %q, "+
        "expected %q (RFC 8414 §3.3 reject)", cand, as.Issuer, issuer)
}

Чому так суворо? Тому що атакуючий, який контролює DNS або знаходиться на мережевому шляху, може подати метадані, які вказують на його власну токен-ендпойнт, при цьому претендуючи на роль легітимного емітента. Якщо клієнт нормалізує емітента перед порівнянням, https://auth.example.com і https://AUTH.example.com збіглися б — і документ атакуючого був би прийнятий. Порівняння байт-в-байт закриває цю уразливість.

Та сама дисципліна застосовується до RFC 9207 (перевірка видавця відповіді авторизації). Коли клієнт запускає процес авторизації за кодом, він записує видавця з перевірених метаданих AS. При редиректі назад параметр iss у відповіді повинен збігатися з цим записаним значенням — знову ж таки, побайтно, без нормалізації — перед тим, як код авторизації буде використано. Це захист від змішування: без нього зловмисний AS може перехопити код і дозволити клієнту використати його на токен-ендпойнті злочинця.

Ідентифікація клієнта: CIMD замінює DCR

Специфікація MCP визначає пріоритетний порядок, за яким клієнт ідентифікує себе перед сервером авторизації:

  1. Попередньо зареєстровані облікові дані — оператор заздалегідь надає client_id та client_secret
  2. CIMD (Документи метаданих клієнта) — клієнт розміщує JSON-документ за HTTPS-адресою; ця адреса є client_id
  3. Динамічна реєстрація клієнта (RFC 7591) — клієнт реєструє себе на кінцевій точці реєстрації AS
  4. Запитувати користувача — не застосовується для безголових агентів

DCR застарілий у кандидати на випуск для фінальної специфікації, запланованої на 2026-07-28, на користь CIMD. Причина операційна: DCR створює постійний стан клієнта на сервері авторизації. Кожен агент, який реєструється, залишає пару client_id/client_secret, яку AS має зберігати, і ніхто їх не відслідковує і не відкликає. Для флоту агентів це неконтрольоване розростання облікових даних.

CIMD інвертує модель. Клієнт розміщує документ за URL, який він контролює. AS отримує документ, коли потрібно підтвердити клієнта, кешує його відповідно до заголовків кешування HTTP і нічого не зберігає постійно. Обертання ключа — це оновлення документа. Виведення клієнта з експлуатації — це видалення документа. Жодних осиротілих реєстрацій на AS не накопичується.

Компроміс: CIMD вимагає від клієнта запуску HTTPS-ендпоінта. Для самостійно розміщеної площини управління це природньо — площина вже запускає HTTPS-сервіси. Для CLI-інструмента на ноутбуці розробника це менш природньо, саме тому заздалегідь зареєстровані облікові дані залишаються першим варіантом у порядку пріоритету.

Ідентифікатор CIMD не може містити спільний секрет (URL документа є публічним — секрет у ньому буде витоком облікових даних). Аутентифікація клієнта використовує private_key_jwt (RFC 7523): клієнт підписує короткоживучий JWT приватним ключем, публічний аналог якого опублікований у полі jwks документа CIMD.

Правило «ніколи не пропускати»

Структурний захист, який легко пропустити: конектор використовує лише токен, який отримав самостійно, для конкретного сервера, через процес виявлення, описаний вище. Він ніколи не приймає токен від третьої сторони і не пересилає його на сервер MCP. Він ніколи не читає токен із вхідного запиту і не передає його далі.

Це захист «заплутаного заступника» на рівні протоколу. Якби клієнт пересилав отримані токени, зловмисник міг би подати токен, обмежений ресурсом з низькими правами, і змусити клієнта передати його до ресурсу з високими правами (або навпаки — отримати токен з високими правами, обманом змусивши клієнта подати його на сервер, керований зловмисником). Оскільки конектор отримує власні токени і ніколи не зачіпає чужі, його не можна змусити діяти як ретранслятор токенів.

Передача токена не просто не рекомендується — вона структурно неможлива. HTTP-клієнт конектора для потоків OAuth відокремлений від будь-якого обробника вхідних запитів. Не існує коду, який читає маркер доступу з вхідного запиту і записує його в вихідний.

SSRF: захист від повторного призначення DNS

Кожен URL-адреса для метаданих та кінцевих точок токенів, яку отримує конектор, захищена від SSRF на двох рівнях. Перший — це попередня перевірка: URL має бути HTTPS (за винятком локального циклу для розробки) і не повинен бути літеральною зарезервованою IP-адресою.

Другий — це перевірка під час виклику, яка закриває вразливість TOCTOU при повторному прив’язуванні DNS. Ім’я хоста, яке перетворюється на публічну IP-адресу під час попередньої перевірки, може перев’язатися на приватну IP-адресу до моменту виклику сокета. HTTP-клієнт конектора встановлює функцію net.Dialer.Control, яка перевіряє конкретну вирішену IP-адресу під час підключення і відхиляє будь-яку зарезервовану адресу. Це авторитетна перевірка — попередня перевірка швидка для очевидних випадків, але перевірка під час виклику має значення.

Підвищення рівня області

Сервер MCP може відповісти на запит WWW-Authenticate: Bearer error="insufficient_scope" scope="mcp:tools:list mcp:resources:read". Специфікація (SEP-835/SEP-2350) визначає, як клієнт обробляє це: обчислити об’єднання областей, які він раніше запитував, та областей, які сервер щойно оскаржив, а потім повторно отримати токен із цим розширеним набором. Об’єднання зберігає раніше надані дозволи та додає нові. Підвищення прав відбувається один раз — вдруге виклик недостатньої області за тим самим запитом не повторюється, щоб уникнути нескінченних циклів.

Серверу дозволено бути безстанним у своєму виклику: він вказує тільки ті області, які потрібні для поточної операції, а не повний набір, який клієнт міг запитувати раніше. Накопичення на стороні клієнта дозволяє це працювати без того, щоб сервер відстежував історію областей для кожного клієнта.

Що це означає на практиці

Потік OAuth для серверів MCP добре визначений і, якщо його впроваджувати строго, він усуває реальні вектори атак: повторне використання токенів між серверами, підробка метаданих, SSRF через перебиндування DNS та неконтрольоване розповсюдження облікових даних клієнтів. Перевірка видавця з ідентичними байтами, індикатор ресурсу в кожному запиті токена та структурне правило без пропуску є основними засобами захисту. Це не функції, які можна впровадити частково — кожна з них є суворою перевіркою, яка закінчується відмовою у разі невдачі.

Складніша проблема — операційна. Команди, що розгортають сервери MCP, повинні:

  • Опублікувати дійсний документ PRM на /.well-known/oauth-protected-resource з полем resource, яке відповідає канонічному URI їхнього сервера
  • Використовуйте AS, що підтримує індикатори ресурсів — багато провайдерів ідентичності досі цього не роблять або трактують параметр resource як рекомендаційний, а не як обов’язковий для аудиторії
  • Перейдіть з DCR на CIMD до того, як застарівання перетвориться на видалення, або заздалегідь явно зареєструйте облікові дані
  • Прив’яжіть облікові дані до видавця, щоб уникнути непомітного повторного використання між видавцями при зміні топології AS

Документація конектора MCP охоплює оперативну конфігурацію, а модель безпеки описує, як токен, прив’язаний до ресурсу, вписується у ширшу карту доступу.

Пов’язані матеріали

Часто задавані запитання

Чи гарантує RFC 9728, що сервер MCP, з яким я спілкуюся, є легітимним?

Ні. RFC 9728 дозволяє клієнту дізнатися, який сервер авторизації захищає ресурс і які області він вимагає, але це метадані про ресурс, а не доказ його ідентичності. Сертифікат TLS підтверджує ім'я хоста сервера; PRM повідомляє, як автентифікуватися на ньому. Обидва доповнюють одне одного: PRM без перевірки TLS — це виявлення проти неперевіреного хоста, а TLS без PRM залишає клієнта в здогадах, як отримати токен.

Чому динамічна реєстрація клієнтів застаріла у специфікації MCP, якщо вона все ще працює?

DCR створює стійкий стан клієнта на сервері авторизації — client_id і client_secret мають зберігати та керувати AS. Для флоту безголових агентів це стає проблемою неконтрольованого розповсюдження облікових даних: кожен агент реєструє себе, і ніхто не відслідковує або не оновлює ці реєстрації. CIMD (Документи метаданих ідентифікатора клієнта) замінює це на розміщений документ, який контролює клієнт, і AS отримує його за потреби — ніякого стійкого стану на AS, жодних покинутих реєстрацій, а обертання ключів відбувається через оновлення документа, а не повторну реєстрацію.

Подивіться, до чого мають доступ ваші агенти

Olivares AI — це відкрита self-hosted платформа для вашого AI-середовища. Розгорніть її на власній інфраструктурі та отримайте карту доступу, про яку давно просять ваші команди безпеки й платформи.