Читать 7 мин

Путаница типов содержимого в аутентифицированных вызовах API агентов

Путаница типов содержимого позволяет аутентифицированным вызовам агентов обходить намерение запроса. Тестируйте JSON, формы, multipart и пустые тела по одной схеме.

Путаница типов содержимого в аутентифицированных вызовах API агентов

Аутентифицированный вызов агента должен одинаково интерпретироваться на всём пути от сетевого края до обработчика действия. Если шлюз видит безобидный JSON-запрос, слой авторизации получает один набор полей, а обработчик видит привилегированную отправку формы, учётные данные сработали, но API всё равно оказался уязвим.

Это не узкая проблема старых браузерных форм. Агенты напрямую формируют HTTP-запросы, настойчиво повторяют их, используют примеры из описаний инструментов и часто работают с учётными данными, которые позволяют менять реальные системы. Тело запроса участвует в решении об авторизации, если оно выбирает цель, сумму, окружение, команду или разрешение. Прежде чем решать, может ли вызывающая сторона выполнить действие, нужно сделать тип содержимого, синтаксис и схему однозначными.

Аутентифицированный запрос всё равно должен иметь одно значение

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

Рассмотрим конечную точку, которая меняет цель развёртывания:

POST /v1/deployments/promote HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/json

{"environment":"staging","release":"2026.07.22"}

Код авторизации может разрешать продвижение в staging, но запрещать его для production. Этот код корректен только в том случае, если получает то же значение environment, которое использует обработчик действия. Если один слой читает JSON, обработчик позже обращается к параметрам формы, а оба источника могут заполнить один объект запроса, вы создали два источника истины.

Для этого не нужен сломанный криптографический токен. Агент с легитимной сессией может отправить тело, которое один слой проигнорирует, а другой учтёт. То же может сделать скомпрометированный агент. В итоге обход авторизации выражается в формате входных данных.

RFC 9110 говорит, что Content-Type указывает тип содержимого связанного представления и определяет формат данных и способ их обработки получателем. Значит, этот заголовок относится к смыслу запроса, а не служит украшением. Тот же RFC позволяет получателю без Content-Type считать содержимое octet-stream или изучить сами данные. Это удобно для универсальной работы с файлами, но опасно как значение по умолчанию для защищённых API действий.

Для конечной точки действия установите такой инвариант:

Ровно один разрешённый тип содержимого преобразует байты запроса ровно в один проверенный объект команды. Все решения безопасности и побочные эффекты используют этот объект.

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

Заголовок Content-Type не является схемой

Content-Type: application/json не означает: «это форма запроса, которую я ожидал». Он означает, что отправитель заявляет: тело использует тип содержимого JSON. Вам всё ещё нужно решить, поддерживается ли этот тип данным маршрутом, разрешены ли параметры, корректно ли тело с точки зрения синтаксиса и соответствует ли разобранное значение контракту операции.

Защищённая конечная точка должна намеренно принимать небольшой набор представлений. Многие конечные точки команд должны принимать только JSON. Конечная точка загрузки может принимать только multipart. Действие без аргументов должно принимать только отсутствие тела. Чем шире набор разрешённых вариантов, тем больше путей парсинга вы обязаны поддерживать.

OWASP REST Security Cheat Sheet даёт простой практический совет: документировать поддерживаемые типы содержимого и отклонять неожиданные или отсутствующие типы, разрешая отсутствие Content-Type для запроса с нулевой длиной. В рекомендациях также говорится, что тело и заявленный тип должны соответствовать друг другу, иначе производитель и потребитель могут понять запрос по-разному.

