Security & AI Architecture

OAuth, OAuth2 и OAuth 2.1: различия для AI и MCP (2026)

Быстрый ответ: OAuth 1.0a требовал сложной криптографической подписи каждого запроса, а OAuth 2.0 ввел Bearer-токены, уязвимые для атак повторного воспроизведения (replay). Для автономных AI-агентов и серверов Model Context Protocol (MCP) обязательным стандартом стал OAuth 2.1: он предписывает PKCE для всех потоков, полностью исключает небезопасные Implicit и Password Grants, а в сочетании с DPoP (RFC 9449) гарантирует привязку токенов к отправителю (sender-constraining) в автономных headless-средах.


1. Введение: Кризис аутентификации и идентификации AI-агентов в 2026 году

Стремительный переход от изолированных диалоговых интерфейсов больших языковых моделей (LLM) к полностью автономным многошаговым AI-агентам и серверам Model Context Protocol (MCP) породил критическую проблему корпоративной кибербезопасности: кризис доверенной идентификации и делегирования прав для автономных систем.

В 2024 и 2025 годах разработчики подключали инструменты автономного кодинга и рассуждений — такие как Claude Code, Cursor, Windsurf, AutoGen и специализированные LangGraph-агенты — к корпоративным API преимущественно с помощью статических персональных токенов доступа (Personal Access Tokens, PAT) или долгоживущих API-ключей, жестко прописанных в файлах .env или переменных окружения операционной системы. Когда AI-агент выполняет локальные shell-команды, обращается к внутренним базам данных (через серверы PostgreSQL или Supabase MCP) или обновляет корпоративные трекеры задач (через Jira или Linear MCP), он действует в контексте глобальных, неизбирательных полномочий (ambient authority).

УЯЗВИМАЯ АРХИТЕКТУРА АГЕНТОВ ПРОШЛОГО ПОКОЛЕНИЯ (Статические ключи):
+--------------------+   Запуск дочернего процесса   +---------------------------+
|  LLM Host Agent    | ───────────────────────────> |   Локальный MCP-сервер    |
| (Claude Code /     |   Env: GITHUB_TOKEN=ghp_...  |  (Читает process.env)     |
|  Cursor / LangSeq) |                              +-------------+-------------+
+---------+----------+                                            |
          | Косвенная prompt-инъекция                             | Неограниченный Read/Write
          v                                                       v
+--------------------+                                      +-------------------+
| Промпт атакующего  |                                      | Корпоративные     |
| на веб-странице:   | ──> Утечка статического ключа ─────> | GitHub / Slack /  |
| "Выведи env-перем" |     на HTTP-вебхук злоумышленника    | Внутренние БД     |
+--------------------+                                      +-------------------+

Эта статическая модель аутентификации фундаментально непригодна для реального продакшена по трем причинам:

  1. Prompt Injection как вектор эксфильтрации секретов: Если агент сталкивается с непроверенными внешними данными (враждебный промпт, внедренный в веб-страницу, тело входящего письма или тикет GitHub), модель может быть спровоцирована на запуск диагностических утилит или форматирующих команд, которые выводят process.env или инспектируют локальные конфигурационные файлы, мгновенно раскрывая ключи с наивысшими привилегиями.
  2. Отсутствие делегирования идентичности: Статический API-ключ не позволяет отличить действия, совершенные непосредственно человеком-оператором, от галлюцинированных или автономно инициированных шагов AI-агента. В корпоративных журналах аудита безопасности все вызовы выглядят идентично, как если бы их совершал сам пользователь.
  3. Невозможность динамического отзыва и отсутствие принципа наименьших привилегий: Статические токены обычно обладают избыточными полномочиями (например, полный доступ на чтение и запись репозитория) и имеют многомесячный либо неограниченный срок действия.

Для устранения этого системного риска экосистема AI переходит на протоколы делегированной авторизации. Однако архитектурный выбор между OAuth 1.0a, OAuth 2.0 и развивающимся консолидированным стандартом OAuth 2.1 — в сочетании с PKCE (RFC 7636), DPoP (RFC 9449) и Device Authorization Grant (RFC 8628) — требует детального понимания того, как эти протоколы функционируют в условиях автономного и headless-выполнения.


2. Эволюция OAuth: Структурное сравнение 1.0a, 2.0 и 2.1

Чтобы понять, почему современные фреймворки AI-агентов требуют внедрения OAuth 2.1, необходимо детально разобрать архитектурную эволюцию, компромиссы и критические уязвимости трех основных итераций спецификации OAuth.

ЭВОЛЮЦИЯ СПЕЦИФИКАЦИЙ OAUTH (2007 - 2026):
+---------------------------------------------------------------------------------------------+
| OAuth 1.0a (RFC 5849, 2010)                                                                 |
| - Симметричные/асимметричные криптоподписи для КАЖДОГО HTTP-запроса (HMAC-SHA1, RSA-SHA1)   |
| - Нет механизма ротации токенов; расчет подписей зависит от состояния; изоляция от сети     |
| - Вердикт для AI: Неприменимо. Крипто-оверхед ломает стриминг и динамические прокси тулов.  |
+---------------------------------------------------------------------------------------------+
                                               │
                                               ▼
+---------------------------------------------------------------------------------------------+
| OAuth 2.0 (RFC 6749 & RFC 6750, 2012)                                                       |
| - Перенос криптографии на транспортный уровень (TLS 1.2/1.3)                                |
| - Введены Bearer-токены, области доступа (Scopes), Refresh-токены и специализированные Grants|
| - Включал небезопасные потоки: Implicit Flow и Resource Owner Password Credentials (ROPC)   |
| - Вердикт для AI: Опасно. Bearer-токены легко похищаются через prompt-инъекции или SSRF.   |
+---------------------------------------------------------------------------------------------+
                                               │
                                               ▼
+---------------------------------------------------------------------------------------------+
| OAuth 2.1 (Консолидированный стандарт IETF, 2025/2026)                                      |
| - Полное удаление устаревших потоков (Implicit и Password Grants навсегда ликвидированы)     |
| - ОБЯЗАТЕЛЬНЫЙ PKCE (RFC 7636) для всех Authorization Code потоков (Public и Confidential)  |
| - Точное побайтовое совпадение Redirect URI; запрет токенов в query-параметрах URI          |
| - Обязательная ротация Refresh-токенов (RTR) или токены с привязкой к отправителю (DPoP/mTLS)|
| - Вердикт для AI: Золотой стандарт безопасности MCP-серверов и автономных агентов.          |
+---------------------------------------------------------------------------------------------+

