# Псевдонимы учетных данных для AI-агентов: безопасные локальные действия

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

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

**Псевдонимы учетных данных для AI-агентов** работают, когда доверенный локальный исполнитель разрешает короткое понятное имя подключения и сам выполняет аутентифицированный запрос. Агент запрашивает `repo-release-prod`, исполнитель добавляет учетные данные в момент использования, а агент получает код состояния, тело ответа с удаленными секретами или вывод команды. Секрет никогда не становится входом или выходом цикла агента.

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

## Псевдоним обозначает полномочия, а не скрытую строку

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

Возьмем `payments-prod`. Для автономного агента это слишком расплывчатое имя. Разрешает ли оно читать счета, оформлять возвраты, изменять данные учетных записей или скачивать отчеты? Один bearer-токен технически может позволять все это, но псевдоним не должен стирать различия. Лучше назвать подключения по назначению: `payments-invoice-read-prod` и `payments-refund-review-prod` показывают оператору, что именно он собирается разрешить, а через полгода делают запись аудита понятной.

Люди часто смешивают три разных понятия:

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

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

Я предпочитаю псевдонимы, в которых указаны целевой сервис, окружение и задача, но не тип учетных данных. `artifact-publish-prod` лучше, чем `artifact-api-token-2`. Первое имя переживет переход со статического токена на краткоживущий обмен. Второе навсегда встраивает техническую деталь в каждый запрос агента, вызов инструмента, тест и инструкцию для дежурных.

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

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

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

Рассмотрим знакомую неудачную схему. Агент для программирования вызывает локальный помощник с параметром `connection=deploy-prod`. Помощник находит API-токен, а затем запускает консольный клиент, передавая токен в переменной окружения. Команда завершается ошибкой. Диагностическая оболочка записывает окружение в файл журнала. У агента есть доступ к каталогу журналов рабочей области, и он читает токен, чтобы объяснить сбой.

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

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

Путь ответа требует такого же внимания. Некоторые API возвращают учетные данные при создании токена, webhook может повторить данные авторизации, а отчеты об ошибках иногда содержат заголовки запроса. Исполнителю нужна политика ответа, которая удаляет известные заголовки с секретами и не отражает детали формирования собственного запроса. Это не значит, что нужно молча переписывать бизнес-данные. Нужно заранее решить, какие части запроса и ответа агент вправе видеть.

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

## Объем подключения должен соответствовать поверхности действий

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

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

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

Для SSH нужна такая же дисциплина, хотя средства контроля выглядят иначе. Ключ, открывающий интерактивную оболочку, дает агенту слишком широкую поверхность действий. Предпочтительнее учетная запись, которая выполняет нужную задачу в ограниченном каталоге, или принудительная команда, принимающая именно предусмотренную операцию. Не думайте, что псевдоним хоста в `~/.ssh/config` решает проблему. Он выбирает назначение для клиента, но не ограничивает происходящее после аутентификации.

В руководстве OpenSSH для `authorized_keys` описаны такие параметры, как `command=`, `no-pty`, `no-port-forwarding` и `restrict`. Это практические инструменты для машинных подключений. Но у них есть острые края: принудительная команда должна проверять аргументы, а запрет перенаправления портов не делает безопасной учетную запись с широкой оболочкой. Сначала изучите реальные полномочия учетной записи. Затем используйте параметры SSH, чтобы закрыть ненужные пути доступа.

Не используйте один псевдоним для staging и production только потому, что конечная точка отличается параметром URL. Под давлением агент ошибается так же, как человек. Разные имена позволяют задать разные учетные данные, правила подтверждения и ожидания от аудита. Они также не дают задаче для staging получить полномочия production из-за ошибки подстановки строки.

## Исполнитель должен аутентифицировать вызывающего до обработки псевдонима

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

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

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

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

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

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

## При ротации меняйте материал, но сохраняйте имя

Практическая польза псевдонимов особенно заметна при ротации. Вы заменяете токен или SSH-ключ за `artifact-publish-prod`, агент продолжает запрашивать то же имя подключения, и ни в запросе, ни в исходном файле, ни в конфигурации агента не нужен новый секрет.

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

Процесс ротации должен подтвердить обе стороны изменения:

1. Добавьте новые учетные данные за существующим псевдонимом и выполните через исполнителя один узко ограниченный проверочный вызов.
2. Отзовите или отключите старые данные у поставщика сервиса, затем повторите ожидаемое действие агента.
3. Проверьте запись аудита на наличие неудачных повторов или неожиданных вызывающих, потому что старые процессы часто обнаруживают себя после ротации.
4. Удалите скопированные файлы учетных данных, старые настройки окружения и аварийные скрипты, обходившие исполнителя.

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

