Читать 6 мин

Уберите MCP-агентов с переменных среды: перенесите учётные данные

Перенесите MCP-агентов с переменных среды: составьте список инструментов, уберите секреты в хранилище, проверьте границы доступа и безопасно замените старые токены.

Уберите MCP-агентов с переменных среды: перенесите учётные данные

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

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

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

Переменные среды дают агенту больше полномочий, чем нужно инструменту

Переменная среды не принадлежит одному запросу. Она принадлежит процессу и часто всему, что этот процесс запускает. Если MCP-клиент запускает сервер с SERVICE_TOKEN в окружении, сервер может его прочитать. То же могут сделать дочерние процессы, которые унаследовали переменную, если кто-то специально её не удалит. Команды отладки, отчёты о сбоях, тестовые данные и случайный вывод env могут превратить локальное удобство в долговечную утечку.

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

Разницу легко не заметить, потому что оба варианта могут привести к одному успешному запросу. Но это не одно и то же:

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

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

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

Составьте список инструментов до изменения конфигурации

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

Для каждого инструмента MCP запишите его название, команду, цель, способ аутентификации, владельца учётных данных, область разрешений и безопасный запрос для проверки. Отдельно укажите, где секрет сейчас попадает в процесс: в конфигурацию клиента, файл запуска оболочки, .env-файл, экспорт переменной в CI, команду менеджера паролей или вспомогательный скрипт.

Краткий список может выглядеть так:

ИнструментЦель действияТекущий путь секретаНовая границаПроверка
поиск задачAPI задачISSUES_TOKEN в конфигурации клиенталокальное действие HTTPпоказать один известный проект
статус развёртыванияAPI развёртывания.env.localлокальное действие HTTPпрочитать статус сервиса
диагностика хостапсевдоним хоста SSHпуть к файлу закрытого ключалокальное действие SSHвыполнить uname
публикация пакетаAPI реестраэкспорт из оболочкилокальное действие HTTPпрочитать метаданные пакета

Не прячьте широкие разрешения за расплывчатыми именами вроде prod-token или default-key. Дайте записи учётных данных название, по которому оператор поймёт, что она может делать и куда направляется. deploy-api-production-read выглядит менее изящно, зато намного безопаснее при поспешной проверке.

Список также показывает, нужна ли учётная запись вообще. Я находил токены с правом записи у инструментов, которые только читали метаданные проекта, потому что кто-то скопировал настройки для разработки. Миграция подходит для выдачи более узких разрешений. Она не повод сохранить все старые полномочия в более аккуратном контейнере.

Удаляйте передачу секретов, а не маскируйте её

После миграции секрет не должен попадать к агенту или его MCP-серверу. Замена явного токена на ${SERVICE_TOKEN}, $(secret-tool lookup ...) или путь к незащищённому файлу этому требованию не соответствует. Вы изменили написание, но не полномочия.

Сначала найдите текущие ссылки. В каталоге проекта эта команда обнаружит много очевидных случаев:

rg -n --hidden --glob '! .git' 'API[_-]?KEY|API[_-]?TOKEN|SECRET|PASSWORD|PRIVATE[_-]?KEY|Authorization: Bearer' .

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

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

env | cut -d= -f1 | sort | rg 'TOKEN|KEY|SECRET|PASSWORD'

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

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

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

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

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

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

GET https://api.example.internal/projects/atlas/issues?state=open

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

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

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

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

Выбирайте точки подтверждения, которые человек может оценить

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

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

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

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

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

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

Проверяйте успех и сохранность секрета отдельно

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

Для каждого инструмента используйте такую последовательность:

  1. Заблокируйте локальное хранилище и вызовите инструмент. Вызов должен завершиться ошибкой и не использовать токен из окружения как запасной вариант.
  2. Разблокируйте хранилище, запустите новый процесс агента и подтвердите его, если это требуется в вашей настройке. Выполните безопасный запрос из списка.
  3. Проверьте журнал действий или серверную запись аудита: точную цель, аккаунт, метод и статус результата. Убедитесь, что записано само действие, но не секрет.
  4. Из разрешённого агенту пути выполнения проверьте его окружение на старое имя переменной, а рабочую область на префикс токена. Токен не должен появиться ни там, ни там.
  5. Перезапустите процесс агента и повторите запрос. Так вы убедитесь, что случайно не использовали состояние, унаследованное от старой оболочки.

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

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

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

Записи аудита должны позволять восстановить спорное действие

Останавливайте работу при блокировке
Пока хранилище заблокировано, Sallyport отклоняет все действия HTTP и SSH.

Журнал действий полезен, когда отвечает на неприятный вопрос понедельника: какой запуск агента выполнил этот запрос, через какие настроенные учётные данные и подтвердил ли его человек? Расплывчатая строка вроде tool succeeded ничего не прояснит.

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

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

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

sp audit verify

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

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

Замена токенов завершает очистку и доказывает, что миграция была настоящей

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

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

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

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

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

Считайте широкие полномочия недостатком дизайна инструмента

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

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

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

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

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

Почему переменные среды небезопасны для MCP-агентов?

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

Можно ли безопасно поделиться конфигурационным файлом MCP, в котором использовались переменные среды?

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

Когда менять API-токены: до или после миграции инструмента MCP?

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

Может ли файл конфигурации MCP содержать ссылку на секрет?

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

Как проверить, что агент не видит перенесённый токен?

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

Можно ли так же убрать SSH-ключи из окружения AI-агента?

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

Когда для учётных данных MCP нужно подтверждение каждого вызова?

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

Что происходит, когда локальное хранилище блокируется во время работы агента?

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

Как безопаснее всего откатить миграцию учётных данных MCP?

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

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

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

Sallyport

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

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