Field note

Аутентификация и авторизация AI-агентов: в чём разница

Разбираем разницу между аутентификацией и авторизацией AI-агентов: identity, OAuth, scopes, политики доступа, MCP и коды 401 и 403.

Аутентификация и авторизация AI-агентов: в чём разница

Обновлено 1 августа 2026 года

Аутентификация AI-агента подтверждает, кто отправил запрос и каким приватным ключом владеет отправитель. Авторизация определяет, что этому агенту разрешено делать от имени пользователя, с конкретным ресурсом, инструментом и параметрами операции. Валидная подпись или OAuth-токен сами по себе не дают неограниченного доступа.

Коротко

  • Аутентификация агента отвечает на вопрос: «Кто отправил запрос и каким приватным ключом он владеет?»
  • OAuth-авторизация показывает, какие полномочия пользователь, приложение или другой субъект передал клиенту.
  • Авторизация действия определяет, можно ли выполнить конкретную операцию с указанными параметрами.
  • Валидная подпись доказывает identity отправителя, но не безопасность принятого им решения.
  • Обычный OAuth bearer-токен может использовать любой процесс, получивший его значение, поэтому он не всегда подтверждает identity агента.
  • Перед выполнением MCP-инструмента серверу обычно нужно проверить подпись, токен, scopes, целевой ресурс, параметры операции, replay status и политику доступа.
  • Итоговое решение должно выглядеть как allowed, а не просто как verified.

Оглавление

  1. Главное различие между аутентификацией и авторизацией
  2. Термины за 30 секунд
  3. Что доказывает аутентификация AI-агента
  4. Что означает авторизация AI-агента
  5. Где в этой модели находится OAuth
  6. Identity агента и пользователя
  7. Почему scopes недостаточно
  8. Step-up authorization для опасных действий
  9. Три практических сценария
  10. Матрица принятия решения
  11. Аутентификация и авторизация в MCP
  12. 401 и 403
  13. Почему решение нельзя оставлять модели
  14. Рекомендуемая архитектура
  15. Пример с AgentBouncer
  16. Распространённые ошибки
  17. FAQ

AI-агент отправляет правильно подписанный запрос:

text
POST /mcp
tools/call → delete_production_database

Сервер проверяет подпись и подтверждает: запрос действительно поступил от известного агента.

Можно ли выполнять операцию?

Нет — по крайней мере, не только на основании успешной аутентификации.

Подпись доказывает, кто отправил запрос. Она не сообщает:

  • разрешил ли пользователь удаление базы данных;
  • имеет ли агент необходимые полномочия;
  • допускает ли политика вызов этого инструмента;
  • соответствует ли операция текущей задаче;
  • не попал ли агент под prompt injection;
  • не был ли запрос перехвачен и отправлен повторно.

Поэтому аутентификация и авторизация AI-агентов — связанные, но независимые уровни безопасности.

Упрощённо разница выглядит так:

text
Authentication:
«Кто отправил запрос?»

User authorization:
«Какие полномочия были делегированы клиенту?»

Action policy:
«Можно ли выполнить именно это действие прямо сейчас?»

Для автономных агентов одной проверки identity почти никогда не достаточно.


Главное различие между аутентификацией и авторизацией

Аутентификация обычно выполняется раньше авторизации.

Сначала сервер устанавливает, кто обращается к ресурсу. Затем определяет, какие действия разрешены установленному субъекту.

ПроверкаГлавный вопросПример
Аутентификация агентаКто отправил запрос?Подпись создана зарегистрированным ключом
Пользовательская авторизацияКакие права были делегированы?Токен содержит scope calendar.events.create
Авторизация действияРазрешена ли конкретная операция?Можно создать событие, но нельзя удалить календарь
Проверка целостностиНе изменился ли запрос?Content-Digest соответствует body
Replay protectionНе использовали ли запрос раньше?Fingerprint отсутствует в replay cache
Оценка рискаНужны ли дополнительные ограничения?Крупный платёж требует подтверждения

Это не альтернативные способы защиты. Каждый уровень закрывает отдельную проблему.

Удобно представлять итоговую проверку как три независимых результата:

json
{
  "verified": true,
  "userAuthorized": true,
  "allowed": false
}

Здесь:

  • verified — агент прошёл криптографическую проверку;
  • userAuthorized — предъявлена действительная пользовательская авторизация;
  • allowed — политика запретила конкретное действие.

Именно allowed должен определять, будет ли вызван инструмент.


Термины за 30 секунд

ТерминЗначение
AuthenticationПроверка identity субъекта или владения credential
AuthorizationОпределение разрешённых субъекту действий
Access tokenCredential, представляющий выданные клиенту полномочия
ScopeКласс разрешений, например files.read или payments.create
PolicyПравило, разрешающее или запрещающее конкретную операцию