Для аутентифицированных действий эту рекомендацию нужно уточнить. Не «сопоставляйте» заявленный тип, изучая первый символ и выбирая парсер. Тело, начинающееся с {, не даёт права обрабатывать запрос, объявленный как form, через JSON-парсер. Анализ содержимого превращает ясный контракт в догадку о реализации.

Полезный контракт маршрутов может выглядеть так:

МаршрутРазрешённый тип содержимого запросаПравило для тела
POST /v1/deployments/promoteapplication/jsonОбязательный JSON-объект, соответствующий PromoteRequest
POST /v1/artifactsmultipart/form-dataОбязательные части, соответствующие ArtifactUpload
POST /v1/sessions/revokeотсутствуетДолжно содержать ноль байт

Точно определяйте параметры типа содержимого. Если ваш JSON-парсер принимает application/json; charset=utf-8, задокументируйте это и нормализуйте параметры одной библиотекой. Если принимается только application/json, отклоняйте параметр, не позволяя прокси и приложению расходиться во мнениях. Конкретный выбор менее важен, чем его единообразное применение.

Также разделяйте предпочтение формата ответа в Accept и Content-Type запроса. Клиент может запросить JSON-ответ и одновременно отправить недопустимое тело. Никогда не позволяйте заголовку Accept расширять набор форматов запроса, которые будет разбирать конечная точка действия.

Для JSON нужны правила, выходящие за пределы синтаксической корректности

JSON-парсер может успешно разобрать ввод, который API всё равно обязан отклонить. Очевидный пример, повторяющиеся имена членов объекта:

{"environment":"staging","environment":"production","release":"2026.07.22"}

RFC 8259 говорит, что имена объектов должны быть уникальными, и объясняет почему: получатели не одинаково обрабатывают повторы. Многие сохраняют последнее значение, некоторые завершаются ошибкой, а другие показывают каждую пару. Это задокументированная проблема совместимости, а не теоретическое требование к стилю.

Представьте, что middleware авторизации использует парсер, сохраняющий первое значение environment, а последующий декодер оставляет последнее. Middleware разрешает staging, а обработчик продвигает production. Это нельзя исправить более удачными именами ролей или ещё одним требованием в токене. Запрос нужно отклонить до того, как любой компонент примет решение.

То же относится к значениям, которые в языке с нестрогими типами выглядят безобидно:

  • Отклоняйте неизвестные члены объекта для запросов действий, если у вас нет явно указанной причины сохранять их ради совместимости.
  • Требуйте ожидаемый тип JSON. Boolean не превращается в строку только потому, что в ней написано true, а целочисленный идентификатор не является числом с плавающей точкой.
  • Устанавливайте ограничение размера тела до начала парсинга. Валидатор схемы не защитит память, которую вы уже исчерпали чтением огромного тела.
  • Решите, может ли поле отсутствовать, иметь значение null или быть пустой строкой. Это три разных состояния.
  • Отклоняйте данные после основного значения и расширения парсера, такие как комментарии, NaN или имена без кавычек, если библиотека их поддерживает.

Не проводите авторизацию непосредственно по универсальной map-структуре. Декодируйте запрос в тип с явной схемой, выполните семантическую проверку, а затем создайте внутренний тип команды, который не хранит необработанные артефакты парсера. Обработчику, который получает PromoteCommand { environment, release }, сложнее переосмыслить ввод, чем обработчику, который получает map, коллекцию query-параметров, объект запроса и необработанное тело.

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

Тела форм создают скрытые правила для массивов и вложенности

application/x-www-form-urlencoded кажется простым, потому что напоминает строку запроса. Но всё меняется, когда библиотеки начинают придавать смысл повторяющимся именам, скобочной записи, плюсам и пустым значениям.

Рассмотрим такие тела:

role=user&role=admin
role[]=user&role[]=admin
role[user]=1&role[admin]=1
role=user%26role%3Dadmin

Разные фреймворки могут трактовать их как последнее или первое скалярное значение, массив, объект, буквальные имена полей или ошибку парсинга. Некоторые middleware разбирают формы для любого метода запроса. Некоторые фреймворки объединяют query-параметры и параметры формы в удобный объект. Именно в таком объекте защищённые API теряют понимание того, что на самом деле отправил вызывающий.

В рекомендациях OWASP по тестированию HTTP parameter pollution отмечается, что поведение зависит от взаимодействия приложения, веб-сервера, WAF и middleware. Поэтому нужно тестировать необработанные повторяющиеся параметры, а не полагаться на документацию парсера одного фреймворка.

Популярная рекомендация принимать JSON и URL-кодированные формы на каждой конечной точке «ради совместимости клиентов» обычно ошибочна. Она сохраняется, потому что упрощает написание демонстрационного клиента и потому что многие фреймворки включают такое поведение по умолчанию. Но для каждого действия это вдвое увеличивает число контрактов представления, а затем незаметно добавляет третий контракт, когда query-поля объединяются с телом.

Если форму всё же нужно поддерживать, задайте для такой конечной точки отдельную политику парсинга:

  1. Отклоняйте повторяющиеся имена, если схема не определяет это поле как список.
  2. Отклоняйте скобочную запись, если схема не определяет её точную форму и ваш парсер не обрабатывает её единообразно.
  3. Держите query-параметры отдельно от полей формы. Не позволяйте одному источнику перезаписывать другой.
  4. Преобразуйте разобранные поля в тот же типизированный внутренний объект команды, который используется маршрутом JSON, только после проверки.
  5. Тестируйте процентное кодирование, + и %20, пустые значения, отсутствие = и дублирующиеся поля через рабочий путь обработки запросов.

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

Multipart предназначен для загрузок, а не для гибкого JSON

Отзывайте запуск агента
Журнал Sessions записывает каждый запуск агента и позволяет немедленно отозвать активную сессию.

multipart/form-data решает конкретную задачу: передаёт несколько независимо заголовленных частей, часто с файлами. RFC 7578 определяет его для значений форм и требует параметр boundary, отделяющий части. Каждая часть также может содержать собственные заголовки и метаданные имени файла.

Из-за такой структуры multipart плохо подходит как запасной формат для обычных аутентифицированных команд. В нём больше синтаксиса, сложнее обработка размера, больше мест для повторяющихся имён полей и больше возможностей для шлюза прочитать одну часть, а приложению выбрать другую.

Распространённая неудачная конструкция принимает часть JSON metadata вместе с файлом, а затем также принимает поля верхнего уровня, которые могут переопределить metadata:

Content-Disposition: form-data; name="metadata"

{"project":"alpha","visibility":"private"}

Content-Disposition: form-data; name="visibility"

public

Один компонент может авторизовать запрос по metadata.visibility, а другой привязать последующую часть формы к параметру visibility обработчика. В запросе появляются два значения одного чувствительного к безопасности свойства, выраженные на двух языках.

Стройте multipart-конечные точки вокруг именованных частей с разными задачами. Например, принимайте ровно одну часть file и ровно одну часть manifest. Требуйте, чтобы manifest был JSON со своей строгой схемой. Отклоняйте имена частей, не перечисленные в контракте загрузки, повторяющиеся одиночные части, задавайте отдельные лимиты для всего тела и файла и решите, обязательны ли значения Content-Type для частей.

Не доверяйте имени файла как пути, MIME-заявлению как классификации файла или поведению multipart-парсера с временными файлами как средству защиты. Это отдельные проблемы загрузки. Правило против путаницы парсеров проще: входные данные для авторизации должны поступать из одного именованного проверенного источника. Если manifest.project определяет место сохранения файла, никакая другая часть, query-параметр или заголовок не должны менять этот проект.

Если команда не содержит файла, не принимайте multipart. Каждый дополнительный разрешённый тип содержимого даёт ещё один способ заставить компоненты разойтись в интерпретации.

Пустое тело является контрактом, а не отсутствием проверки

Некоторым аутентифицированным действиям аргументы не нужны. Отзыв текущей сессии, смена nonce, созданного сервером, или подтверждение фиксированного события могут использовать пустое тело. В таких случаях отсутствие содержимого должно проверяться явно.

Конечная точка с контрактом «без тела» должна отклонять всё перечисленное ниже:

POST /v1/sessions/revoke HTTP/1.1
Content-Type: application/json
Content-Length: 2

{}
POST /v1/sessions/revoke HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

scope=other
POST /v1/sessions/revoke HTTP/1.1
Transfer-Encoding: chunked

0

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

Для маршрута без тела установите такие правила до бизнес-логики:

  • Запрос не содержит байтов содержимого.
  • Маршрут не принимает Content-Type, кроме явно разрешённого правила совместимости.
  • Маршрут не объединяет query-параметры с командой, если каждое разрешённое имя query-параметра не имеет собственной схемы.
  • Сервер записывает действие как не имеющее аргументов, а не сохраняет универсальный объект запроса, который позже можно принять за входные данные.

RFC 9110 описывает содержимое запроса с учётом семантики метода и не придаёт телу универсального смысла только потому, что запрос использует POST. Этот смысл задаёт контракт ресурса.

Неловкий случай возникает, когда библиотека клиента всегда отправляет {}. Не расширяйте конечную точку только ради неё. Исправьте клиент или создайте отдельный задокументированный маршрут. Тело, которое сейчас ни на что не влияет, после изменения обработчика легко становится случайным каналом ввода.

Проверяйте данные до авторизации и выполняйте действие из проверенной команды

Разделяйте намерение и учётные данные
Sallyport выполняет HTTP-действия из зашифрованного хранилища и возвращает агенту только результат.

Самый безопасный конвейер обработки запроса движется в одном направлении. Поступают необработанные байты. Маршрут выбирает один разрешённый парсер. Парсер создаёт типизированное значение. Проверка формирует каноническую команду. Авторизация оценивает эту команду. Исполнитель получает ту же команду.

raw HTTP request
  -> route and media-type check
  -> bounded body read
  -> one strict parser
  -> schema and semantic validation
  -> canonical command
  -> authorization
  -> execution and audit record

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

Каноническая команда является практической границей, а не элементом диаграммы. Она должна содержать только значения, необходимые исполнителю, и исключать необработанный текст тела, коллекции форм, объекты запроса фреймворка и псевдонимы. Если исполнитель получает target_environment, он не должен позже обращаться к req.query.environment, потому что цель отсутствовала или была неудобна.

Это улучшает и аудит. Записывайте аутентифицированного субъекта, конечную точку, принятый тип содержимого, дайджест запроса, безопасные для хранения поля канонической команды, решение авторизации и результат. Без необходимости не сохраняйте необработанные тела: в них могут быть учётные данные, загруженные файлы и пользовательские данные. Дайджест позволяет связать событие с сохранённым доказательством, не превращая логи в хранилище секретов.

Для подписи запросов нужна та же дисциплина. Если клиент подписывает байты, а сервер авторизует нормализованный объект, зафиксируйте и правила подписываемого представления, и правила каноникализации. Если клиент подписывает канонический объект, отклоняйте все альтернативные представления до проверки подписи. Иначе две последовательности байтов могут нести один бизнес-запрос или одна последовательность может получить другое значение после парсинга.

Тестируйте разногласия, а не только успешный парсинг

Модульные тесты, которые десериализуют один корректный JSON-файл, почти ничего не говорят о согласованности парсеров. Цель тестирования, публичный путь запроса: балансировщик или обратный прокси, шлюз, middleware фреймворка, обработчик маршрута и любой сервис, который повторно разбирает тело.

Создайте небольшой набор негативных тестов для каждой аутентифицированной операции. Запускайте его в CI в отдельном окружении и проверяйте как ответ, так и отсутствие побочных эффектов. Ответ 400 ничего не доказывает, если до него уже были отправлены сообщения в очередь, создано событие аудита или частично записан файл.

Начните с этого shell-теста. Он намеренно отправляет необработанные тела, вместо того чтобы использовать сгенерированный клиент, отбрасывающий некорректные данные:

base=https://api.test.example
bearer='test-token'

send() {
  name=$1
  type=$2
  body=$3
  code=$(curl -sS -o "/tmp/${name}.out" -w '%{http_code}' \
    -X POST "$base/v1/deployments/promote" \
    -H "Authorization: Bearer $bearer" \
    -H "Content-Type: $type" \
    --data-binary "$body")
  printf '%-28s %s\n' "$name" "$code"
}

send valid_json 'application/json' \
  '{"environment":"staging","release":"2026.07.22"}'
send duplicate_json 'application/json' \
  '{"environment":"staging","environment":"production","release":"2026.07.22"}'
send form_body 'application/x-www-form-urlencoded' \
  'environment=production&release=2026.07.22'
send false_json 'application/json' \
  'environment=production&release=2026.07.22'

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

valid_json                   200
 duplicate_json               400
form_body                    415
false_json                   400

Ваша система может использовать 422 для синтаксически корректного запроса, который не проходит схему. Важно сохранить различие: несоответствие типа содержимого никогда не должно попадать в запасной парсер, а повторяющееся имя в JSON никогда не должно доходить до авторизации.

Расширьте набор случаями, нацеленными на границы между компонентами:

СлучайЧто должно произойти
Отсутствует Content-Type, но тело непустоеОтклонить до парсинга
JSON-объект содержит неизвестное полеОтклонить или применить задокументированное правило совместимости
Повторяющееся скалярное поле формыОтклонить
Значение query конфликтует со значением JSONОтклонить или игнорировать query согласно контракту маршрута
Multipart содержит две части manifestОтклонить
Маршрут без тела получает {}Отклонить

Затем изучите аудитный след. Для каждого отклонённого ввода должна существовать трассировка с маршрутом и классом отказа, но без чувствительного содержимого запроса. Каждый принятый ввод должен давать одну каноническую команду. Если в логах видно, что шлюз получил одну цель, а обработчик записал другую, вы нашли разногласие, даже если тест завершился ответом 2xx.

Прокси и middleware тоже являются парсерами

Подключайте агентов без bearer-токенов
Дайте Claude Code доступ к HTTP через `sp mcp`, не помещая bearer-учётные данные в его контекст.

Команды часто указывают на парсер приложения и забывают о компонентах перед ним. Обратные прокси могут нормализовать заголовки. API-шлюзы могут изучать JSON для применения правил. WAF может разбирать данные формы. Middleware для наблюдаемости может читать и заново собирать тело. Фреймворк может заполнить query-, form- и JSON-поля до запуска обработчика маршрута.

В рекомендациях OWASP по HTTP request smuggling описан более масштабный вариант этой проблемы: посредники и серверы могут по-разному интерпретировать границы запроса, особенно при преобразовании протоколов и фрейминге. Путаница типов содержимого не требует request smuggling, чтобы стать опасной, но обе проблемы возникают, когда разным слоям позволяют принимать несовместимые решения о парсинге.

Составьте список всех компонентов, читающих тело на пути действия. Для каждого укажите, какие типы содержимого он разбирает, сохраняет ли повторяющиеся значения, распаковывает ли содержимое, устанавливает ли ограничение размера и может ли переписать тело. Если никто не может ответить на эти вопросы, конечная точка не готова работать с учётными данными агентов.

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

Тестируйте версии HTTP и варианты развёртывания, которые действительно используются в production. Запрос, корректный на локальном сервере разработки, может повести себя иначе, когда клиент HTTP/2 обращается к прокси, пересылающему запрос приложению по HTTP/1.1. Цель не в том, чтобы создать лабораторию для исследования атак. Нужно, чтобы рабочая цепочка доказывала: для каждого принятого запроса она создаёт один объект команды.

Шлюзы агентов должны сохранять эту границу

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

HTTP-канал Sallyport подставляет учётные данные, не раскрывая API-ключи агенту. Благодаря этому агент может запросить действие, не получая сам секрет. Это полезная граница для учётных данных. Её нужно дополнить контрактами конечных точек, которые отклоняют неоднозначные тела, потому что защищённые учётные данные всё равно авторизуют запрос, дошедший до API.

Давайте агентам инструменты, отражающие контракт, вместо универсального действия «выполнить любой HTTP-запрос» для чувствительных систем. Инструмент продвижения должен принимать типизированные аргументы environment и release. Его реализация должна сериализовать один JSON-объект, установить один тип содержимого и отклонять входные данные инструмента, не соответствующие схеме API. Принимающий сервис должен повторить проверку. Схемы инструментов уменьшают количество ошибок, но не заменяют недоверие на стороне сервера.

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

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

Вопросы и ответы

Что такое путаница типов содержимого в API?

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

Почему путаница типов содержимого опасна для ИИ-агентов?

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

Гарантирует ли application/json безопасный JSON-запрос?

Нет. application/json сообщает только заявленный тип содержимого. Он не гарантирует корректный JSON, уникальные имена полей, правильные типы значений или соответствие форме запроса. Разбирайте JSON строго, отклоняйте повторяющиеся имена, а затем проверяйте результат по схеме конечной точки.

Должен ли JSON API принимать запросы application/x-www-form-urlencoded?

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

Когда аутентифицированному API следует разрешать multipart/form-data?

Только если конечной точке нужна загрузка файлов или существующий протокол клиента требует multipart. Обрабатывайте multipart как отдельный путь с собственным парсером и схемой, ограничивайте имена частей и их заголовки, не принимайте его молча как другой вариант JSON-действия.

Может ли аутентифицированная конечная точка POST требовать пустое тело?

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

Какой код состояния должен возвращать API для неправильного Content-Type?

Строгая конечная точка возвращает ошибку клиента до проверки авторизации и запуска бизнес-логики. Используйте 415 для неподдерживаемого типа содержимого, 400 для некорректного синтаксиса и 422, когда синтаксически корректный запрос не соответствует схеме конечной точки, если эти различия вписываются в правила вашего API.

Как проверить API на разногласия между парсерами?

Проверяйте необработанные запросы через тот же внешний контур, шлюз и путь приложения, что используются в продакшене. Для каждой защищённой операции меняйте тип содержимого, повторяющиеся поля, повторяющиеся параметры, заголовки частей multipart, длину тела и кодирование содержимого. Убедитесь, что каждый недопустимый вариант завершается до уровня выполнения действия.

Может ли API-шлюз или WAF самостоятельно решить проблему путаницы парсеров?

WAF или API-шлюз может отклонять очевидно неправильный ввод, но сам является ещё одним парсером и может создать другую интерпретацию. Приложение, которое авторизует и выполняет действие, всё равно должно разобрать и проверить одно каноническое представление.

Должно ли одобрение человеком заменять проверку схемы каждого запроса?

Одобрение должно относиться к процессу агента и выданным ему возможностям, а API всё равно должен проверять тело каждого запроса так, как если бы его отправил вредоносный код. Одобрение человеком не заменяет проверку формата и не устраняет неоднозначность, возникшую после отправки запроса агентом.

Sallyport

Sallyport выполняет API-вызовы и SSH-команды за вашего ИИ-агента. Ключи остаются в локальном хранилище на вашем Mac; вы подтверждаете каждый запуск, и каждое действие попадает в запечатанный журнал.

© 2026 Sallyport · Открытый код по лицензии Apache-2.0 · Oleg Sotnikov