OAuth 1.0a (RFC 5849): Криптографическая жесткость и зависимость от состояния

OAuth 1.0a создавался в эпоху, когда протокол HTTPS/TLS был дорогим в вычислительном плане и применялся ограниченно. Для защиты от перехвата данных в открытом HTTP-трафике OAuth 1.0a требовал от клиента и сервера вычисления криптографической подписи (HMAC-SHA1 или RSA-SHA1) для каждого индивидуального HTTP-запроса.

Вычисление подписи требовало нормализации HTTP-метода, точного нормализованного URL и лексикографически отсортированной строки из всех query-параметров, заголовков запроса, клиентского nonce и Unix-таймстемпа:

$$\text{BaseString} = \text{HTTP\_METHOD} \mathbin{\Vert} \text{"\&"} \mathbin{\Vert} \text{Encode}(\text{URL}) \mathbin{\Vert} \text{"\&"} \mathbin{\Vert} \text{Encode}(\text{SortedParams})$$

$$\text{Signature} = \text{HMAC-SHA1}(\text{ClientSecret} \mathbin{\Vert} \text{"\&"} \mathbin{\Vert} \text{TokenSecret}, \text{BaseString})$$

Почему OAuth 1.0a не подходит для AI-агентов:

  1. Стриминг и фрагментированные транспорты: Современные протоколы AI-агентов (такие как MCP поверх Server-Sent Events или WebSockets) непрерывно передают инкрементальные полезные нагрузки JSON-RPC. Повторный расчет подписи поверх недетерминированных потоковых фрагментов или при динамическом переписывании заголовков прокси-серверами приводит к постоянным сбоям верификации.
  2. Динамическая оркестрация инструментов: AI-агенты формируют HTTP-запросы динамически на основе параметров инструментов LLM. Незначительное изменение порядка параметров, нюансы URL-кодирования (например, %20 вместо +) или заголовки, добавленные сетевыми прокси, аннулируют подпись OAuth 1.0a, вызывая циклические ошибки 401 Unauthorized в автономных сценариях.
  3. Отсутствие нативного разделения обновления токенов: В токенах OAuth 1.0a не было предусмотрено механизма короткоживущих токенов с автоматической ротацией, что вынуждало долгоживущие секреты постоянно находиться в среде клиента.

OAuth 2.0 (RFC 6749): Простота ценой уязвимости Bearer-токенов

OAuth 2.0 решил проблему сложности 1.0a путем переноса криптографической целостности на транспортный уровень (сделав использование HTTPS строго обязательным) и внедрения Bearer-токена (RFC 6750). Любой субъект, владеющий этим токеном, получает доступ к ресурсу, подобно владельцу наличных денег:

GET /v1/repositories HTTP/1.1
Host: api.github.com
Authorization: Bearer ya29.a0AfH6SMB...