Дополнительные понятия — bearer-токены, proof of possession, replay protection и HTTP Message Signatures — подробно разобраны в предыдущей статье: «Аутентификация AI-агентов: identity, authorization и контроль доступа».


Что доказывает аутентификация AI-агента

Практическая аутентификация AI-агента может включать три шага:

  1. Сервер получает подписанный запрос.
  2. Проверяет подпись с помощью публичного ключа.
  3. Связывает ключ с зарегистрированным агентом, провайдером или развёртыванием.

Результат может выглядеть так:

json
{
  "verified": true,
  "agent": "https://agent.example.com",
  "provider": "Example AI",
  "keyId": "agent-key-2026-08",
  "trustTier": 2
}

Сервер знает, что запрос подписан владельцем соответствующего приватного ключа.

Но поле:

json
{
  "verified": true
}

не означает:

json
{
  "allowed": true
}

RFC 9421 позволяет получателю убедиться, что подписанные компоненты HTTP-сообщения не изменились по пути и подписаны владельцем соответствующего ключа. Готовую модель доверия, бизнес-политику и правила авторизации спецификация не задаёт.

Что аутентификация не доказывает

Успешная проверка identity не подтверждает, что:

  • пользователь разрешил запрашиваемое действие;
  • агенту доступен указанный MCP-инструмент;
  • сумма платежа укладывается в лимит;
  • получатель входит в allowlist;
  • решение агента не вызвано вредоносной инструкцией;
  • запрос не является повтором ранее принятого сообщения;
  • ключ не принадлежит скомпрометированному развёртыванию;
  • операция соответствует первоначальной цели пользователя.

Аутентификация предоставляет данные для последующей авторизации, но не заменяет её.

Для запросов с body также важна проверка целостности через Content-Digest. Подробнее о подписываемых компонентах и replay protection — в руководстве по аутентификации AI-агентов.


Что означает авторизация AI-агента

Авторизация определяет, какие операции субъект может выполнять после проверки обязательных credentials.

Решение может зависеть от комбинации:

text
agent identity
+ user identity
+ OAuth scopes
+ resource
+ action
+ tool
+ parameters
+ risk
+ current context

Например, один и тот же агент может получить разные решения:

text
calendar.events.read
→ ALLOW

calendar.events.create
→ ALLOW

calendar.calendars.delete
→ DENY

payments.send, amount=20 USD
→ ALLOW

payments.send, amount=5000 USD
→ REQUIRE_USER_CONFIRMATION

Это и есть action-level authorization: сервер оценивает не только субъект и общий класс разрешений, но и конкретное действие.

Конвенция именования в примерах

Чтобы не смешивать scopes, внутренние действия и названия инструментов, далее используется единый стиль:

text
OAuth scope → точки:
payments.create

Policy action → двоеточие:
payments:create

MCP tool → snake_case:
payments_send

Эти значения связаны по смыслу, но не обязаны совпадать буквально.


Где в этой модели находится OAuth

OAuth предназначен для предоставления ограниченного доступа к защищённому ресурсу.

В пользовательском сценарии access token может представлять полномочия, которые владелец ресурса передал клиенту:

json
{
  "iss": "https://auth.example.com",
  "sub": "user-1842",
  "aud": "https://mcp.example.com",
  "scope": "calendar.read calendar.events.create",
  "exp": 1785603600
}

Сервер должен проверить как минимум:

  • доверенного издателя iss;
  • целевой ресурс или audience;
  • срок действия токена;
  • необходимые scopes;
  • другие обязательные claims и ограничения;
  • криптографическую подпись JWT либо результат introspection — в зависимости от формата токена и используемого профиля.

OAuth определяет модель делегированного доступа, но access token не обязательно представляет человека. Он может быть выпущен для workload, приложения или client_credentials flow.

Интерпретировать sub как пользователя можно только тогда, когда это гарантируют профиль токена и конфигурация authorization server.

OAuth 2.0 описывает access token как credential, представляющий выданную авторизацию, а scope — как способ ограничить предоставляемый клиенту доступ (RFC 6749, разделы 1.4 и 3.3).

Почему OAuth-токен не всегда является identity агента

Рассмотрим запрос:

http
Authorization: Bearer USER_ACCESS_TOKEN

Обычный bearer-токен можно использовать без дополнительного доказательства владения ключом.

Если значение токена получат:

  • вредоносный процесс;
  • другой агент;
  • скомпрометированный MCP-сервер;
  • сотрудник с доступом к журналам;
  • сторонняя интеграция,

