FlawOpen

flawopen.com/AI Security/mcp-python-sdk-oauth-issuer-confusion

● CWE-345 / CWE-522 · CVSS 7.5[1] · Высокий
Исследования · FlawOpen

Ошибка проверки issuer OAuth в MCP Python SDK

GHSA-qx49-fqc8-xw99 позволяла HTTP-серверу MCP влиять на адрес, куда затронутые OAuth-клиенты Python SDK могли отправить учётные данные. Обновитесь до mcp 1.30.0 или 2.2.0; для машинных провайдеров необходимо задать issuer=. CVE не присвоен.[1], [3], [8]

Вектор CVSSCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N[1]
💡 Простыми словами (ELI5)

Компания отправляет запечатанный ключ в банк, адрес которого указан в утверждённой карточке справочника. Сервер MCP мог предоставить эту карточку, а резервный путь не проверял, что запись относится к ожидаемому банку. Проверка issuer сопоставляет идентификатор в карточке с доверенным банком из конфигурации, прежде чем отправить ключ.[1], [2]

Ключевые понятия и термины

Клиент MCP
Приложение, использующее Model Context Protocol для доступа к инструментам или данным сервера MCP. Уязвимость затрагивает Python SDK в роли HTTP-клиента с OAuth-провайдерами.[1]
Issuer сервера авторизации
Стабильный идентификатор OAuth-службы, выдающей токены. Клиент должен сравнить обнаруженные метаданные с issuer, ожидаемым независимо от них.[1], [2]
Protected Resource Metadata (PRM)
Метаданные, которые могут указывать серверы авторизации защищённого ресурса. При отсутствии PRM SDK также использует устаревший путь обнаружения.[1], [2]
Проверочный код PKCE
Секрет, созданный клиентом для привязки обмена OAuth-кода к клиенту, начавшему поток. Его нельзя раскрывать недоверенной конечной точке.[1], [2]
CWE-345 / CWE-522
Недостаточная проверка подлинности данных: система принимает данные, не подтверждая должным образом, что они пришли из ожидаемого источника. CWE-522 описывает недостаточную защиту учётных данных.[1], [4], [7]

Связи со стандартами

  • Ссылка CWE для слабого места, описанного в этом руководстве: CWE-345 CWE-522

Пошаговый сценарий атаки

Шаг 1

HTTP-клиент MCP хранит OAuth-учётные данные

Приложение использует затронутую версию mcp и один из OAuth-провайдеров SDK. Оно может подключаться к не полностью доверенному серверу MCP, имея секрет клиента, ключ подписи или сохранённую регистрацию клиента.[1]

Шаг 2

Сервер влияет на обнаружение авторизации

В mcp 1.9.1–1.29.1 не проверялся issuer и не выполнялась привязка учётных данных на всех путях обнаружения. mcp 2.0.0–2.1.1 был уязвим при отсутствии PRM и использовании legacy fallback либо при обнаружении после 403 insufficient_scope. В обеих ветках у машинных провайдеров не было issuer= для привязки заранее выданных учётных данных. Диапазон пакета в advisory начинается с предварительной версии 2.0.0a1 и ограничен <2.2.0; следовательно, затронутые предварительные сборки 2.0.0 также входят в него.[1], [3], [8]

Шаг 3

Учётные данные попадают на token endpoint

Клиент может отправить секрет клиента, код авторизации и проверочный код PKCE либо подписанное утверждение на endpoint, выбранный через эти метаданные. Набор данных зависит от провайдера; это не означает, что уязвим каждый клиент.[1], [2]

Шаг 4

Последствия зависят от провайдера и прав

Получатель может использовать раскрытые учётные данные или токены в пределах выданных разрешений. В уведомлении для автономных провайдеров указан CVSS 7,5; для интерактивного — 6,5, поскольку вход должен начать человек. В уведомлении сказано, что CVE не присвоен.[1]

Шаг 5

Привязка issuer закрывает пробел доверия

Исправленные версии определяют ожидаемый issuer до обнаружения, отклоняют несовпадающие метаданные и привязывают сохранённые регистрации к этому issuer. Для машинных провайдеров также нужно явно задать issuer=.[1], [2], [3], [8]

Матрица версий и действий для OAuth в MCP Python SDK

Конфигурация клиентаДокументированное условие уязвимостиНеобходимое действие
HTTP OAuth-клиент с mcp 1.9.1–1.29.1Во всех путях обнаружения не хватало проверки issuer и последовательной привязки учётных данных к нему.Обновитесь до mcp 1.30.0 или новее и проверьте фактически развёрнутую версию.
HTTP OAuth-клиент с mcp 2.0.0–2.1.1Уязвим при отсутствии PRM и срабатывании старого fallback либо при повторном обнаружении после 403 insufficient_scope. Диапазон пакета в advisory начинается с предварительной версии 2.0.0a1 и ограничен <2.2.0; следовательно, затронутые предварительные сборки 2.0.0 также входят в него.Обновитесь до mcp 2.2.0 или новее и проверьте фактически развёрнутую версию.
ClientCredentialsOAuthProvider или PrivateKeyJWTOAuthProvider с заранее выданными учётными даннымиВ уязвимых версиях отсутствовал параметр issuer= для привязки этих данных к ожидаемому серверу авторизации.После обновления явно задайте issuer=. Перейдите с устаревшего RFC7523OAuthClientProvider в ветке 1.x.
Созданный SDK сервер, stdio-клиент или клиент со своим токеном/заголовкомВ advisory указано, что они не затронуты.Для этой конкретной ошибки действий не требуется; проверьте приложение повторно, если оно также использует затронутый HTTP OAuth-клиент.

Анализ первопричины

У этой ошибки доверия было две связанные причины: некоторые затронутые пути обнаружения не всегда сравнивали issuer в метаданных сервера авторизации со значением, ожидаемым независимо от MCP-сервера, а сохранённые или заранее заданные учётные данные не были привязаны к своему серверу авторизации. Поэтому вредоносный или скомпрометированный MCP-сервер мог направить обмен токенов на контролируемую им конечную точку и получить OAuth-данные клиента. Исправления определяют ожидаемый issuer до обнаружения, отклоняют несовпадения и привязывают сохранённые регистрации к этому issuer; для автоматических провайдеров machine-to-machine также требуется явно указать issuer=.[1], [2]

Исходный код: Уязвимый vs Защищённый вариант

Аргумент issuer доступен в исправленных версиях SDK. Он связывает машинные учётные данные с нужным сервером авторизации; это не заменяет обновление, удаление старых непривязанных регистраций и ротацию учётных данных, которые могли раскрыться.[2], [3], [8]

Фрагмент исходного кода: mcp.client.auth

УЯЗВИМАЯ РЕАЛИЗАЦИЯ
import os
from mcp.client.auth import ClientCredentialsOAuthProvider

auth = ClientCredentialsOAuthProvider(
    client_id=os.environ["MCP_CLIENT_ID"],
    client_secret=os.environ["MCP_CLIENT_SECRET"],
)
БЕЗОПАСНЫЙ ИСПРАВЛЕННЫЙ ВАРИАНТ
import os
from mcp.client.auth import ClientCredentialsOAuthProvider

auth = ClientCredentialsOAuthProvider(
    client_id=os.environ["MCP_CLIENT_ID"],
    client_secret=os.environ["MCP_CLIENT_SECRET"],
    issuer=os.environ["MCP_AUTH_ISSUER"],
)
# Используйте mcp >= 1.30.0 (1.x) или >= 2.2.0 (2.x).

Чек-лист по защите системы для инженеров

Источники

← AI Security Directory →