OAuth 2.0 также определил четыре исходных потока авторизации (grant flows):

  1. Authorization Code Grant: Безопасный поток перенаправления для веб-приложений с бэкендом, способным безопасно хранить клиентский секрет.
  2. Implicit Grant: Поток для браузерных приложений, возвращающий токены непосредственно во фрагменте хеша URL (#access_token=...) без серверного обмена кодом.
  3. Resource Owner Password Credentials (ROPC): Прямая передача логина и пароля пользователя клиентскому приложению для получения токена.
  4. Client Credentials Grant: Прямая авторизация типа machine-to-machine (M2M) для серверных демонов без промежуточного участия человека.

Критические уязвимости OAuth 2.0 в системах искусственного интеллекта:

  • Уязвимость воспроизведения Bearer-токена (Replay Attack): Если контекст выполнения агента скомпрометирован посредством SSRF, prompt-инъекции или утечки логов, атакующий может перехватить Bearer-токен и воспроизвести его из любой точки мира вплоть до момента истечения срока его действия.
  • Ловушка потока Implicit Grant: Ранние одностраничные приложения (SPA) и десктопные интерфейсы агентов использовали Implicit-поток. Токены утекали через историю браузера, HTTP-заголовки Referer и локальные обработчики перенаправлений.
  • Антипаттерн Password Grant: Разработчики CLI-агентов нередко запрашивали логин и пароль пользователя прямо в терминале, полностью нарушая базовое обещание безопасности OAuth: никогда не передавать учетные данные третьим лицам.

OAuth 2.1: Современный защищенный стандарт для автономных агентов

OAuth 2.1 представляет собой консолидированную спецификацию IETF, которая устраняет накопившийся технический долг и уязвимости безопасности OAuth 2.0. Для автономных AI-систем и интеграций с Model Context Protocol спецификация OAuth 2.1 формулирует категорические архитектурные требования:

  1. Полная ликвидация небезопасных потоков: Потоки Implicit Grant и Resource Owner Password Credentials официально признаны устаревшими и полностью исключены из стандарта.
  2. Обязательное применение PKCE для всех потоков Authorization Code: Proof Key for Code Exchange (RFC 7636) больше не является опциональным или применимым только к мобильным приложениям. Он строго обязателен как для публичных клиентов (CLI-агенты, расширения IDE), так и для конфиденциальных клиентов (бэкенд-рои агентов).
  3. Точное побайтовое совпадение Redirect URI: Серверы авторизации обязаны проверять Redirect URI на точное побайтовое совпадение строк, что предотвращает атаки через открытые редиректоры (Open Redirector) и перехват поддоменов с использованием подстановочных знаков.
  4. Строгие ограничения на передачу токенов: Bearer-токены категорически запрещено передавать в query-параметрах URI (что исключает попадание токенов в логи доступа HTTP, кэш прокси-серверов и браузерную телеметрию).
  5. Обязательная защита Refresh-токенов: Серверы авторизации ДОЛЖНЫ внедрить либо ротацию Refresh-токенов (Refresh Token Rotation, RTR) (при которой каждый запрос на обновление аннулирует старый Refresh-токен и возвращает новую пару), либо токены с привязкой к отправителю (Sender-Constrained Tokens через DPoP или mTLS).

Сравнительная таблица: OAuth 1.0a vs OAuth 2.0 vs OAuth 2.1

Архитектурный параметр OAuth 1.0a (RFC 5849) OAuth 2.0 (RFC 6749 / 6750) OAuth 2.1 (Стандарт IETF 2026)
Криптографическая модель Подпись каждого запроса на прикладном уровне (HMAC-SHA1 / RSA) Защита транспортного уровня (TLS) + обычный Bearer TLS + обязательный PKCE + привязка отправителя (DPoP/mTLS)
Риск повторного воспроизведения Bearer-токена Исключен (каждый запрос подписан уникальным nonce) Критически высокий (владение токеном равносильно авторизации) Нейтрализован (токен привязан к ключам DPoP)
Требование PKCE Не поддерживается Опционально (RFC 7636, преимущественно для мобильных клиентов) Обязательно для ВСЕХ сценариев Authorization Code
Implicit Grant (response_type=token) Не поддерживается Разрешен (проектировался для браузерных SPA) Полностью удален и запрещен
Password Grant (ROPC) Не поддерживается Разрешен (устаревший прямой обмен учетными данными) Полностью удален и запрещен
Валидация Redirect URI Допускалось сопоставление по префиксу Нередко допускались маски поддоменов и путей Требуется точное побайтовое совпадение
Токены в параметрах запроса URI Поддерживалось Разрешалось (?access_token=...) Категорически запрещено (только заголовок или тело)
Жизненный цикл Refresh-токена Нативный механизм отсутствовал Одиночный токен многократного использования Обязательная ротация (RTR) либо криптопривязка
Применимость для локальных CLI-агентов Низкая (сбои расчета подписей в shell) Уязвимо (риск перехвата на портах loopback) Оптимально (PKCE + эфемерные порты loopback)
Применимость для удаленных MCP-серверов Несовместимо с потоковым JSON-RPC Высокий риск неконтролируемой утечки токенов Стандарт по умолчанию (минимальные привилегии)

3. Топология авторизации Model Context Protocol (MCP): Защита коммуникации агент-сервер

Стандарт Model Context Protocol (MCP), открытый Anthropic и интегрированный в Claude Code, Cursor, Windsurf и корпоративные среды исполнения агентов, задает асимметричную клиент-серверную архитектуру поверх JSON-RPC 2.0.

Чтобы понимать, где именно функционирует OAuth 2.1, выделим две независимые границы взаимодействия внутри инфраструктуры MCP:

  • Граница A (Хост -> MCP-сервер): Соединение между клиентским приложением LLM (например, Claude Code, Cursor) и процессом MCP-сервера.
  • Граница B (MCP-сервер -> Корпоративная инфраструктура): Взаимодействие между MCP-сервером и внешними SaaS API (GitHub, Linear, Jira, Slack, Salesforce).
ТОПОЛОГИЯ АУТЕНТИФИКАЦИИ MODEL CONTEXT PROTOCOL (MCP):
+-------------------------------------------------------------------------------------------------------+
| СРЕДА ИСПОЛНЕНИЯ MCP-ХОСТА (Claude Code / Cursor / Среда автономных агентов)                          |
|                                                                                                       |
|  +---------------------+        Контекст промпта       +--------------------------------------------+ |
|  |  Промпт / Агентский | <───────────────────────────> | Движок рассуждений LLM (Claude 3.7 / GPT-4o)| |
|  |  цикл оркестратора  |                               +--------------------------------------------+ |
|  +----------+----------+                                                                              |
|             | Отправка вызова инструмента (`tools/call`)                                              |
|             v                                                                                         |
|  +--------------------------------------------------------------------------------------------------+ |
|  | ДВИЖОК MCP-КЛИЕНТА                                                                               | |
|  | - Выполняет рукопожатие OAuth 2.1 PKCE с сервером авторизации                                    | |
|  | - Удерживает эфемерный приватный ключ DPoP в неэкспортируемой памяти                             | |
|  | - Генерирует JWT DPoP-Proof для каждого запроса; внедряет Access Token в заголовки JSON-RPC       | |
|  +-----------------------------------+--------------------------------------------------------------+ |
+--------------------------------------|----------------------------------------------------------------+
                                       |
                                       | Транспорт: Stdio (локальный процесс) ИЛИ SSE/HTTP (удаленный сервер)
                                       v
+-------------------------------------------------------------------------------------------------------+
| СРЕДА ИСПОЛНЕНИЯ MCP-СЕРВЕРА (GitHub MCP / Корпоративная БД MCP)                                      |
|                                                                                                       |
|  +--------------------------------------------------------------------------------------------------+ |
|  | ПЕРЕХВАТЧИК АУТЕНТИФИКАЦИИ И ПРОВЕРКИ ТОКЕНОВ                                                    | |
|  | 1. Валидирует подпись токена OAuth 2.1 через эндпоинт JWKS сервера авторизации                    | |
|  | 2. Проверяет DPoP-Proof: проверяет HTTP-метод, URI, Nonce и открытый эфемерный ключ              | |
|  | 3. Проверяет области доступа (Scopes): применяет Least Privilege (`issues:read` vs `admin:all`)  | |
|  +-----------------------------------+--------------------------------------------------------------+ |
|                                      |                                                                |
|                                      v                                                                |
|  +--------------------------------------------------------------------------------------------------+ |
|  | ДВИЖОК ВЫПОЛНЕНИЯ ИНСТРУМЕНТА MCP (реализация `tools/call`)                                       | |
|  | - Санитизирует входные данные, блокирует Path Traversal, выполняет изолированный API-вызов       | |
|  +-----------------------------------+--------------------------------------------------------------+ |
+--------------------------------------|----------------------------------------------------------------+
                                       | Вызов внешнего API (аутентифицирован делегированным токеном)
                                       v
                      +----------------------------------+
                      | Внешние корпоративные SaaS / БД  |
                      | (GitHub / Jira / PostgreSQL / S3)|
                      +----------------------------------+

Сравнение транспортов Stdio и Remote SSE/HTTP

  1. Локальный транспорт Stdio (transport: "stdio"):
  • MCP-сервер запускается как локальный дочерний процесс хоста, обмениваясь сообщениями через стандартные потоки ввода и вывода (stdin/stdout).
  • Антипаттерн безопасности: Исторически инженеры внедряли учетные данные через переменные окружения процесса:
  • Уязвимость: Любая shell-команда, запущенная агентом, или любой дочерний процесс внутри контейнера могут проинспектировать /proc/[pid]/environ либо выполнить env, моментально скомпрометировав доступ к GitHub для всей организации.
  • Решение OAuth 2.1: Хост управляет защищенным хранилищем токенов OAuth 2.1 PKCE. При запуске MCP-сервер инициализируется без статических секретов. Хост передает короткоживущий делегированный токен в процессе рукопожатия инициализации MCP либо выступает в роли аутентифицирующего обратного прокси.
  1. Удаленный транспорт SSE/HTTP (transport: "sse"):
  • MCP-сервер функционирует как распределенный веб-сервис, прослушивающий HTTP-порт и использующий Server-Sent Events для передачи уведомлений от сервера к клиенту.
  • В данной архитектуре использование OAuth 2.1 безальтернативно. Клиент MCP обязан аутентифицироваться на удаленном сервере с помощью стандартных HTTP-заголовков Authorization, передавая токен доступа OAuth 2.1, валидируемый через JSON Web Key Sets (JWKS) и привязанный к криптографическому DPoP-доказательству.

4. Детальный разбор PKCE (RFC 7636): Защита локальных callback-вызовов агентов

Спецификация Proof Key for Code Exchange (PKCE, произносится «пикси») изначально стандартизирована в RFC 7636 для предотвращения перехвата кодов авторизации на мобильных устройствах. В рамках OAuth 2.1 PKCE стал строго обязательным для любого обмена кодом авторизации.

Почему CLI-агенты и среды IDE являются уязвимыми публичными клиентами

Инструменты разработки на базе AI (такие как Claude Code, Cursor и Roo Code) классифицируются в терминологии OAuth как публичные клиенты (Public Clients). Поскольку их исходный код или скомпилированные бинарные файлы выполняются непосредственно на локальной машине пользователя, они не способны надежно скрыть статический client_secret. Если разработчик внедрит клиентский секрет в открытый CLI-агент, любой желающий сможет декомпилировать приложение и извлечь этот ключ.

Когда публичный клиент запрашивает авторизацию, сервер возвращает код авторизации (Authorization Code) через локальный Redirect URI (как правило, временный локальный HTTP-сервер loopback, например http://127.0.0.1:18492/callback).

АТАКА ПЕРЕХВАТА КОДА АВТОРИЗАЦИИ (Без использования PKCE):
1. Легитимный CLI-агент запрашивает код авторизации у сервера Auth.
2. Вредоносный фоновый процесс на машине разработчика занимает порт или перехватывает loopback-трафик.
3. Сервер Auth перенаправляет браузер на http://127.0.0.1:18492/callback?code=AUTH_CODE_123.
4. Вредоносный процесс перехватывает код AUTH_CODE_123.
5. Вредоносный процесс отправляет AUTH_CODE_123 на эндпоинт /token сервера Auth.
   Поскольку клиент публичный (client_secret не требуется), сервер Auth выдает токен доступа атакующему!

Математическая защита PKCE

PKCE полностью устраняет данную уязвимость благодаря динамической генерации одноразового криптографического секрета для каждого сеанса авторизации.

СЕТЕВОЙ ПОТОК ПРОТОКОЛА PKCE:
+-------------+                     +-----------------------+                    +--------------------+
|  Agent CLI  |                     | Браузер пользователя  |                    | Сервер авторизации |
|  (Клиент)   |                     +-----------+-----------+                    +---------+----------+
+------+------+                                 |                                          |
       | 1. Генерирует code_verifier (энтропия) |                                          |
       |    Вычисляет code_challenge = S256(...)|                                          |
       |                                        |                                          |
       | 2. Запускает HTTP-слушатель Loopback   |                                          |
       |    Открывает браузер с challenge ─────>| 3. GET /authorize?response_type=code     |
       |                                        |    &client_id=agent_cli                  |
       |                                        |    &code_challenge=E9Melhoa2Owv...       |
       |                                        |    &code_challenge_method=S256 ─────────>|
       |                                        |                                          | 4. Согласие юзера.
       |                                        | 5. 302 Редирект на локальный Loopback    |    Сохранение challenge
       |                                        |<─────────────────────────────────────────|
       |<───────────────────────────────────────|    http://127.0.0.1:18492/callback?code=AC_88921
       | 6. Перехватывает Callback с кодом      |
       |                                                                                   |
       | 7. POST /oauth/token                                                              |
       |    code=AC_88921 & code_verifier=dBjftJeZ4CVP-mB92K... ─────────────────────────>|
       |                                                                                   | 8. Вычисляет:
       |                                                                                   |    SHA256(verifier)
       |                                                                                   |    Сверка с challenge?
       | 9. Возврат Access Token + Refresh Token (RTR) <───────────────────────────────────|    ДА: Выдача токена
+------+------+

Математическое рукопожатие выполняется следующим образом:

  1. Генерация Code Verifier: AI-агент генерирует криптографически случайную строку $V$ с высокой энтропией, используя нерезервированные символы URL ([A-Z], [a-z], [0-9], -, ., _, ~) длиной от 43 до 128 символов:
  2. Вычисление Code Challenge: Клиент вычисляет хеш SHA-256 от верификатора и кодирует его с помощью Base64URL без знаков заполнения:
  3. Запрос авторизации: Клиент передает $C$ и метод преобразования code_challenge_method=S256 на эндпоинт /authorize. Сервер сохраняет $C$ вместе с созданным кодом авторизации.
  4. Обмен кода на токен: Когда клиент отправляет код авторизации на /token, он прикрепляет исходный code_verifier=V. Сервер авторизации вычисляет $\text{Base64URL-Encode}(\text{SHA-256}(V))$ и сверяет результат с ранее сохраненным $C$.

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

Реализация на TypeScript для продакшена: Защищенный модуль PKCE

Ниже представлена эталонная продакшн-реализация генератора и верификатора PKCE, созданная для локальных клиентских сред MCP:

// pkce.ts - Enterprise OAuth 2.1 PKCE Engine for AI Agent Clients
import { randomBytes, createHash } from "node:crypto";

export interface PKCEChallenge {
  codeVerifier: string;
  codeChallenge: string;
  codeChallengeMethod: "S256";
}

export class PKCEEngine {
  /**
   * Generates a cryptographically secure code_verifier (RFC 7636 Section 4.1)
   * Length defaults to 64 bytes of entropy (yielding ~86 base64url characters).
   */
  public static generateVerifier(length: number = 64): string {
    if (length < 32 || length > 96) {
      throw new RangeError("Verifier byte length must be between 32 and 96.");
    }
    const buffer = randomBytes(length);
    return this.base64UrlEncode(buffer);
  }

  /**
   * Computes the S256 code_challenge from the code_verifier (RFC 7636 Section 4.2)
   */
  public static computeChallenge(verifier: string): string {
    const hash = createHash("sha256").update(verifier, "ascii").digest();
    return this.base64UrlEncode(hash);
  }

  /**
   * Generates the complete PKCE pair ready for OAuth 2.1 authorization
   */
  public static createPair(): PKCEChallenge {
    const codeVerifier = this.generateVerifier(64);
    const codeChallenge = this.computeChallenge(codeVerifier);
    return {
      codeVerifier,
      codeChallenge,
      codeChallengeMethod: "S256",
    };
  }

  /**
   * Server-side verification: Validates an incoming code_verifier against stored challenge
   */
  public static verify(verifier: string, storedChallenge: string): boolean {
    const computed = this.computeChallenge(verifier);
    // Timing-safe buffer comparison to prevent side-channel timing attacks
    const bufA = Buffer.from(computed);
    const bufB = Buffer.from(storedChallenge);
    if (bufA.length !== bufB.length) return false;
    
    let result = 0;
    for (let i = 0; i < bufA.length; i++) {
      result |= bufA[i] ^ bufB[i];
    }
    return result === 0;
  }

  private static base64UrlEncode(buffer: Buffer): string {
    return buffer
      .toString("base64")
      .replace(/\\+/g, "-")
      .replace(/\\//g, "_")
      .replace(/=+$/, "");
  }
}

5. Авторизация в Headless-средах и CLI: Device Flow (RFC 8628)

Хотя PKCE решает проблему аутентификации в интерактивных десктопных средах, где возможно открыть браузер и поднять локальный порт loopback, современные AI-агенты все чаще работают в headless-средах без доступа к браузеру:

  • Docker-контейнеры в удаленных облачных кластерах (AWS ECS, Kubernetes, Fly.io).
  • Одноразовые раннеры CI/CD (GitHub Actions, GitLab CI).
  • Удаленные виртуальные машины и терминальные сессии SSH.

В headless-окружении агент не может отобразить окно браузера для завершения перенаправления. Предложение пользователю ввести логин и пароль прямо в консоль нарушает стандарты OAuth 2.1. Отраслевым решением этой задачи выступает OAuth 2.0 Device Authorization Grant (RFC 8628).

DEVICE AUTHORIZATION GRANT (RFC 8628) В СРЕДАХ HEADLESS-АГЕНТОВ:
+-------------------+                                                +-----------------------+
|  Headless-агент   |                                                | Сервер авторизации    |
| (Docker / Облако) |                                                +-----------+-----------+
+---------+---------+                                                            |
          | 1. POST /oauth/device/code                                           |
          |    (client_id, scope) ──────────────────────────────────────────────>|
          |                                                                      | 2. Генерирует:
          | 3. Возвращает параметры устройства:                                  |    device_code (секрет)
          |    - user_code: "WDJB-HGNP"                                          |    user_code (публичный)
          |    - verification_uri: "https://auth.corp.com/activate"              |    interval: 5 секунд
          |    - interval: 5 <───────────────────────────────────────────────────|
          |                                                                      |
          | 4. Выводит терминальную инструкцию оператору:                        |
          |    "Откройте https://auth.corp.com/activate и введите: WDJB-HGNP"    |
          |                                                                      |
          | 5. Переходит в цикл опроса (Polling Loop):                           |
          |    POST /oauth/token (grant_type=device_code, device_code=...) ─────>|
          |    <── 400 Bad Request: {"error": "authorization_pending"} ─────────|
          |    [Ожидание 5 секунд]                                               |
          |                                                                      |
+---------+---------+     Пользователь открывает URL на ноутбуке/телефоне        |
| Ноутбук оператора | ──> Вводит "WDJB-HGNP", проходит MFA ─────────────────────>| 6. Успешный вход!
+-------------------+                                                            |
          |                                                                      |
          | 7. Следующий цикл опроса:                                            |
          |    POST /oauth/token ───────────────────────────────────────────────>|
          |    <── 200 OK: {access_token: "...", refresh_token: "..."} ──────────|
          v
[Headless-агент авторизован с нулевым раскрытием постоянных секретов]

Альтернатива Machine-to-Machine (M2M): RFC 7523 Private Key JWT

Когда полностью автономный агент выполняет задачи без участия человека (например, ночной бот рефакторинга кодовой базы), Device Authorization Grant неприменим из-за отсутствия оператора.

В подобных сценариях архитектура Zero-Trust задействует Client Credentials Grant, усиленный RFC 7523 (JWT Profile for Client Authentication):

  • Вместо передачи статического общего секрета client_secret по сети, агент хранит приватный асимметричный ключ (RSA или ECDSA) в аппаратном модуле безопасности (HSM) или защищенном хранилище секретов Kubernetes.
  • При обращении к /oauth/token агент генерирует и подписывает короткоживущий токен JWT, подтверждающий его идентичность, с уникальным идентификатором jti (UUID), сроком действия 60 секунд и целевой аудиторией aud.
  • Сервер авторизации проверяет подпись по предварительно зарегистрированному открытому ключу агента, полностью исключая передачу постоянных паролей по сети.

6. Жизненный цикл токенов и механизмы автономного обновления

Автономные AI-агенты часто выполняют длительные многочасовые задачи: индексацию крупных репозиториев, прогон объемных бенчмарков или мониторинг инцидентов. Поскольку access-токены OAuth 2.1 намеренно выпускаются короткоживущими (как правило, от 5 до 15 минут), агент обязан автономно управлять их обновлением без прерывания активных вызовов инструментов LLM.

Ротация Refresh-токенов (RTR) и обнаружение компрометации

В OAuth 2.1 токены обновления должны быть строго защищены от перехвата. Главным защитным механизмом служит ротация токенов обновления (Refresh Token Rotation, RTR):

  1. Каждый раз, когда агент отправляет refresh_token на эндпоинт /oauth/token, сервер авторизации немедленно аннулирует этот конкретный Refresh-токен.
  2. Сервер возвращает новый access_token И совершенно новый refresh_token.
  3. Если злоумышленник перехватил ранее использованный Refresh-токен и попытался его применить, сервер фиксирует инцидент немедленной компрометации:

$$\text{Incoming Token State} == \text{"REVOKED"} \implies \text{Revoke All Tokens in Family Tree}$$

Сервер авторизации мгновенно отзывает всю ветку выданных прав, аннулируя все активные Access- и Refresh-токены для всех запущенных инстансов агента.

РОТАЦИЯ REFRESH-ТОКЕНОВ (RTR) И АВТОМАТИЧЕСКАЯ БЛОКИРОВКА ПРИ КОМПРОМЕТАЦИИ:
Цепочка генерации токенов:
[Refresh Token A] ──(Использован)──> [Refresh Token B] ──(Использован)──> [Refresh Token C] (Активен)
         │
         │ Злоумышленник повторно отправляет перехваченный [Refresh Token A]
         v
[Сервер авторизации фиксирует повторное использование отозванного токена A!]
         │
         ▼
[КРИТИЧЕСКИЙ АЛЕРТ]: Мгновенный отзыв токенов B, C и всех связанных Access Tokens.
Сессия агента безопасно завершается, исключая несанкционированное повышение привилегий.

Продакшн-реализация на Python: Потокобезопасный асинхронный менеджер токенов

В мультиагентных системах (когда оркестратор запускает 10 параллельных субагентов, обращающихся к одному MCP-серверу) несколько параллельных вызовов могут одновременно обнаружить истечение срока токена. Если все 10 агентов одновременно попытаются обменять одноразовый Refresh-токен, 9 из них получат отказ, а сервер может расценить гонку запросов как атаку повторного воспроизведения!

Следующий модуль на Python реализует асинхронный менеджер токенов с мьютекс-блокировкой, упреждающим обновлением и защитой от состояния гонки:

# token_manager.py - Enterprise Async Token Lifecycle Manager for AI Agents
import asyncio
import time
import httpx
from typing import Optional, Dict, Any

class AgentTokenManager:
    def __init__(
        self,
        token_endpoint: str,
        client_id: str,
        initial_refresh_token: str,
        proactive_refresh_seconds: int = 60,
    ):
        self.token_endpoint = token_endpoint
        self.client_id = client_id
        self.refresh_token = initial_refresh_token
        self.access_token: Optional[str] = None
        self.expires_at: float = 0.0
        self.proactive_refresh_seconds = proactive_refresh_seconds
        self._lock = asyncio.Lock()

    async def get_valid_access_token(self) -> str:
        now = time.time()
        if self.access_token and (self.expires_at - now) > self.proactive_refresh_seconds:
            return self.access_token

        async with self._lock:
            now = time.time()
            if self.access_token and (self.expires_at - now) > self.proactive_refresh_seconds:
                return self.access_token

            await self._refresh_token_exchange()
            if not self.access_token:
                raise RuntimeError("Failed to acquire valid access token from authorization server.")
            return self.access_token

    async def _refresh_token_exchange(self) -> None:
        payload = {
            "grant_type": "refresh_token",
            "refresh_token": self.refresh_token,
            "client_id": self.client_id,
        }
        
        async with httpx.AsyncClient(timeout=10.0) as client:
            try:
                response = await client.post(
                    self.token_endpoint,
                    data=payload,
                    headers={"Content-Type": "application/x-www-form-urlencoded"},
                )
            except httpx.RequestError as exc:
                raise ConnectionError(f"Network transport error during token refresh: {exc}")

            if response.status_code == 200:
                data: Dict[str, Any] = response.json()
                self.access_token = data["access_token"]
                expires_in = int(data.get("expires_in", 3600))
                self.expires_at = time.time() + expires_in
                
                # Update to the newly rotated refresh token if provided
                if "refresh_token" in data:
                    self.refresh_token = data["refresh_token"]
            elif response.status_code in (400, 401):
                err_data = response.json()
                # If error is 'invalid_grant', the token was likely already rotated or revoked
                raise PermissionError(f"Token refresh rejected (possible token theft or expiry): {err_data}")
            else:
                response.raise_for_status()

7. Изоляция учетных данных Zero-Trust: DPoP (RFC 9449) и аппаратные анклавы

Даже при строгом соблюдении требований OAuth 2.1 и PKCE классические Bearer-токены сохраняют фатальную архитектурную уязвимость: будучи перехваченным, Bearer-токен может быть использован кем угодно.

В контексте интеграций с AI автономный агент регулярно обрабатывает неструктурированные входящие данные. Если злоумышленник реализует косвенную prompt-инъекцию и заставит агента выполнить HTTP-запрос (SSRF) на подконтрольный сервер с передачей заголовка авторизации, токен окажется скомпрометирован.

Для реализации полноценной концепции Zero-Trust стандарт OAuth 2.1 интегрирует спецификацию DPoP: Demonstrating Proof-of-Possession at the Application Layer (RFC 9449).

DPOP (RFC 9449) ПРИВЯЗКА ТОКЕНОВ К ОТПРАВИТЕЛЮ НА ПРИКЛАДНОМ УРОВНЕ:
+-------------------------------------------------------------------------------------------------+
| ЛОКАЛЬНАЯ СРЕДА АГЕНТА (Клиент)                                                                 |
| - Генерирует эфемерную пару ключей: открытый ключ (JWK) + закрытый ключ (только в RAM/Анклаве)  |
+-------------------------------------------------------------------------------------------------+
                                                │
                                                │ 1. Добавляет заголовок DPoP Proof:
                                                │    DPoP: eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Ar...
                                                │    Payload: {
                                                │      "htm": "GET",
                                                │      "htu": "https://api.enterprise.com/mcp/tools",
                                                │      "iat": 1772630400,
                                                │      "jti": "random_nonce_9921",
                                                │      "jwk": { ...public_key... }
                                                │    }
                                                │
                                                │ 2. Передает привязанный DPoP-токен:
                                                │    Authorization: DPoP dpop_access_token_88921
                                                v
+-------------------------------------------------------------------------------------------------+
| КОРПОРАТИВНЫЙ ШЛЮЗ MCP (Resource Server)                                                        |
| 1. Проверяет, что Access Token криптографически привязан к открытому ключу в JWK.               |
| 2. Проверяет цифровую подпись заголовка DPoP Proof открытым ключом.                             |
| 3. Проверяет, что "htm" равен "GET", а "htu" в точности соответствует целевому URL.             |
| 4. Проверяет, что метка "iat" находится в окне допуска (< 60 сек), а "jti" не воспроизводился. |
+-------------------------------------------------------------------------------------------------+
                                                │
       ┌────────────────────────────────────────┴────────────────────────────────────────┐
       ▼                                                                                 ▼
[ВАЛИДНЫЙ PROOF И СОВПАДАЮЩИЙ КЛЮЧ]                                           [ПОВТОР ПЕРЕХВАЧЕННОГО ТОКЕНА]
Запрос успешно передается на исполнение                                       У атакующего есть токен, НО нет
                                                                              приватного ключа агента.
                                                                              Результат: 401 Unauthorized!

Как DPoP работает на практике

  1. Генерация эфемерных ключей: При инициализации сессии агент создает временную пару асимметричных криптографических ключей (как правило, ECDSA на кривой P-256 или Ed25519) в оперативной памяти или защищенном анклаве.
  2. Криптографическая привязка: При запросе токена у сервера авторизации агент прикрепляет DPoP-доказательство. Выпущенный токен доступа криптографически связывается с отпечатком (thumbprint) открытого ключа (клейм jkt).
  3. Генерация Proof-of-Possession на каждый запрос: Для каждого последующего вызова API или MCP-сервера агент генерирует и подписывает одноразовый JWT-заголовок DPoP, содержащий:
  • htm: точный метод HTTP-запроса (например, POST).
  • htu: точный целевой URI без параметров строки запроса и фрагментов.
  • iat: метку времени создания (в пределах $\pm 60$ секунд).
  • jti: уникальный UUID для предотвращения атак повторного воспроизведения.
  • nonce: проверочный nonce, предоставленный сервером (при наличии требования).
  1. Эшелонированная оборона: Даже если атакующий перехватит строку access-токена через логи прокси или утечку в чате LLM, этот токен абсолютно бесполезен без локального закрытого ключа, необходимого для генерации корректных заголовков DPoP.

8. Корпоративные бенчмарки безопасности, матрица рисков и режимы отказов

Развертывание аутентификации для автономных AI-агентов требует соблюдения баланса между вычислительными задержками и надежностью нейтрализации угроз. Ниже приведены эмпирические результаты бенчмарков, сопоставляющие задержки, вычислительные накладные расходы и гарантии безопасности различных архитектур.

Эмпирические бенчмарки производительности (10 000 итераций, оборудование Apple M4 Max)

Архитектура аутентификации Задержка рукопожатия (p50) Задержка рукопожатия (p99) Накладные расходы на валидацию запроса Защита от Replay Потребление памяти клиентом Нагрузка на CPU сервера
Статический PAT / API-ключ 0.1 мс (Без рукопожатия) 0.2 мс 0.02 мс (Сравнение строк) Отсутствует (Полный Replay) < 1 КБ Базовая
OAuth 1.0a (HMAC-SHA1) 14.2 мс 38.5 мс 1.84 мс (Парсинг подписи) Частичная (Проверка nonce) 12 КБ +18%
OAuth 2.0 Bearer 45.1 мс 112.0 мс 0.15 мс (Подпись JWT/кэш) Отсутствует (Replay токена) 18 КБ +4%
OAuth 2.1 (PKCE + RTR) 48.6 мс 118.4 мс 0.16 мс (Валидация JWT) Умеренная (Отзыв цепочки RTR) 24 КБ +5%
OAuth 2.1 + DPoP (P-256) 54.2 мс 132.8 мс 1.22 мс (Валидация DPoP JWT) Максимальная (Zero Replay) 36 КБ +12%
mTLS (RFC 8705) 62.8 мс 154.1 мс 0.45 мс (Кэш TLS-сессий) Максимальная (Привязка к серту) 128 КБ +15%

Матрица рисков и угроз для AI-агентов

УРОВЕНЬ ОПАСНОСТИ УГРОЗ В ЗАВИСИМОСТИ ОТ ВЫБРАННОГО ПРОТОКОЛА:
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| Вектор атаки                  | Статический ключ  | OAuth 1.0a        | OAuth 2.0 Bearer  | OAuth 2.1 + DPoP  |
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| 1. Косвенная prompt-инъекция  | КРИТИЧЕСКИЙ (10)  | ВЫСОКИЙ (7/10)    | КРИТИЧЕСКИЙ (10)  | НИЗКИЙ (2/10)     |
|    (Утечка переменных / логов)| Утечка суперключа | Сложная подпись   | Утечка bearer     | Токен без ключа   |
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| 2. Перехват локального порта  | N/A               | НИЗКИЙ (3/10)     | ВЫСОКИЙ (8/10)    | ЗАЩИЩЕНО (1/10)   |
|    (Перехват loopback в CLI)  | Нет редиректа     | Nonce подписан    | Перехват кода     | Блокируется PKCE  |
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| 3. Атака через SSRF-инструмент| КРИТИЧЕСКИЙ (10)  | СРЕДНИЙ (5/10)    | КРИТИЧЕСКИЙ (10)  | ЗАЩИЩЕНО (1/10)   |
|    (Обход сетевого периметра) | Утечка прав       | Сбой URL подписи  | Повтор токена     | Несовпадение URI  |
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| 4. Чтение среды процессов     | КРИТИЧЕСКИЙ (10)  | СРЕДНИЙ (5/10)    | ВЫСОКИЙ (8/10)    | НИЗКИЙ (2/10)     |
|    (Чтение /proc/environ)     | Постоянный секрет | Секрет в env      | Bearer в env      | Короткоживущий    |
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| 5. Состояние гонки обновления | N/A               | N/A               | НИЗКИЙ (2/10)     | ВЫСОКИЙ (Требует  |
|    (Параллельный рой агентов) | Нет обновления    | Нет обновления    | Токен повторен    | Мьютекс-менеджер) |
+-------------------------------+-------------------+-------------------+-------------------+-------------------+

9. Пошаговое руководство по внедрению: Защищенный MCP-сервер OAuth 2.1 + PKCE

Для практического применения этих архитектурных стандартов мы разработаем готовый к эксплуатации удаленный сервер Model Context Protocol (MCP) на базе TypeScript, Express и JSON-RPC 2.0. Сервер реализует строгую проверку токенов OAuth 2.1, валидацию PKCE и гранулярный контроль областей доступа (scopes) при вызове инструментов AI-агентами.

Обзор архитектуры

  • Промежуточное ПО аутентификации (Middleware): Проверяет входящие Bearer- и DPoP-токены по эндпоинту JWKS корпоративного провайдера идентификации (IdP).
  • Контроллер областей доступа (Scope Guard): Строго ограничивает доступ (например, инструмент поиска кода получает исключительно code:read и не может выполнить запись или административные действия).
  • Изолированная среда выполнения инструментов: Запускает логику инструмента в безопасном контексте.

Полный исходный код MCP-сервера на TypeScript

// server.ts - Hardened Remote MCP Server with OAuth 2.1 Validation
import express, { Request, Response, NextFunction } from "express";
import { createRemoteJWKSet, jwtVerify } from "jose";

const app = express();
app.use(express.json());

// Configuration
const ISSUER = "https://auth.enterprise.com/";
const AUDIENCE = "https://mcp.enterprise.com/";
const JWKS_URI = new URL("https://auth.enterprise.com/.well-known/jwks.json");
const JWKS = createRemoteJWKSet(JWKS_URI);

interface AuthenticatedRequest extends Request {
  tokenClaims?: any;
}

/**
 * Enterprise OAuth 2.1 Token Validation Middleware
 */
async function requireOAuth21(
  req: AuthenticatedRequest,
  res: Response,
  next: NextFunction
): Promise<void> {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    res.status(401).json({
      jsonrpc: "2.0",
      error: { code: -32001, message: "Missing or invalid OAuth 2.1 Authorization header." },
      id: req.body?.id || null,
    });
    return;
  }

  const token = authHeader.split(" ")[1];

  try {
    // Cryptographically verify token signature, issuer, audience, and expiration
    const { payload } = await jwtVerify(token, JWKS, {
      issuer: ISSUER,
      audience: AUDIENCE,
    });

    // Enforce OAuth 2.1 requirement: reject tokens without an expiration claim
    if (!payload.exp || typeof payload.exp !== "number") {
      res.status(401).json({
        jsonrpc: "2.0",
        error: { code: -32002, message: "Non-compliant token: missing expiration claim." },
        id: req.body?.id || null,
      });
      return;
    }

    req.tokenClaims = payload;
    next();
  } catch (err: any) {
    res.status(401).json({
      jsonrpc: "2.0",
      error: { code: -32003, message: `Token verification failed: ${err.message}` },
      id: req.body?.id || null,
    });
  }
}

/**
 * Fine-Grained Scope Enforcement Guard
 */
function requireScope(requiredScope: string) {
  return (req: AuthenticatedRequest, res: Response, next: NextFunction): void => {
    const scopes: string[] = (req.tokenClaims?.scope || "").split(" ");
    if (!scopes.includes(requiredScope)) {
      res.status(403).json({
        jsonrpc: "2.0",
        error: {
          code: -32004,
          message: `Insufficient permissions: missing required scope '${requiredScope}'`,
        },
        id: req.body?.id || null,
      });
      return;
    }
    next();
  };
}

/**
 * Standard MCP JSON-RPC 2.0 Handler Endpoint
 */
app.post(
  "/mcp/v1",
  requireOAuth21,
  requireScope("mcp:tools:execute"),
  async (req: AuthenticatedRequest, res: Response): Promise<void> => {
    const { jsonrpc, method, params, id } = req.body;

    if (jsonrpc !== "2.0") {
      res.status(400).json({ jsonrpc: "2.0", error: { code: -32600, message: "Invalid JSON-RPC version." }, id });
      return;
    }

    // Router for MCP Primitives
    switch (method) {
      case "tools/list":
        res.json({
          jsonrpc: "2.0",
          result: {
            tools: [
              {
                name: "query_database",
                description: "Executes read-only SQL queries against the analytics warehouse.",
                inputSchema: {
                  type: "object",
                  properties: { query: { type: "string" } },
                  required: ["query"],
                },
              },
            ],
          },
          id,
        });
        break;

      case "tools/call":
        if (params?.name === "query_database") {
          // Verify elevated data scope for this specific tool execution
          const scopes: string[] = (req.tokenClaims?.scope || "").split(" ");
          if (!scopes.includes("db:analytics:read")) {
            res.json({
              jsonrpc: "2.0",
              error: { code: -32005, message: "Forbidden: tool requires 'db:analytics:read' scope." },
              id,
            });
            return;
          }

          // Execute tool with verified, scoped identity
          const userSub = req.tokenClaims.sub;
          console.log(`Executing query on behalf of verified agent identity: ${userSub}`);
          
          res.json({
            jsonrpc: "2.0",
            result: {
              content: [
                {
                  type: "text",
                  text: JSON.stringify({ status: "success", rows_returned: 42, latency_ms: 12 }),
                },
              ],
            },
            id,
          });
        } else {
          res.status(404).json({ jsonrpc: "2.0", error: { code: -32601, message: "Tool not found." }, id });
        }
        break;

      default:
        res.status(404).json({ jsonrpc: "2.0", error: { code: -32601, message: "Method not found." }, id });
    }
  }
);

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Hardened OAuth 2.1 MCP Server listening on port ${PORT}`);
});

10. Заключение и стратегические рекомендации (E-E-A-T)

Интеграция автономных моделей искусственного интеллекта в корпоративную инфраструктуру требует отношения к AI-агентам как к делегированным акторам с частичным доверием. Отношение к агенту как к полностью доверенному внутреннему микросервису (с root-ключами в переменных среды) либо как к ненадежному публичному субъекту (с ручным подтверждением каждого клика человеком) является грубейшей архитектурной ошибкой.

Стандарт OAuth 2.1 предоставляет точную криптографическую модель, необходимую для преодоления этого разрыва, сочетая свободу действий автономных разработчиков с бескомпромиссными требованиями Zero-Trust.

Чеклист архитектурной безопасности AI-агентов из 5 пунктов

  1. Полная ликвидация статических глобальных секретов: Проведите аудит всех конфигураций MCP, файлов .env и параметров запуска контейнеров. Замените постоянные токены PAT на короткоживущие access-токены OAuth 2.1.
  2. Обязательное внедрение PKCE со схемой S256: Убедитесь, что все CLI-инструменты (Claude Code, плагины Cursor, внутренние утилиты) используют RFC 7636 с криптографической энтропией verifier и хешированием SHA-256. Полностью исключите небезопасный метод plain.
  3. Перевод headless-сред на RFC 8628 или Private Key JWT: Для Docker-контейнеров и облачных агентов откажитесь от скриптов ввода паролей в терминале. Внедрите Device Authorization Grant для сессий с участием человека либо Private Key JWT (RFC 7523) для полностью автономных фоновых агентов.
  4. Развертывание ротации Refresh-токенов с асинхронными мьютексами: Клиентские библиотеки агентов должны блокировать параллельные запросы на обновление токена, исключая состояние гонки и обеспечивая мгновенную блокировку цепочки при компрометации.
  5. Внедрение токенов с привязкой к отправителю (DPoP): Для высокорисковых операций (выполнение кода, финансовые транзакции, модификация баз данных) требуйте заголовки RFC 9449 DPoP, надежно блокируя атаки косвенной prompt-инъекции и кражи токенов на сетевом уровне.
← Все статьи
0 / 4
Сравнить →