сервер может увидеть корректный токен, но не узнать, какой программный субъект на самом деле его предъявил.

Получается:

text
OAuth access token
→ сообщает о предоставленном доступе

Подпись запроса
→ подтверждает владение ключом отправителя

Эти данные дополняют друг друга.

Для уменьшения риска кражи токенов применяются sender-constrained механизмы, например DPoP или mTLS-bound access tokens. DPoP связывает токен с публичным ключом и требует доказательства владения соответствующим приватным ключом (RFC 9449).

При этом proof of possession не отвечает на главный бизнес-вопрос: разрешено ли выполнить конкретную операцию.


Identity агента и identity пользователя — не одно и то же

Типичный запрос автономного агента может содержать две независимые identity:

text
Agent identity:
подтверждается HTTP Message Signature

User authorization:
подтверждается OAuth access token

Например:

http
POST /mcp HTTP/1.1
Host: mcp.example.com
Content-Type: application/json
Authorization: Bearer USER_ACCESS_TOKEN
Content-Digest: sha-256=:BODY_DIGEST:
Signature-Agent: "https://agent.example.com"

Signature-Input: sig1=("@method" "@authority" "@path" "content-digest" "signature-agent");
  created=1785600000;
  expires=1785600060;
  keyid="agent-key-2026-08";
  tag="web-bot-auth"

Signature: sig1=:BASE64_SIGNATURE:

Поле tag="web-bot-auth" связывает подпись с ожидаемым назначением запроса. Сервер может отклонить криптографически корректную подпись, если endpoint ожидает другой intent tag.

После проверки сервер получает два контекста:

json
{
  "agent": {
    "identity": "https://agent.example.com",
    "verified": true
  },
  "user": {
    "subject": "user-1842",
    "scopes": [
      "documents.read",
      "documents.summarize"
    ]
  }
}

Но после этого всё равно требуется третье решение:

json
{
  "action": "documents:export",
  "tool": "documents_export",
  "destination": "https://external.example.com/upload",
  "allowed": false,
  "reason": "external_destination_not_allowed"
}

Пользователь разрешил читать документы и создавать краткие пересказы. Это не означает, что агенту разрешено экспортировать исходные файлы на внешний сервер.

Разделение identity AI-агента, пользователя и итогового решения policy engine


Почему scopes недостаточно

Scopes задают границы выданного доступа, но обычно остаются шире отдельной операции.

Scope:

text
payments.create

может разрешать создание платежей. Но он не отвечает на вопросы:

  • какую сумму можно отправить;
  • в какой валюте;
  • каким получателям;
  • сколько операций разрешено за день;
  • можно ли создавать международные переводы;
  • нужно ли подтверждение пользователя;
  • допустим ли вызов от нового агента;
  • соответствует ли операция задаче пользователя.

Для этого требуется policy engine.

Концептуальная политика может выглядеть так:

text
IF
  agent.trustTier <= 2
  AND user.scope includes "payments.create"
  AND action == "payments:create"
  AND tool == "payments_send"
  AND amount <= 100
  AND recipient in user.allowlist
  AND replay == false
THEN
  ALLOW
ELSE
  REQUIRE_CONFIRMATION or DENY

Это псевдокод, а не синтаксис конкретного продукта. На практике такую проверку реализуют в Cedar, Open Policy Agent, собственном policy engine или в политиках проекта AgentBouncer.

Scope — входное условие авторизации, а не её окончательный результат.


Step-up authorization для опасных действий

Не каждому инструменту сразу нужны максимальные OAuth scopes.

Например, при первом подключении клиент может запросить только:

text
documents.read

Если позднее пользователь просит экспортировать документ, сервер отвечает:

http
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope",
  scope="documents.export",
  resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
Cache-Control: no-store

Клиент запускает повторную авторизацию и запрашивает расширенный набор полномочий.

В актуальной спецификации MCP клиент должен вычислять объединение ранее запрошенных и новых scopes:

text
previous scopes:
documents.read

challenged scopes:
documents.export

new authorization request:
documents.read documents.export

Это предотвращает ситуацию, когда step-up выдаёт новый токен для экспорта, но случайно теряет ранее предоставленное право чтения.

Серверу, в свою очередь, рекомендуется возвращать сразу все scopes, необходимые для текущей операции, а не запрашивать их по одному в нескольких циклах. Клиент должен ограничивать количество повторных попыток, чтобы не попасть в бесконечный authorization loop. Эти правила закреплены в step-up flow MCP 2026-07-28.

Важно: новый scope всё ещё не гарантирует выполнение операции.

Например:

text
documents.export
+ destination in corporate allowlist
→ ALLOW

documents.export
+ destination controlled by unknown third party
→ DENY

