Менеджер секретов для AI-агентов: хранение не равно контролю действий
Менеджер секретов для AI-агентов защищает хранение, но разрешенное выполнение HTTP- и SSH-действий не передает учетные данные в контекст агента и записывает каждый вызов.

Менеджер секретов для AI-агентов необходим, но он не решает главную опасную проблему: что происходит после того, как агент запрашивает учетные данные. Если менеджер возвращает секрет процессу агента в открытом виде, секрет уже пересек границу, которую вы хотели защитить.
Это различие кажется придиркой, пока агент не выполнит отравленную инструкцию, не вызовет неожиданный инструмент, не запишет диагностический файл или не свяжется не с тем хостом. Хранилище может годами держать токен зашифрованным, а затем свести значительную часть этой защиты на нет одним вызовом получения. Для работы агентов безопаснее хранить учетные данные в доверенном исполнителе и позволять агенту запрашивать аутентифицированное действие.
Хранилище защищает секрет, но не его использование
Обычный менеджер секретов отвечает на вопрос хранения: кто может получить это значение? Для автономных агентов возникает другой вопрос: может ли этот процесс прямо сейчас выполнить конкретное действие с этим значением и обратиться к указанному адресату?
Многие команды начали с разумных привычек. Они убрали токены из репозиториев, зашифровали их при хранении, сменили после случайного коммита и добавили в окружение сборки. Для обычного развертывания приложений этого часто достаточно. У задания сборки обычно есть фиксированный скрипт, ограниченный срок работы и известный набор конечных точек. Окружение все еще может быть слишком широким, но команды заранее написал человек.
Агент устроен иначе. Он генерирует команды, выбирает инструменты, следует тексту задач, читает файлы, созданные другими людьми, и может менять собственный план. Если передать ему DEPLOY_TOKEN через переменную окружения, токен сможет прочитать каждый дочерний процесс. То же относится к команде оболочки, которая печатает окружение, отладчику, хуку установки пакета или ненадежному инструменту, выбранному агентом из-за текста в документе.
В такой ситуации менеджер секретов не сломался. Он сделал ровно то, для чего был настроен: передал секрет авторизованной рабочей нагрузке. Проблема в том, что модель авторизации слишком груба для участника, который сам выбирает следующую операцию.
Разделяйте два разрешения:
- право указать учетные данные по стабильной ссылке;
- право использовать их для описанного исходящего действия.
Первое разрешение может быть безопасным для агента. Второе должно учитывать само действие. Запрос вроде POST https://deploy.example.internal/releases может оценить проверяющий. Запрос вроде read production token передает актив, дальнейшее использование которого невозможно оценить в момент получения.
Это также исправляет распространенное, но бесполезное утверждение: «у агента уже есть выполнение кода, поэтому сокрытие секрета ничего не меняет». Выполнение кода на компьютере разработчика и так опасно. Но скопированный с него Bearer-токен создает отдельную проблему: он может покинуть компьютер, пережить окончание сессии и работать с совершенно другого устройства. Сокрытие секрета не устраняет весь риск, но убирает переносимую и долговечную форму доступа, которой особенно хотят завладеть злоумышленники.
RFC 6750 ясно описывает это свойство Bearer-токенов OAuth. Согласно определению, любой, кто владеет таким токеном, может его использовать. Протокол не различает утвержденного вами агента, вредоносный плагин и человека, нашедшего файл журнала. Обращаться с Bearer-токенами как с обычным выводом инструмента значит игнорировать их главное свойство.
Доступ на чтение превращает ошибки в инструкциях в утечки учетных данных
Агент с доступом к секрету в открытом виде может утечь им, даже не пытаясь украсть его. Опасная цепочка часто выглядит обычно до самого последнего шага.
Представьте, что агенту поручили выяснить, почему API развертывания отклонил выпуск. Он читает документ в репозитории, где сказано собрать пакет поддержки. Скрипт запускает env, копирует конфигурационные файлы и архивирует результат. Агент добросовестно создает архив и прикрепляет его к задаче или загружает в чат. Токен ни разу не показывался в терминале, но он прошел через окружение, архив, задачу и все системы резервного копирования и уведомлений, связанные с этой задачей.
Это не просто prompt injection. Подмена инструкций может привести к опасному действию, но скопированные учетные данные создают собственную область ущерба даже после исчезновения исходной инструкции. Позже другой читатель скачает файл из задачи, автоматический сканер проиндексирует его, а получатель воспользуется токеном спустя долгое время после завершения запуска агента.
Та же ошибка встречается в менее заметных формах:
- подробный HTTP-клиент выводит заголовок
Authorizationпри повторной попытке; - токен, переданный в командной строке, попадает в историю оболочки;
- тестовая фикстура записывает заголовок запроса и оказывается в коммите;
- дочерний процесс наследует ненужную переменную окружения;
- модель включает секрет в объяснение, потому что увидела его в выводе инструмента.
Популярный ответ на это, редактирование вывода. Оно помогает после ошибки, но не доказывает, что каждое направление вывода, формат архива, трассировка, дочерний процесс и удаленный сервис обработали значение правильно. Редактирование также плохо работает с неизвестными форматами секретов и токенами, разделенными между несколькими полями. Не делайте его главной защитой для учетных данных, которые агент вообще не должен был читать.
Лучше сузить входные данные агента. Агент отправляет намерение без самих учетных данных: HTTP-метод, разрешенный URL, тело запроса и ссылку на учетные данные. Доверенный компонент проверяет запрос, добавляет данные аутентификации внутри себя, отправляет запрос и возвращает ответ с удаленными чувствительными заголовками.
Такая граница дает службе реагирования на инциденты более ясный ответ. Если агент ведет себя плохо, можно отозвать его сессию или запретить следующие действия. Если вредоносную инструкцию обнаружили после запуска, не придется сразу предполагать, что каждый протокол, временный файл и удаленный артефакт содержит production-токен.
Выполнение действий, это другой интерфейс, не получение секретов
Граница выполнения должна принимать операцию, а не выдавать агенту непонятное значение в надежде, что он аккуратно им воспользуется. Именно это различие команды чаще всего размывают, превращая хранилище в автомат выдачи учетных данных.
Для HTTP агенту может понадобиться описать такой запрос:
{
"credential": "release-api",
"method": "POST",
"url": "https://deploy.example.internal/releases",
"headers": {
"Content-Type": "application/json"
},
"body": {
"version": "2025.03.8",
"environment": "staging"
}
}
Исполнитель находит release-api в защищенном хранилище, добавляет подходящую схему аутентификации и выполняет запрос. Агент может получить результат такого вида:
{
"status": 201,
"headers": {
"content-type": "application/json"
},
"body": {
"release_id": "rel_4821",
"state": "queued"
}
}
В ответе не должно быть добавленного заголовка Authorization, копии учетных данных или диагностических данных транспорта, которые их раскрывают. Это кажется очевидным, но граница часто ломается, когда разработчики считают журналирование запросов и ответов безобидной служебной деталью.
Для SSH агент должен передавать хост, учетную запись, команду и, возможно, ссылку на учетные данные. Исполнитель использует закрытый ключ во время SSH-аутентификации и возвращает стандартный вывод, ошибки и код завершения. Агент не получает PEM-блок или сокет агента, который можно использовать в другом месте.
Не путайте это с обычным прокси. Прокси пересылает произвольный трафик и может проверять или изменять его. Исполнитель действий решает более узкую задачу: хранит учетные данные, выполняет именованные HTTP- и SSH-операции, записывает решение и возвращает ограниченный результат. Узкая область это преимущество. Каждый новый протокол и универсальный обход дают агенту еще один способ превратить полномочия в запрос, который невозможно нормально проверить.
Сам интерфейс запросов тоже требует ограничений. Одной ссылки на учетные данные недостаточно. Если release-api может обращаться к множеству хостов или принимать произвольный URL, агент направит действительные учетные данные на контролируемый злоумышленником адрес или менее защищенный внутренний сервис. Привяжите учетные данные к нужной схеме аутентификации и разрешенным адресатам. Отклоняйте хитрые URL с неожиданными сегментами userinfo, перенаправлениями на новые хосты и именами, лишь похожими на разрешенные.
Исполнитель должен решить и вопрос возвращаемых данных. Полный ответ API может содержать пользовательские данные, токены, выданные нижестоящим сервисом, или конфигурацию, которую не стоит снова помещать в контекст модели. Если возвращать только поля, нужные для следующей операции, агент часто становится и надежнее, и безопаснее.
Для SSH нужен контроль команд, а не только защита закрытого ключа
Скрыть закрытый SSH-ключ полезно, но этого недостаточно, чтобы произвольные удаленные команды стали безопасными. Аутентификация сообщает серверу, кто подключился. Она не ограничивает действия учетной записи после запуска оболочки.
RFC 4252 описывает аутентификацию по открытому ключу SSH как подпись данных обмена. Закрытый ключ остается закрытым, поэтому SSH часто считают безопаснее API-токена в переменной окружения. В одном узком смысле это верно: клиент доказывает владение ключом, а не отправляет его по сети. Но процесс, способный попросить SSH-агент подписать данные, все равно может получить доступ к системам, доверяющим этому ключу.
К forwarding SSH-агента нужно относиться прямо. Он позволяет удаленному серверу использовать локальный агент аутентификации на протяжении соединения. Файл закрытого ключа удаленный сервер не получает, но может запрашивать подписи через перенаправленный сокет. Это допустимо лишь тогда, когда вы доверяете удаленной машине настолько, чтобы предоставить ей практическую возможность аутентифицироваться от вашего имени. Для автономного агента, который сам выбирает адрес и команду, это плохая граница.
Более безопасный интерфейс SSH явно называет адресат и команду. Нужно записывать оба значения, потому что ssh deploy@host "./deploy staging" и ssh deploy@host "cat /etc/shadow" используют один и тот же механизм аутентификации, но имеют совершенно разные последствия.
Ограничения на стороне сервера по-прежнему необходимы. Используйте отдельные учетные записи для разных задач. Учетной записи развертывания нужен доступ к каталогам развертывания, а не права администратора. Если сервер это поддерживает, настройте принудительные команды или ограниченные обертки команд для автоматизационных учетных данных. Не используйте общий личный ключ администратора для задач агента лишь потому, что он уже есть на рабочей станции.
Практический SSH-запрос может выглядеть так:
{
"credential": "staging-deployer",
"host": "staging-runner.internal",
"user": "deploy",
"command": "./release apply 2025.03.8",
"timeout_seconds": 120
}
Такой запрос дает проверяющему конкретный объект для одобрения, а вам, конкретную запись для последующего аудита. Если агенту нужна интерактивная оболочка, остановитесь и выясните причину. Интерактивные оболочки полезны людям, восстанавливающим систему. Для агента это плохой вариант по умолчанию: ограниченное действие превращается в открытую сессию, где каждая следующая команда наследует те же полномочия.
Важна и проверка хоста. Исполнитель должен использовать проверку известных хостов, а не принимать любой ключ, который ему предъявили по инструкции модели. Если агент может отключить проверку, сетевой злоумышленник получит команды, увидит вывод и, возможно, перехватит данные, отправленные агентом после входа.
При одобрении нужно указывать процесс и область действия
Кнопка одобрения человека мало полезна, если сообщает только, что доступ запросил «агент». Нужно знать, какой локальный процесс сделал запрос, какие подписанные полномочия его запустили и действует ли разрешение для этого запуска или для всех будущих запусков.
Запросы для каждого вызова кажутся самым безопасным вариантом, поэтому команды часто начинают с них. Затем агент выполняет длинную цепочку безобидных чтений, проверок состояния и повторных вызовов. Человек видит стену почти одинаковых окон и начинает одобрять их автоматически. Такая реакция понятна, но она разрушает задуманный контроль.
Для обычной работы лучше подходит разрешение на сессию, привязанное к сроку жизни одного процесса агента. Человек видит личность процесса и одобряет конкретный запуск. Агент выполняет ожидаемую последовательность до завершения. Новый процесс получает новое решение. Это ограничивает последствия перезапущенного или подмененного процесса, использующего тот же каталог проекта.
Оставьте подтверждение каждого вызова для учетных данных, использование которых может привести к необратимым или дорогостоящим последствиям. К этой категории относятся учетные данные production-развертывания, токены удаления учетных записей и SSH-идентичность, открывающая доступ к важным системам. Для запросов статуса только на чтение это обычно не нужно.
Область одобрения должна простыми словами отвечать на четыре вопроса:
- Какие полномочия подписи кода или исполняемый файл инициировали запрос?
- Какую ссылку на учетные данные он использует?
- Какой адрес или хост получит действие?
- Истекает ли решение после завершения процесса или для этого вызова нужно отдельное подтверждение?
Не вводите язык политик, если у вас нет специалистов, которые будут его поддерживать, и тестов, подтверждающих результат. Движки политик привлекают инженеров обещанием точного ответа для любой ситуации. На практике набор правил превращается в недокументированную программу авторизации, а при блокировке работы команда добавляет широкое исключение. Небольшой и понятный набор ограничений легче проверять и сложнее случайно ослабить.
Sallyport использует фиксированную последовательность решений: заблокированное хранилище отклоняет все действия, новый процесс агента по умолчанию требует разрешения на сессию, а для выбранных учетных данных можно включить подтверждение каждого использования. Модель намеренно остается небольшой: граница вокруг учетных данных агента должна делать состояние разрешений очевидным, а не заставлять администраторов отлаживать синтаксис авторизации.
Журнал аудита должен дать ответ после завершения запуска
Нужны два представления активности агента: одно для запуска, другое для каждого вызова с учетными данными. Запись сессии показывает, кто запустил агента и когда его полномочия закончились. Запись действия показывает адресат, метод или команду, ссылку на учетные данные, решение и результат.
Команды часто записывают только терминальный протокол. Этого недостаточно. Протокол показывает, что вывел агент, но не обязательно то, что отправил исполнитель. В нем могут отсутствовать фоновые вызовы, быть измененный вывод или оказаться секреты, если агент имел к ним доступ. С другой стороны, сырые сетевые или запросные журналы могут содержать слишком много чувствительных данных.
Записывайте точку принятия решения, а не каждый байт. Для HTTP сохраняйте нормализованные метод, хост, путь, ссылку на учетные данные, статус ответа, время, идентификатор сессии и результат одобрения. Для связи событий можно записать дайджест тела запроса или краткое описание, не сохраняя данные клиентов. Для SSH сохраняйте проверенный хост, удаленного пользователя, команду, код завершения и те же поля сессии и одобрения.
Запись должна позволять ответить на вопрос: «Какой авторизованный процесс использовал учетные данные развертывания, для какой операции и одобрил ли это использование человек?» Если нет, журнал может помочь с отладкой, но мало поможет после инцидента.
Защита от изменений повышает ценность таких записей. Обычный файл журнала на той же машине может изменить процесс с достаточными локальными правами. Хеш-цепочка связывает каждое событие с предыдущим, поэтому удаление или переписывание ранней записи нарушает проверку последующих. Шифрование защищает содержимое, а цепочка показывает, что последовательность менялась.
Sallyport создает журналы сессий и действий в зашифрованном аудите с хеш-цепочкой, а sp audit verify может офлайн проверить цепочку поверх шифротекста без ключа хранилища. Это важно при расследовании: проверяющий может убедиться в целостности журнала, не получая доступа к учетным данным и содержимому запросов в хранилище.
Цепочка не делает скомпрометированную конечную точку надежной сама по себе. Злоумышленник, контролирующий работающее приложение, все еще может выполнить действия до вашей реакции, а получивший контроль до записи события может повлиять на то, что будет записано. Цепочка дает убедительные свидетельства о сохраненной истории. Для полной картины добавьте быстрое отзыв разрешений, защищенное локальное хранение и журналы на стороне удаленного сервиса.
Проблема начинается с удобного обходного пути
Самые опасные архитектуры обычно начинаются с разумного исключения. Кто-то считает шлюз слишком ограничивающим для одной интеграции, поэтому дает агенту универсальную команду оболочки с токеном в окружении. Или инструменту развертывания нужен SSH, и команда включает forwarding вместо определения конкретного SSH-действия. Или разработчик добавляет отладочную конечную точку, возвращающую заголовки, потому что так проще тестировать.
Каждое исключение решает локальную задачу, но снова открывает получение секретов под другим названием.
Рассмотрим знакомую ситуацию. У агента для программирования есть сервисный токен в окружении, чтобы он мог запустить команду выпуска. Агент читает комментарий к pull request с просьбой расследовать неудачный выпуск. Вспомогательный скрипт в репозитории вызывает диагностическую команду. Команда экспортирует переменные окружения в архив поддержки. Агент загружает архив в стороннюю систему учета задач, потому что комментарий просил создать задачу.
Злоумышленнику не нужно было заранее знать значение токена. Достаточно было повлиять на текст, который агент принял за инструкцию, и направить его к инструменту, раскрывшему наследуемое состояние. Отзыв сессии после обнаружения не удалит архив. Токен придется сменить, а команде придется выяснить, где успел побывать архив.
Исполнитель с контролем меняет эту последовательность. Вспомогательный скрипт по-прежнему может работать, а агент, получать статус выпуска через разрешенный API-вызов. Но прочитать токен из окружения он не может, потому что токен туда не помещали. Если скрипт пытается выполнить новый вызов с учетными данными, исполнитель записывает его и применяет решение для сессии или отдельного вызова. В архиве поддержки остается меньше того, что можно украсть.
Не давайте обходной путь только потому, что агент заявляет о необходимости инструмента. Попросите описать минимальную операцию. Если инструменту нужен один API-адрес, откройте именно эту операцию. Если нужна одна команда развертывания, откройте ее. Если потребность действительно широкая, считайте это широкими полномочиями и требуйте отдельного осознанного действия человека.
Стройте границу вокруг важных действий
Начните с учетных данных, утрата которых потребует срочной замены или позволит внести значимое изменение в production. Необязательно переносить все токены разработки в первый день. Частичная граница вокруг самых опасных учетных данных лучше полной миграции, которую никто не завершит.
Сначала разберите, как агенты получают полномочия сейчас. Проверьте скрипты запуска агентов, настройки оболочки, файлы передачи данных CI, конфигурацию инструментов, локальные файлы .env и обертки команд. Отметьте каждое место, которое передает агенту токен, пароль, закрытый ключ, облачную сессию или перенаправленный сокет аутентификации. Часто выясняется, что агент получает больше полномочий через унаследованные настройки разработчика, чем кто-либо предполагал.
Затем замените передачу значений в открытом виде запросами действий. Называйте ссылки на учетные данные по назначению, а не по человеку или расплывчатой метке окружения. staging-deployer понятнее, чем shared-key-2. Создавайте отдельные ссылки, если для двух способов использования нужны разные требования к одобрению или разные адресаты.
Перед автоматизацией каждой ссылки решите следующее:
- Какие HTTP-хосты, методы и пути либо SSH-хосты, пользователи и команды соответствуют ее назначению?
- Нужно ли новому процессу агента явно подтвердить использование?
- Требует ли каждое использование подтверждения, потому что действие разрушительно или его трудно отменить?
- Какие поля результата нужны агенту для продолжения работы?
- Какие записи позволят позже объяснить действие, не сохраняя секретный материал?
Проверяйте отказы так же тщательно, как успешный сценарий. Запустите процесс агента без разрешения и убедитесь, что он не может действовать. Проверьте похожее имя хоста, перенаправление на другой хост, команду вне ожидаемого пути развертывания и попытку вывести ссылку на учетные данные так, будто это само значение. Убедитесь, что отозванная сессия больше не может выполнять запросы.
Проверьте и работу людей. Если запросов на одобрение так много, что вы перестаете их читать, области действия выбраны неправильно. Если окно не показывает, какой процесс сделал запрос, его идентификация слишком слабая. Если агент не может завершить обычную работу без постоянных просьб о универсальном обходе, интерфейсу действий нужна еще одна тщательно ограниченная операция, а не выгрузка секрета.
Критерий успеха прост: агент может выполнить одобренную работу, но протокол, дочерний процесс, плагин или отравленная инструкция не могут превратить эту работу во владение повторно используемыми учетными данными. Стройте такую границу до того, как очередное срочное развертывание заставит считать удобное исключение безобидным.
Вопросы и ответы
Почему нельзя просто дать AI-агенту доступ на чтение к менеджеру секретов?
Менеджер секретов защищает учетные данные при хранении и определяет, кто может их получить. Но этого недостаточно, если агент получает значение в открытом виде: процесс агента может передать его другому инструменту, вывести на экран, записать в файл или отправить не тому хосту. Для автономной работы лучше использовать границу, которая выполняет разрешенный запрос, но не передает агенту учетные данные.
Что такое инъекция учетных данных для AI-агентов?
Инъекция учетных данных означает, что доверенный локальный компонент добавляет нужные данные аутентификации в запрос после проверки действия. Агент передает метод, адрес и параметры запроса, но не получает Bearer-токен, пароль, закрытый ключ или пользовательский заголовок аутентификации. Так уменьшается число мест, где скопированный секрет может утечь.
Безопасно ли позволять агенту выполнять API-вызовы с внедренными учетными данными?
Это может быть безопасно, если компонент, который подтверждает запрос, проверяет точный адрес назначения, способ аутентификации и структуру запроса до подключения. Шлюз, который лишь позволяет агенту выбрать учетные данные, оставляет риск подмены хоста и слишком широких вызовов. В записи о разрешении должны быть указаны адрес запроса и полномочия использовавшего его процесса.
Защищает ли SSH agent forwarding закрытые ключи от AI-агентов?
Перенаправление SSH-агента решает другую задачу: удаленный хост может просить локальный агент подписывать запросы аутентификации. Если удаленный хост взломан или ему нельзя доверять, он может использовать перенаправленный агент, пока соединение открыто. Не считайте forwarding универсальным механизмом разрешений для автономных агентов.
API-ключи и SSH-ключи создают разные проблемы безопасности?
API-токен часто является Bearer-учетными данными, поэтому для его использования достаточно владеть им. Закрытый SSH-ключ обычно подтверждает владение с помощью подписей, но процесс, у которого есть доступ к ключу, все равно может подключаться от вашего имени к разрешенным или неразрешенным адресатам. Форматы различаются, но необходимость контролировать каждое действие остается.
Нужно ли требовать подтверждение человека для каждого вызова инструмента AI-агента?
Подтвердите личность процесса агента и область его сессии, а дополнительное подтверждение оставьте для учетных данных, способных причинить серьезный ущерб. Постоянные запросы для безобидных действий приучают людей нажимать кнопку, не читая текст. Постоянное разрешение для расплывчатой личности агента дает слишком широкие полномочия.
Что записывать, когда AI-агент использует учетные данные?
Полезная запись аудита содержит данные о запуске агента, идентификаторе процесса в операционной системе, адресате, типе действия, ссылке на учетные данные, результате, времени и решении об одобрении. Не храните значения секретов и по умолчанию не записывайте чувствительные тела запросов. Защита от изменений важна: обычный локальный текстовый журнал не поможет разобраться в споре после инцидента.
Может ли агент использовать учетные данные, ни разу их не увидев?
Нет. Шлюзу нужно знать достаточно, чтобы правильно установить соединение, но это не значит, что секрет должен попасть к агенту. Шлюз может хранить токен или закрытый ключ в защищенном хранилище, добавить его в исходящий HTTP-запрос или использовать для подписи SSH-аутентификации, а агенту вернуть только результат команды.
Как ограничить действия AI-агента по SSH?
Команда, принимающая произвольные аргументы, все равно может причинить ущерб после успешной аутентификации. Ограничьте доступные хосты и учетные записи, отделите учетные данные для развертывания от административных и используйте серверные средства, например принудительные команды или ограниченные учетные записи. Граница вокруг учетных данных должна дополнять эти меры, а не заменять их.
Какой первый практический шаг поможет остановить чтение секретов агентами?
Сначала перенесите в шлюз действий учетные данные, которыми агенты пользуются чаще всего, особенно токены развертывания, токены систем учета задач и SSH-ключи для систем рядом с рабочей средой. Уберите секреты из окружения агента, требуйте подтверждения для нового процесса и в течение первой недели проверяйте записи действий на необычные адреса и команды. Если найдете проблему, сузьте область действия учетных данных до автоматизации новых задач.