Читать 6 мин

Для загрузок файлов AI-агентами нужны жесткие границы

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

Для загрузок файлов AI-агентами нужны жесткие границы

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

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

Действие загрузки должно заранее описывать, что может покинуть систему

Безопасный процесс загрузки начинается с того, что каждый файл рассматривается как объект с заявленной целью. Цель определяет максимальный размер, допустимые форматы, разрешенного получателя, срок хранения и необходимость подтверждения человеком. Если API принимает произвольное multipart-тело и URL, переданный вызывающей стороной, вы создали универсальный маршрут вывода данных с удобным интерфейсом для разработчиков.

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

Вместо универсального действия upload_file определите именованные цели загрузки. Цель diagnostic_bundle может разрешать сжатый пакет поддержки только одному получателю из службы поддержки. Цель invoice_export может разрешать CSV для получателя из бухгалтерии. Ни одна из них не должна принимать в одном запросе путь, URL получателя и произвольный файл.

Небольшой контракт делает границу видимой:

{
  "purpose": "diagnostic_bundle",
  "file_path": "/private/tmp/app-diagnostics-2025-03-08.zip",
  "destination_id": "support-case",
  "case_reference": "CASE-1842"
}

Вызывающая сторона выбирает разрешенную цель и передает метаданные, нужные для бизнес-действия. Сервис загрузки сопоставляет support-case с получателем, которым управляет сам. Он не позволяет заменить этого получателя URL из комментария к задаче, документа или ответа инструмента.

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

Для размера нужны два измерения

Ограничение файла должно отклонять слишком большое тело до того, как сервис сохранит, просканирует или перешлет его. Установите лимит на уровне HTTP, используя Content-Length, если заголовок присутствует, а затем считайте байты во время чтения, потому что клиент может не передать этот заголовок или указать в нем ложное значение.

Лимит должен соответствовать цели. Ограничение 25 МБ для клиентского CSV и такое же ограничение для сжатого диагностического пакета не означают одно и то же. При чтении парсер может развернуть CSV в гораздо больший объем памяти. Архив после распаковки может во много раз превысить транспортный размер. Задайте и лимит размера в передаче, и лимит размера после обработки.

Не позволяйте агенту разбивать отклоненный файл на множество допустимых запросов, если получатель явно не поддерживает порционную загрузку, а ваш сервис не отслеживает общий объем. Иначе правило в 10 МБ превратится в передачу из 100 частей без большей части защиты, на которую вы рассчитывали.

Отклоняйте файл как можно раньше и возвращайте ответ, который поможет агенту безопасно восстановиться:

{
  "error": "attachment_too_large",
  "purpose": "diagnostic_bundle",
  "observed_bytes": 12582911,
  "max_bytes": 8388608,
  "safe_alternatives": [
    "create_redacted_diagnostic_bundle",
    "attach_selected_log_window"
  ]
}

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

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

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

Имя файла и MIME-заголовок почти ничего не доказывают

Проверяйте тип файла по независимым признакам, потому что расширение и заголовок Content-Type приходят от отправителя. Агент может передать вводящую в заблуждение метку без злого умысла. Инструмент поддержки может называть каждое вложение application/octet-stream. В любом случае получатель должен принимать решение по байтам и разрешенной структуре.

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

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

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

Для экспорта CSV примите небольшой список разрешенных вариантов, например .csv и текст в UTF-8, разберите ограниченный фрагмент и отклоните встроенные двоичные данные или неожиданно широкие строки. Для PDF проверьте сигнатуру %PDF-, примените ограничение размера и, если нужно просматривать страницы, используйте парсер с ограничениями по времени и памяти. Для изображений определите размеры до обработки: даже небольшой файл может потребовать чрезмерно много памяти после декодирования.

Не используйте общий тип «архив». ZIP, TAR и GZIP различаются, и каждый формат требует отдельной проверки. Если бизнес-процессу архив не нужен, отклоняйте его. Именно так, когда формат принимают «на всякий случай», появляются универсальные конечные точки вложений.

Переименовывайте принятые файлы на стороне сервиса. Исходное имя можно сохранить как отображаемые метаданные после очистки, но не используйте его как путь файловой системы, ключ в объектном хранилище или значение Content-Disposition без экранирования. Имена могут содержать управляющие символы, обманчивые символы Unicode, разделители пути и строки, меняющие последующие журналы.

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

Список разрешенных получателей должен указывать точное место, которому можно передать файл. Разрешения https://example.com недостаточно, если HTTP-клиент следует перенаправлениям на другой хост, разрешает внутренний адрес или принимает другой порт.

Храните назначения как записи на стороне сервера с фиксированными схемой, хостом, портом, префиксом пути, идентификатором учетных данных и разрешенными целями. Действие получает destination_id, а не произвольную конечную точку. Если получателю нужен идентификатор дела в пути, создайте путь из ограниченного идентификатора, а не принимайте полный URL от агента.

При каждой отправке HTTP-клиент должен выполнять такие проверки:

  1. Требуйте HTTPS, если у вас нет документированного внутреннего исключения.
  2. До подключения сверяйте запрошенные хост и порт с записью назначения.
  3. По умолчанию отключайте перенаправления. Если получателю они необходимы, проверяйте каждое новое назначение по той же записи до отправки следующего байта.
  4. Отклоняйте IP-литералы, loopback-адреса, link-local-адреса и частные диапазоны, если такое назначение явно не создано для контролируемого внутреннего сервиса.
  5. Фиксируйте разрешенный префикс пути и HTTP-метод, а не разрешайте весь хост.

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

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

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

Для экспорта журналов нужен отдельный продуманный маршрут

Подтверждайте чувствительные отправки каждый раз
Включите для ключа загрузки подтверждение каждого вызова, одним нажатием или через Touch ID.

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

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

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