Step-up authorization расширяет делегированные полномочия, но не отменяет action-level policy.


Три сценария, показывающих разницу

Сценарий 1. Известный агент без OAuth-токена

Агент вызывает инструмент:

text
calendar_create_event

Подпись корректна, identity известна. Но политика требует пользовательской авторизации со scope:

text
calendar.events.create

Результат:

json
{
  "verified": true,
  "userAuthorized": false,
  "allowed": false,
  "reason": "oauth_token_required"
}

Практический ответ:

http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
  resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
  scope="calendar.events.create"
Cache-Control: no-store

Агент аутентифицирован, но обязательный пользовательский credential отсутствует.

Сценарий 2. Корректная подпись и недостаточный scope

Агент предъявляет токен со scope:

text
calendar.read

Но запрашивает:

text
calendar.events.create

Результат:

json
{
  "verified": true,
  "userAuthorized": true,
  "allowed": false,
  "reason": "insufficient_scope"
}

Ответ:

http
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer
  error="insufficient_scope",
  scope="calendar.events.create",
  resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
Cache-Control: no-store

Credentials действительны, но не дают требуемого разрешения.

Сценарий 3. Все credentials корректны, но действие опасно

Агент имеет:

  • валидную подпись;
  • известную identity;
  • OAuth-токен;
  • scope payments.create.

Он запрашивает:

json
{
  "tool": "payments_send",
  "amount": 5000,
  "currency": "USD",
  "recipient": "new-recipient"
}

Политика допускает автоматические платежи только до 100 долларов, а для новых получателей требуется ручное подтверждение.

Результат:

json
{
  "verified": true,
  "userAuthorized": true,
  "allowed": false,
  "reason": "user_confirmation_required"
}

Текущий запрос не выполняется. Сервер может вернуть 403 с прикладным кодом ошибки и создать отдельный confirmation flow:

http
HTTP/1.1 403 Forbidden
Content-Type: application/json
Cache-Control: no-store
json
{
  "error": "user_confirmation_required",
  "confirmationId": "confirm-7f3c",
  "expiresIn": 300
}

Подтверждение должно быть связано с точными параметрами операции:

text
tool
+ amount
+ currency
+ recipient
+ expiration

Нельзя принимать абстрактное подтверждение «разрешить следующий платёж», потому что агент сможет изменить параметры между подтверждением и выполнением.


Матрица принятия решения

Подпись агентаOAuthScopes и policyРезультатHTTP
Некорректна или отсутствуетЛюбой—invalid_agent_credentials401
КорректнаНе требуетсяPolicy разрешаетВыполнить200
КорректнаОтсутствуетOAuth обязателенoauth_required401
КорректнаНекорректен или просрочен—invalid_token401
КорректнаКорректенScopes недостаточныinsufficient_scope403
КорректнаКорректенPolicy запрещаетagent_access_denied403
КорректнаКорректенНужно подтверждениеuser_confirmation_required403
КорректнаКорректенВсё разрешеноВыполнить200
Replay обнаруженЛюбой—replay_detected401
ЛюбаяЛюбойПревышен rate limitrate_limit_exceeded429
Не удалось выполнить обязательную проверкуЛюбойРешение неизвестноverification_unavailable503

Для временных ошибок полезно добавлять:

http
Retry-After: 30

Подробный словарь ошибок, включая 429 и 503, приведён в статье «Аутентификация AI-агентов: identity, authorization и контроль доступа».

Главное правило интеграции:

ts
if (!verification.allowed) {
  deny();
}

Проверять только verified недостаточно.

В AgentBouncer эти поля разделены намеренно: verified отражает результат криптографической проверки, а allowed — окончательное решение политики проекта. В режиме мониторинга возможна даже комбинация verified: false и allowed: true, поэтому ориентироваться нужно только на итоговый результат. (agentbouncer.io)


Аутентификация и авторизация в MCP

MCP Authorization регулирует доступ клиентов к удалённым MCP-серверам через OAuth.

В актуальной редакции MCP от 28 июля 2026 года:

  • authorization остаётся необязательной возможностью протокола;
  • для HTTP-транспорта используется OAuth-модель;
  • MCP-сервер выступает как OAuth resource server;
  • клиент обнаруживает authorization servers через Protected Resource Metadata;
  • клиент должен передавать параметр resource;
  • сервер должен принимать только токены, предназначенные для его ресурса;
  • для authorization code flow требуется PKCE;
  • stdio-серверы не должны применять HTTP OAuth flow и обычно получают credentials из окружения;
  • клиент должен корректно обрабатывать step-up authorization;
  • OAuth credentials должны быть привязаны к issuer выдавшего их authorization server;
  • Dynamic Client Registration сохраняется для совместимости, но помечена deprecated в пользу Client ID Metadata Documents.

