ai-security

Открытая библиотека проверяет подписи агентов OpenAI

Promtime

ai-security

Regent Protocol выложила на Github библиотеку regent-httpsig, которая проверяет подписи входящих HTTP-запросов по RFC 9421 и черновику Web Bot Auth, а также подписывает исходящий трафик собственного агента. По описанию проекта, агенты OpenAI подписывают каждый свой запрос, а проверяют эти подписи Cloudflare, AWS WAF и Google. Установка идёт через pip install regent-httpsig, лицензия Apache-2.0.

Коротко

  • Ядро библиотеки не зависит от веб-фреймворка: вызов verifier.verify с методом, URL и заголовками возвращает объект VerifiedSignature с идентификатором агента и отпечатком ключа по RFC 7638 либо None.
  • Конфигурация задаёт список доверенных агентов, максимальный возраст подписи в 25 часов и время жизни кэша каталогов ключей в 600 секунд; сам факт валидной подписи доверия не даёт.
  • Второй режим работает наоборот: команда keygen генерирует ключ и файлы для каталога http-message-signatures-directory, после публикации которых любой верификатор Web Bot Auth в интернете опознаёт агента разработчика.

Проверка подписей до сих пор оставалась делом инфраструктурного слоя: агентов OpenAI отличали от ботов на уровне Cloudflare и AWS WAF, а приложение получало уже отфильтрованный трафик. regent-httpsig переносит ту же операцию в код сервиса, где решение о доступе принимает бизнес-логика, а не пограничный прокси. Судя по набору тестов, основная сложность приходится на разнобой форматов: OpenAI отправляет легаси-форму заголовка Signature-Agent, тогда как текущая редакция черновика описывает другую.

Отсутствие подписи ничего не стоит, испорченная подпись возвращает None

Проверка по умолчанию оформлена как обогащение запроса. Отсутствие заголовка Signature не требует затрат, некорректная подпись даёт None, и на недоверенном вводе библиотека не бросает исключений. Для маршрутов, где подпись обязательна, в обвязке для FastAPI предусмотрена зависимость RequiredSignatureDep: она отвечает кодом 401 с описанием того, как агенту следует подписать запрос.

Интеграция с FastAPI сводится к вызову attach и зависимости SignatureDep, которая отдаёт обработчику объект подписи с полями agent и keyid. За обратным прокси агент подписывает публичный URL, а ASGI-сервер видит внутренний адрес контейнера, поэтому зависимость собирает подписанный URL заново из X-Forwarded-Proto и Host; прокси обязан пробрасывать схему, в nginx это proxy_set_header.

Обратное направление закрывает класс EgressSigner: он принимает seed ключа и адрес агента, а возвращает готовый набор заголовков, который затем уходит с запросом, например через httpx. Команда regent-httpsig keygen создаёт ключ и файлы каталога .well-known за один вызов, после чего каталог публикуется по адресу агента.

Библиотека принимает два диалекта: Web Bot Auth и идентификационный режим AAuth

Web Bot Auth ищет ключи по адресу вида {Signature-Agent}/.well-known/http-message-signatures-directory и принимает обе проводные формы заголовка: текущий sf-dictionary и легаси-строку, которую, по описанию проекта, OpenAI использует в продакшене. Поддержка AAuth ставится отдельным экстра-пакетом, командой pip install 'regent-httpsig[aauth]'.

В идентификационном режиме AAuth агент передаёт JWT-токен agent_token в заголовке Signature-Key: токен проверяется по JWKS издателя, а ключ из cnf.jwk проверяет подпись самого запроса. Реализация следует редакторской копии -11 с полностью специфицированными алгоритмами по RFC 9864 и флагом перехода для EdDSA из экосистемы -10; токены вида aa-person+jwt включаются через параметр HttpsigConfig.resource_url.

В CI закреплены семь проверок: побайтовое совпадение с Ed25519-вектором из приложения B.2.6 к RFC 9421, оба варианта Signature-Agent из черновика Web Bot Auth -05, полный цикл подписи и проверки на свежих ключах, роундтрип AAuth, кросс-библиотечная совместимость с aauth-signing и отклонение подделанных, просроченных и подписанных чужим ключом запросов.

Каталоги ключей загружаются только по https и только с публичных адресов

Верификатор запрашивает каталоги ключей у источников, которые называет сам подписант, поэтому ограничения включены по умолчанию: только https, каждый разрешённый IP должен быть публичным, редиректы не выполняются, размер ответа ограничен. Под фильтр попадают 169.254.169.254, loopback, приватные диапазоны и DNS-имена, указывающие на внутренние сервисы.

Кэш ключей живёт внутри экземпляра, имеет TTL и вытеснение, поэтому поток запросов с новыми keyid не разрастается в памяти; неудачные обращения кэшируются отдельно, чтобы мёртвый источник не замедлял проверку. Поле VerifiedSignature.trusted отражает только настроенный список доверенных агентов, а решение о доверии к ключу остаётся за прикладной политикой.

Класс HttpsigConfig принимает набор доверенных агентов, параметр max_age_hours со значением 25 и cache_ttl в 600 секунд, а общий http-клиент приложения передаётся отдельным аргументом ради переиспользования пула соединений. Обходы известных проблем http-message-signatures вшиты внутрь: резолвер компонентов для членов словаря с параметром ;key=, обёртка над регистрозависимым поиском заголовков и объявление typing_extensions.

Что отправлено в апстрим

Байты подписи из примера A.2.2 самого черновика не проверяются над его же базой подписи, о чём авторы сообщили разработчикам спецификации, зафиксировав в тесте вектор, переподписанный тем же тестовым ключом. Второе расхождение направлено в проект aauth-signing: он кодирует последовательность байт Signature в base64url, тогда как RFC 8941 требует стандартный base64.

Web Bot Auth и AAuth остаются черновиками IETF, тогда как RFC 9421 уже финальный стандарт; пока библиотека в ветке 0.x, ломающие изменения черновиков будут выходить минорными релизами, а сроки версии 1.0 не называются. Поддерживается только Ed25519, покрытие тела запроса через content-digest проверяется, но не требуется.

Комментарии

Пока никто не написал. Будьте первым.

Присоединяйтесь к разговору

Войдите через Google, чтобы оставить комментарий. Имя и аватар подставятся из вашего профиля Google, а комментарий появится после модерации.

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