OWASP Secrets Management Cheat Sheet советует ротировать секреты и соблюдать принцип минимальных полномочий. Обе рекомендации верны, но обычно их преподносят как правила хранения. Для агентов ротация также защищает слой инструкций. Стабильный псевдоним означает, что меняющийся секрет не попадает в контекст модели, а обновление секрета не становится отдельной задачей, которая может оказаться в расшифровках, сообщениях коммитов или выводе инструментов.

## Записи аудита должны описывать решения, но не хранить секреты

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

Практическая запись события содержит время, идентификатор сессии, идентичность вызывающего, псевдоним, канал, назначение, метод или класс команды, решение авторизации и класс результата. Для HTTP обычно достаточно записать `POST /releases`, чтобы объяснить действие. Полный заголовок `Authorization` никогда не полезен и создает серьезную проблему при очистке. Для SSH записывайте псевдоним хоста, удаленную учетную запись или класс команды и код завершения, но не отпечаток приватного ключа, если он раскрывает чувствительные сведения об инфраструктуре.

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

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

У удобной команды проверки должен быть намеренно простой контракт:

```
$ sp audit verify
verified 184 records
chain: valid
first record: 2025-04-03T09:14:22Z
last record:  2025-04-03T16:48:01Z
```

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

Не путайте защиту от незаметного изменения с мониторингом. Нужно следить за подозрительной активностью, сохранять копии при начале инцидента и решать, кто имеет право отзывать сессии. Криптография может показать, что последовательность записей изменилась. Она не скажет, была ли разумной команда `delete-production-data`.

## Усталость от подтверждений указывает на плохой дизайн псевдонимов

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

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

Используйте подтверждение каждого вызова для псевдонимов, каждое применение которых требует внимания. Исполнитель должен сделать событие достаточно понятным, чтобы человек мог быстро его отклонить. `Использовать payments-refund-review-prod для отправки запроса на возврат по заказу 4821` это конкретное действие. `Для вызова инструмента требуется подтверждение` это небрежный дизайн.

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

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

## Псевдонимы не устраняют prompt injection и ошибки в постановке задач

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

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

Поэтому широкий псевдоним опасен, даже когда секрет никогда не раскрывается. `cloud-admin-prod` оставляет внедренной инструкции слишком много возможностей. `artifact-publish-prod` вместе с ограниченной сервисной учетной записью и ожидаемым назначением оставляет меньше. Все равно нужно ограничить файлы, которые агент может читать, отделить недоверенное содержимое от инструкций по действиям и требовать подтверждения для действий с последствиями, выходящими за рамки задачи.

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

Рассматривайте каждый псевдоним как ответ на предложение, которое можно записать: «Этот определенный процесс может выполнять такой класс действий для такого назначения с такими правилами подтверждения». Если вам приходится использовать слова вроде «что угодно» или «по необходимости», подключение нужно доработать, прежде чем выдавать его агенту.

## Локальный исполнитель должен оставаться простым и проверяемым

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

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

Sallyport хранит API- и SSH-ключи в зашифрованном локальном хранилище и выполняет HTTP- и SSH-действия от имени агентов с поддержкой MCP, вместо того чтобы передавать учетные данные в контекст агента. Его адаптер `sp mcp` дает агенту обычное подключение через stdio, а приложение остается исполнителем.

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

Держите протокол инструментов узким. Возвращайте структурированные результаты успеха и ошибки. Не включайте во сообщения об ошибках внутренние объекты запросов или пути к хранилищу секретов. Сделайте отмену явной, чтобы пользователь мог остановить длительное действие, не прерывая unrelated работу. Эти решения выглядят обычными. Именно они отделяют границу, которой можно управлять, от границы, на которую остается только надеяться.

## Проверяйте границу как атакующий, который может писать инструкции

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

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

Затем намеренно проверьте сбои. Заблокируйте хранилище во время запроса. Отзовите сессию перед вторым вызовом. Ротируйте лежащие в основе учетные данные. Измените одну сохраненную запись аудита в копии и запустите проверку. Каждый ожидаемый сбой должен быть понятен оператору и бесполезен атакующему. Сообщение «Доступ запрещен, потому что хранилище заблокировано» подходит. Трассировка стека с записью хранилища или значением заголовка нет.

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

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