Официальная страница MCP Authorization для редакции 2026-07-28 закрепляет проверку audience, Protected Resource Metadata, 401 для отсутствующих или недействительных токенов и 403 для недостаточных scopes.

Что изменилось в MCP 2026-07-28

Редакция 2026-07-28 стала крупнейшим обновлением протокола с момента его запуска. В неё вошли:

  • stateless-ядро без обязательного initialize handshake;
  • удаление protocol-level sessions и Mcp-Session-Id;
  • server/discover для обнаружения версий, capabilities и identity сервера;
  • Multi Round-Trip Requests для получения дополнительных данных;
  • обязательные заголовки Mcp-Method и Mcp-Name для HTTP-маршрутизации;
  • кешируемые результаты списков через ttlMs и cacheScope;
  • формальный extensions framework;
  • усиление OAuth- и OpenID Connect-совместимости;
  • формальный жизненный цикл Active → Deprecated → Removed.

Полный список опубликован в changelog MCP 2026-07-28

Переход на новую редакцию не означает, что все реализации 2025-11-25 мгновенно перестают работать. Клиенты и серверы могут сохранять обратную совместимость и согласовывать поддерживаемую версию. Для возможностей, официально получивших статус Deprecated, политика MCP устанавливает не менее двенадцати месяцев до возможного удаления.

Проверка iss и защита от mix-up attacks

В MCP 2026-07-28 усилена проверка issuer в OAuth flow.

Перед перенаправлением пользователя клиент сохраняет ожидаемый issuer из проверенного metadata-документа authorization server. При получении authorization response клиент проверяет параметр iss по правилам RFC 9207.

Если authorization server сообщает:

json
{
  "authorization_response_iss_parameter_supported": true
}

но не возвращает iss, клиент должен отклонить response.

Если iss присутствует, его нужно сравнить с сохранённым issuer точным строковым сравнением — без автоматического изменения регистра, удаления завершающего слеша или нормализации URL.

Эта проверка снижает риск OAuth authorization server mix-up attacks, особенно в MCP-клиентах, которые подключаются ко множеству независимых серверов.

Credentials должны быть привязаны к issuer

Client ID и связанные credentials действуют в контексте authorization server, который их выдал.

Если MCP-ресурс переезжает с одного authorization server на другой, клиент не должен автоматически повторно использовать прежние credentials. Он должен:

  1. обнаружить новый authorization server;
  2. проверить его metadata;
  3. найти credentials, привязанные именно к этому issuer;
  4. при необходимости зарегистрироваться заново.

Это предотвращает случайную отправку client secret, refresh token или других credentials неправильному серверу.

Client ID Metadata Documents вместо DCR

MCP 2026-07-28 рекомендует OAuth Client ID Metadata Documents как основной механизм получения client ID.

Dynamic Client Registration по RFC 7591 по-прежнему может поддерживаться, но в спецификации MCP она отмечена как deprecated и сохранена для обратной совместимости.

Перед началом authorization flow клиент может получить client ID одним из трёх способов:

  1. Client ID Metadata Document;
  2. предварительная регистрация;
  3. Dynamic Client Registration.

DCR нельзя удалять из работающих интеграций без анализа совместимости, однако новые реализации стоит проектировать с поддержкой Client ID Metadata Documents.

Protected Resource Metadata

Protected Resource Metadata стандартизировано RFC 9728. Metadata может сообщать:

  • authorization servers;
  • поддерживаемые scopes;
  • способы передачи bearer-токенов;
  • поддержку DPoP;
  • другие свойства защищённого ресурса.

Пример:

json
{
  "resource": "https://mcp.example.com",
  "authorization_servers": [
    "https://auth.example.com"
  ],
  "scopes_supported": [
    "documents.read",
    "documents.write"
  ],
  "bearer_methods_supported": [
    "header"
  ]
}

Чего MCP OAuth не решает автоматически

OAuth-модель MCP не создаёт универсальную криптографическую identity самого AI-агента.

Она может сообщить серверу:

text
Клиент предъявил токен,
действительный для этого MCP resource,
с указанным набором scopes.

Но для ответа на отдельный вопрос:

text
Какой агент, провайдер или workload
отправил этот HTTP-запрос?

может потребоваться дополнительная проверка:

  • HTTP Message Signatures;
  • mTLS;
  • DPoP-bound access token;
  • workload identity;
  • зарегистрированный signing key.

Это особенно важно, если один пользовательский токен потенциально могут использовать несколько программных участников.