Например, такая форма диагностической записи безопаснее необработанной трассировки HTTP:

{
  "time": "2025-03-08T14:22:11Z",
  "request_id": "local-7f3c",
  "method": "POST",
  "route": "/v1/reports",
  "status": 502,
  "upstream": "reporting-service",
  "authorization": "[removed]",
  "body": "[omitted]"
}

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

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

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

Архивы и офисные документы скрывают больше одного файла

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

ZIP-файл размером 2 МБ на диске может распаковаться до объема, который исчерпает ресурсы рабочего процесса или получателя. Это обычно называют бомбой распаковки, но проблема не ограничивается вредоносными входными данными. Системы сборки могут случайно создавать огромные архивы, а агент может прикрепить первый файл, похожий на экспорт.

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

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

Контракт действия должен делать небезопасные запросы невозможными

Отделите учетные данные от правил загрузки
Sallyport отвечает за учетные данные HTTP, а ваш сервис загрузки контролирует артефакты и назначения.

Инструмент для агента должен предлагать варианты, соответствующие вашим средствам контроля, а не конструктор необработанных HTTP-запросов. Если инструмент предоставляет url, headers, file_path и method, политика уже потеряла большую часть своей формы.

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

{
  "action": "send_attachment",
  "purpose": "customer_export",
  "destination_id": "finance-import",
  "artifact_id": "exp_8c4e1a",
  "note": "March reconciliation correction"
}

Сервис сопоставляет artifact_id с подготовленным объектом, который создал сам или принял через отдельный процесс поступления. Он самостоятельно вычисляет дайджест и определяет тип. destination_id сопоставляется с фиксированной записью получателя. Учетные данные получателя добавляются в момент отправки. Агент их не видит и не может после подтверждения подменить получателя.

Результат предварительной проверки должен содержать достаточно сведений для осознанного решения:

{
  "decision": "approval_required",
  "artifact": {
    "name": "reconciliation-2025-03.csv",
    "bytes": 482913,
    "detected_type": "text/csv",
    "sha256": "a4d1...c09e"
  },
  "destination": {
    "label": "Finance import",
    "host": "imports.example.internal",
    "path": "/v2/reconciliation"
  },
  "reason": "customer_export requires approval"
}

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

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

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

Подтверждение человека работает, когда обозначает настоящую границу

Подтверждение не заменяет проверку. Человек не сможет надежно определить бомбу распаковки, замаскированный файл или ошибку перенаправления по одному модальному окну. Подтверждение нужно для решения, которое политика не может принять автоматически: следует ли отправлять именно этот клиентский экспорт именно этому получателю по этому инциденту.

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

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

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

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

Проверьте пути отказа до того, как их найдет агент

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

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

Начните с обычного запроса загрузки к тестовому приемнику:

curl -i -X POST https://receiver.test/attachments \
  -H 'Authorization: Bearer test-token' \
  -F 'file=@fixtures/diagnostic.zip;type=application/zip' \
  -F 'case_reference=CASE-1842'

Успешный тест должен подтвердить метод, конечный хост, путь, дайджест и размер. Особенно полезны тесты, доказывающие, что приемник ничего не получил. Убедитесь, что тело сверх лимита отклоняется до пересылки, поддельный заголовок text/plain не позволяет пропустить двоичные данные, а неразрешенное назначение не получает даже соединения.

Добавьте в набор такие тестовые случаи:

  • Допустимое расширение с несовпадающими байтами сигнатуры.
  • Сжатый архив, распакованный размер которого превышает правило.
  • Перенаправление с разрешенного хоста на запрещенный.
  • Файл журнала с заголовком авторизации и адресом электронной почты клиента.
  • Повторная попытка с тем же токеном идемпотентности, но другими байтами файла.

Изучайте серверные журналы во время тестов. Нужно проверить две вещи: передача не произошла, а ваши собственные журналы не сохранили отклоненное тело, токен bearer или подписанный URL. Команды часто исправляют сетевой маршрут, но оставляют те же чувствительные данные в трассировках исключений.

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

Сделайте безопасный маршрут проще необработанного

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

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

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

Первое изменение, которое я бы внес, простое: уберите произвольные URL и пути файлов из действия загрузки агента. Когда действие принимает только подготовленные артефакты, именованные цели и идентификаторы назначений, ограничения размера и проверки содержимого получают надежную точку приложения.

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

Достаточно ли ограничения размера файла для защиты загрузок AI-агентов?

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

Как AI-агенту загружать файлы без API-учетных данных?

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

Можно ли доверять MIME-типу в multipart-загрузке?

Не доверяйте ему. MIME-типы и расширения имен приходят от клиента и могут быть подделаны. Сверяйте расширение со списком разрешенных, проверяйте сигнатуру файла и разбирайте формат ограниченным парсером до того, как принять файл.

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

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

Что должен включать список разрешенных назначений для загрузки файлов?

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

Безопасно ли разрешать AI-агенту загружать журналы приложения?

Обычно нет. Полные журналы часто содержат заголовки авторизации, данные сессий, адреса электронной почты, внутренние URL и клиентские данные. Создайте специальный диагностический пакет, который удаляет или маскирует эти поля до загрузки.

Как безопасно разрешить ZIP-файлы в процессе загрузки?

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

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

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

Как проверить политику загрузки файлов AI-агентом?

Используйте изолированный тестовый приемник, который записывает метод, заголовки, размер тела, контрольную сумму и работу перенаправлений. Затем проверьте слишком большие файлы, поддельные типы, архивы, распаковывающиеся за пределы лимита, запрещенные хосты и повторные попытки после отказа.

Что должна записывать запись аудита загрузки файла агентом?

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

Sallyport

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

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