Статус спецификаций на 1 августа 2026 года: актуальная редакция MCP — 2026-07-28. OAuth 2.1 всё ещё является Internet-Draft, а не опубликованным RFC. Текущая редакция IETF — draft-ietf-oauth-v2-1-15 от 2 марта 2026 года со сроком действия до 3 сентября 2026 года.


401 и 403: краткая шпаргалка

Названия HTTP-статусов могут сбивать с толку: 401 Unauthorized используется, когда запросу не хватает допустимых authentication credentials.

Возвращайте 401, если:

  • отсутствует обязательная подпись;
  • подпись не прошла проверку;
  • signing key неизвестен;
  • OAuth-токен отсутствует;
  • токен просрочен, отозван или некорректен;
  • токен предназначен для другого ресурса;
  • DPoP proof не прошёл проверку;
  • обнаружен replay подписанного запроса.
http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
  resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
Cache-Control: no-store

Возвращайте 403, если:

  • credentials уже приняты;
  • не хватает scopes;
  • инструмент недоступен этому агенту;
  • пользователь не имеет доступа к объекту;
  • превышен policy-лимит операции;
  • получатель запрещён;
  • действие требует отдельного подтверждения.
http
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer
  error="insufficient_scope",
  scope="payments.create",
  resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
Cache-Control: no-store

RFC 6750 предусматривает 401 для недействительного bearer-токена и 403 с insufficient_scope, когда предоставленных полномочий недостаточно. Общая семантика статусов определена в RFC 9110.

Расширенный разбор 401, 403, 429 и 503 приведён в первой статье серии.


Почему решение нельзя оставлять самой модели

Можно добавить в system prompt правило:

text
Никогда не отправляй платежи больше 100 долларов.

Но это инструкция для модели, а не надёжная граница безопасности.

Prompt injection может попытаться убедить агента, что:

  • операция уже подтверждена;
  • лимит временно отменён;
  • внешний получатель является внутренним;
  • инструмент нужно вызвать только для проверки;
  • системная инструкция больше не действует.

Если модель после этого отправит запрос, его криптографическая подпись останется валидной.

Поэтому правила, ограничивающие реальные последствия, должны выполняться:

  • в MCP-сервере;
  • в API gateway;
  • в downstream API;
  • в отдельном policy engine;
  • непосредственно перед необратимой операцией.

OWASP рекомендует применять принцип минимальных полномочий, отдельно авторизовывать чувствительные инструменты, проверять параметры tool calls и не полагаться только на инструкции внутри prompt. Для необратимых и дорогостоящих действий также рекомендуется human-in-the-loop.

Модель может предложить действие:

json
{
  "tool": "payments_send",
  "amount": 5000
}

Но доверенный код должен независимо решить:

json
{
  "allowed": false,
  "reason": "transaction_limit_exceeded"
}

Рекомендуемая архитектура

text
AI-агент
  │
  │ Подписанный запрос
  │ OAuth-токен пользователя
  ▼
MCP endpoint или API gateway
  │
  ├── 1. Проверка Content-Digest
  ├── 2. Проверка HTTP Message Signature
  ├── 3. Поиск identity агента
  ├── 4. Проверка timestamps и intent tag
  ├── 5. Replay protection
  ├── 6. Проверка OAuth issuer и audience
  ├── 7. Проверка scopes
  ├── 8. Определение action и tool
  ├── 9. Проверка параметров и лимитов
  └── 10. Итоговая policy evaluation
             │
             ├── DENY
             ├── REQUIRE_CONFIRMATION
             └── ALLOW
                    ▼
                 MCP tool

Все проверки должны выполняться до вызова инструмента.

Для запросов с body проверять Content-Digest следует по исходным байтам до JSON.parse(). RFC 9530 определяет Content-Digest как digest фактического содержимого HTTP-сообщения.

Порядок важен:

text
получить raw request
→ проверить digest
→ проверить подпись
→ проверить replay
→ проверить OAuth
→ применить policy
→ разобрать аргументы
→ выполнить tool

Если приложение сначала изменило или пересериализовало JSON, а затем начало проверять digest, оно уже работает не с теми байтами, которые подписывал отправитель.


Практический пример с AgentBouncer

ts
import {
  createAgentBouncer,
  getRequiredOAuthScopes,
  isOAuthRequired,
  isOAuthScopeDenied,
} from "@agentbouncer/sdk";

const publicOrigin =
  process.env.AGENTBOUNCER_PUBLIC_ORIGIN!;

const resourceMetadataUrl =
  `${publicOrigin}/.well-known/oauth-protected-resource`;

const agentBouncer = createAgentBouncer({
  apiKey:
    process.env.AGENTBOUNCER_API_KEY!,
  publicOrigin,
  validateContentDigest: true,
});

export async function POST(
  request: Request,
): Promise<Response> {
  const verification =
    await agentBouncer.verify({
      request,
      expectedTag: "web-bot-auth",
      action: "payments:create",
      tool: "payments_send",
      forwardAuthorization: true,
    });

  if (!verification.allowed) {
    const headers = new Headers({
      "Cache-Control": "no-store",
    });

    if (isOAuthRequired(verification)) {
      const requiredScopes =
        getRequiredOAuthScopes(verification);

      headers.set(
        "WWW-Authenticate",
        `Bearer resource_metadata="${resourceMetadataUrl}", scope="${requiredScopes.join(" ")}"`,
      );

      return Response.json(
        {
          error: "oauth_required",
          requiredScopes,
        },
        {
          status: 401,
          headers,
        },
      );
    }

    if (isOAuthScopeDenied(verification)) {
      const requiredScopes =
        getRequiredOAuthScopes(verification);

      headers.set(
        "WWW-Authenticate",
        `Bearer error="insufficient_scope", scope="${requiredScopes.join(" ")}", resource_metadata="${resourceMetadataUrl}"`,
      );

      return Response.json(
        {
          error: "insufficient_scope",
          requiredScopes,
        },
        {
          status: 403,
          headers,
        },
      );
    }

    return Response.json(
      {
        error: verification.verified
          ? "agent_access_denied"
          : "invalid_agent_credentials",
        reason: verification.reason,
      },
      {
        status:
          verification.verified
            ? 403
            : 401,
        headers,
      },
    );
  }

  const payload = await request.json();

  return await executePaymentTool(payload);
}

В этом примере:

  • verified показывает, прошла ли криптографическая проверка identity;
  • OAuth helpers определяют, нужен ли токен и хватает ли scopes;
  • allowed содержит окончательное решение;
  • expectedTag проверяет назначение подписи;
  • body разбирается только после всех проверок;
  • выполнение инструмента начинается только при allowed: true.

Актуальная документация AgentBouncer подтверждает, что verify() проверяет Content-Digest до чтения body, по умолчанию пересылает bearer-токен для OAuth-проверки и предоставляет helpers isOAuthRequired(), isOAuthScopeDenied() и getRequiredOAuthScopes(). (agentbouncer.io)


Распространённые ошибки

1. Считать валидную подпись разрешением

Подпись подтверждает владение ключом. Она не выдаёт право на все действия.

Правильно:

ts
if (!verification.allowed) {
  deny();
}

Неправильно:

ts
if (verification.verified) {
  executeTool();
}

2. Считать OAuth-токен identity агента

Bearer-токен сообщает о предоставленном доступе, но не всегда доказывает, какой процесс его предъявил.

Если важно установить identity программного участника, используйте отдельную подпись, workload identity или sender-constrained token.

3. Проверять только scopes

Scope может быть необходимым условием, однако параметры операции всё равно должны пройти policy-проверку.

text
payments.create

не означает:

text
любая сумма
+ любой получатель
+ любая страна
+ неограниченное количество операций

4. Использовать один общий scope

text
mcp.full_access

Такой scope упрощает интеграцию, но мешает разделять:

  • чтение;
  • изменение;
  • удаление;
  • экспорт;
  • административные операции;
  • финансовые действия.

Предпочтительнее:

text
documents.read
documents.write
documents.export
documents.delete

5. Не проверять audience

Токен для другого API нельзя автоматически принимать на MCP-сервере.

MCP требует передавать resource и принимать только токены, выпущенные для конкретного защищённого ресурса. Сервер также не должен принимать или транзитно передавать токены, предназначенные другим сервисам.

6. Передавать пользовательский токен дальше по цепочке

MCP-сервер не должен просто пересылать входящий access token в downstream API.

Для следующего ресурса нужен отдельный токен с подходящим audience. Один из стандартных способов получить его — OAuth Token Exchange по RFC 8693.

Пример:

text
Incoming token:
aud = https://mcp.example.com

Downstream API:
https://payments.example.com

Передавать первый токен напрямую в Payments API нельзя. MCP-сервер может обменять его на более узкий токен:

text
aud = https://payments.example.com
scope = payments.execute

RFC 8693 позволяет resource server обменять входящий токен на новый credential, предназначенный для конкретного downstream-сервиса.

7. Применять политику после вызова инструмента

Проверка, выполненная после удаления данных или отправки платежа, уже не защищает систему.

Авторизация должна происходить непосредственно перед side effect.

8. Разрешать модели самостоятельно отменять ограничения

Модель может предлагать решение, но итоговую авторизацию должен выполнять доверенный код.

Нельзя принимать такие аргументы модели:

json
{
  "limitOverride": true,
  "userAlreadyConfirmed": true
}

без независимой проверки сервером.

9. Не различать пользователя и агента в журналах

Audit log должен сохранять оба контекста:

json
{
  "agent": "https://agent.example.com",
  "agentKeyId": "agent-key-2026-08",
  "userSubject": "user-1842",
  "action": "payments:create",
  "tool": "payments_send",
  "decision": "DENY",
  "reason": "transaction_limit_exceeded",
  "policyVersion": "payments-v12"
}

Для чувствительных операций также полезно сохранять:

  • risk level;
  • confirmation ID;
  • хеш параметров подтверждённой операции;
  • issuer токена;
  • scopes;
  • replay status;
  • версию политики.

10. Использовать verified вместо allowed

Это превращает успешную криптографическую проверку в неограниченное разрешение.

Кроме того, в monitor mode неподтверждённый запрос может временно получить allowed: true, чтобы команда могла изучить реальный трафик перед включением блокировок.

Поэтому:

text
verified
→ результат проверки подписи

allowed
→ итоговое решение для выполнения операции

11. Бесконечно повторять step-up authorization

Если сервер продолжает отвечать insufficient_scope, клиент не должен запускать бесконечные OAuth-циклы.

Нужно:

  • объединять старые и новые scopes;
  • ограничивать число повторов;
  • связывать попытки с ресурсом и операцией;
  • после нескольких неудач считать отказ постоянным.

12. Повторно использовать credentials другого issuer

Client ID, client secret и refresh tokens должны быть привязаны к authorization server, который их выдал.

При смене issuer клиент должен выполнить discovery заново и при необходимости перерегистрироваться.


Часто задаваемые вопросы

В чём разница между аутентификацией и авторизацией AI-агента?

Аутентификация устанавливает identity агента или подтверждает владение credential. Авторизация определяет, какие действия разрешены этому агенту с учётом пользователя, scopes, ресурса, инструмента, параметров и политики доступа.

Является ли OAuth аутентификацией агента?

Не обязательно. OAuth прежде всего представляет выданные полномочия. Для проверки identity вызывающего агента может потребоваться отдельная HTTP Message Signature, сертификат, workload identity или proof-of-possession механизм.

Достаточно ли HTTP Message Signature?

Нет. Подпись защищает выбранные компоненты сообщения и подтверждает владение signing key, но не решает, разрешена ли бизнес-операция.

Чем scope отличается от policy?

Scope описывает класс разрешений, например files.write. Policy проверяет конкретную операцию: файл, владельца, размер, место назначения, уровень риска, параметры запроса и текущий контекст.

Может ли аутентифицированный агент получить 403?

Да. Установленная identity не означает, что субъекту разрешено любое действие. Например, агент может быть известен серверу, но не иметь доступа к конкретному MCP-инструменту.

Когда нужен 401, а когда 403?

401 обычно возвращается при отсутствующих или недействительных credentials. 403 — когда credentials приняты, но scopes или policy-разрешения недостаточно.

Нужно ли проверять identity пользователя и агента отдельно?

Да. Пользователь и агент — разные участники. Пользователь делегирует полномочия, а агент технически формирует и отправляет запрос.

Что должен проверять MCP-сервер перед вызовом инструмента?

Как минимум:

  • подпись агента;
  • целостность body;
  • срок действия подписи;
  • intent tag;
  • replay status;
  • issuer и audience токена;
  • OAuth scopes;
  • вызываемый инструмент;
  • параметры операции;
  • квоты и лимиты;
  • итоговую политику доступа.

Заключение

Аутентификация и авторизация AI-агентов решают разные задачи.

Аутентификация подтверждает:

text
«Этот запрос подписан владельцем известного ключа».

OAuth сообщает:

text
«Клиенту предоставлены определённые полномочия».

Политика доступа принимает окончательное решение:

text
«Этому агенту с этими полномочиями разрешено выполнить
данное действие с указанными параметрами прямо сейчас».

Без первого уровня сервер не знает, кто отправил запрос. Без второго — какие полномочия были делегированы клиенту. Без третьего — допустима ли конкретная операция.

AgentBouncer объединяет проверку HTTP Message Signatures, identity агента, пользовательскую OAuth-авторизацию, replay protection и политики проекта в одно итоговое решение allowed.

Защитите API или MCP-сервер с AgentBouncer — начните с monitor mode, изучите реальный агентский трафик и только затем включайте блокирующие политики.

Предыдущая статья: «Аутентификация AI-агентов: identity, authorization и контроль доступа».

Следующая статья серии: «MCP Authentication and Authorization: полное руководство» — скоро.

Reference file

Источники