SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-26

Как защитить API-ключи от утечки через AI-агентов

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

Что означает исключение секретов из AI-агентов

AI-агент — это обычный процесс в Linux, который выполняет команды. Любая переменная окружения, доступная этому процессу, может быть прочитана исполняемым кодом. Следовательно, API-ключ в окружении агента — это ключ, который агент может отправить на любой доступный ему хост. Исключение секретов из агента означает предоставление ему дескриптора вместо самого ключа: короткоживущего токена с ограниченными правами или заполнителя, который заменяется на реальное значение чем-то другим на сетевом уровне.

Речь не идет о том, что модель становится враждебной. Механизм гораздо прозаичнее. Агент читает веб-страницу, README или комментарий к задаче, содержащие инструкции, и выполняет их, поскольку для языковой модели нет разницы между текстом, который написали вы, и текстом, который она получила извне. Это и есть prompt injection. Как только это происходит, масштаб ущерба ограничивается ровно одним фактором: тем, что может прочитать процесс. Если вы еще не установили границы, безопасный запуск агента для написания кода на сервере описывает уровни изоляции, на которых базируется данное руководство.

Модель угроз простыми словами

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

tr '\0' '\n' < /proc/self/environ | grep -iE 'key|token|secret|password'

Каждая выводимая строка — это один HTTP-запрос к серверу злоумышленника. Теперь посмотрите, что находится на диске рядом с агентом.

grep -rIl --exclude-dir=.git -e 'API_KEY' -e 'SECRET' ~/projects
find ~ -maxdepth 3 -name '.env' -o -name 'credentials' -o -name '*.pem'

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

  • Исходящий запрос curl или fetch к любому хосту, где значение передается в строке запроса.
  • Команды git commit и git push в репозиторий, в который агент имеет право записи.
  • Скрипт установки пакета, который выполняет произвольный код от имени пользователя агента.
  • DNS-запрос к имени хоста, содержащему данные; этот метод работает, даже если исходящий HTTP-трафик заблокирован.

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

Секрет в рабочем дереве — это секрет в контекстном окне

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

До изменений, когда ключ находился в рабочем дереве агента:

cd ~/projects/billing
cat .env
# STRIPE_SECRET_KEY=sk_live_...
# DATABASE_URL=postgres://app:hunter2@db.internal:5432/billing

После изменений, когда файл перемещен в недоступное место:

sudo install -d -m 750 -o root -g agent-review /etc/agent-review
sudo install -m 640 -o root -g agent-review ~/projects/billing/.env /etc/agent-review/billing.env
rm ~/projects/billing/.env

Пользователь агента больше не может открыть файл, так как его нет в рабочем дереве. Правила запрета в конфигурации самого агента — это второй уровень защиты, а не первый. Claude Code считывает правила доступа из .claude/settings.json в проекте:

{
  "permissions": {
    "deny": ["Read(./.env)", "Read(./secrets/**)", "Read(./**/*.pem)"]
  }
}

Это предотвращает случайное открытие файла агентом при изучении репозитория. Однако это не мешает внедренной инструкции выполнить base64 .env, так как это команда оболочки, а не чтение файла. Будет ли у вас запрошено подтверждение перед выполнением такой команды, зависит от режима разрешений сессии, а автоматический режим станет стандартным для Claude Code в августе 2026 года, поэтому сервер, за которым вы не следите, будет выполнять больше таких команд без запроса. То же ограничение касается всего, что формирует привычки агента, а не его права доступа: навык, заставляющий агента вносить минимально необходимые изменения, не дает процессу обращаться к файлам, которые ему не нужны, но это лишь рекомендация, которую модель может проигнорировать под воздействием инструкций. Рассматривайте конфигурацию как ограждение, а права доступа к файловой системе — как стену. Такое же разделение применимо внутри контейнеров: файлы окружения и секреты в Docker Compose описывают этот же тип проблемы на уровень ниже.

Назначьте каждому агенту отдельного непривилегированного пользователя

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

sudo adduser --disabled-password --gecos "" agent-review
sudo chmod 700 /home/agent-review
sudo -u agent-review cat ~/.ssh/id_ed25519

Последняя строка должна завершиться ошибкой cat: /home/you/.ssh/id_ed25519: Permission denied. Если вместо этого выводится ключ, значит, ваш домашний каталог доступен для чтения группе или всем пользователям; исправить это поможет chmod 700 ~. Не добавляйте пользователя агента в группу sudo и не предоставляйте ему правило NOPASSWD с правами шире, чем требуется для выполнения одной конкретной команды. В разделе Пользователи с минимальными привилегиями на VPS подробно описана работа с группами и файлом sudoers. Помните об этом разделении, если запускаете более одного сеанса на сервере, поскольку один сеанс Claude Code может передать текст напрямую другому, и любые данные, доступные первому сеансу, могут быть переданы через этот канал в одном сообщении.

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

sudo iptables -A OUTPUT -m owner --uid-owner agent-review -d 169.254.169.254 -j REJECT

Проверьте это со стороны агента. Команда sudo -u agent-review curl -s --max-time 3 http://169.254.169.254/ не должна ничего выводить и должна завершиться с ненулевым кодом, так как пакет будет отброшен до того, как покинет сервер.

Внедрение учетных данных на границе

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

OneCLI — одна из реализаций этого подхода с открытым исходным кодом, распространяемая по лицензии Apache-2.0, которая запускается как контейнер рядом с агентом. По состоянию на июль 2026 года проект документирует следующую настройку:

git clone https://github.com/onecli/onecli.git
cd onecli
docker compose -f docker/docker-compose.yml up -d --wait

Панель управления ожидает запросы на порту 10254, а шлюз — на 10255. Вы сохраняете реальные учетные данные один раз, а затем предоставляете каждому агенту значение-заполнитель вместо ключа вместе с его собственным токеном доступа с ограниченными правами, который он отправляет в заголовке Proxy-Authorization. Шлюз сопоставляет исходящий запрос по хосту и пути, расшифровывает соответствующие учетные данные и подставляет их. В окружении агента нет ничего, что стоило бы красть.

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

Передавайте секрет процессу, а не через переменные окружения

Если вы запускаете агент под управлением systemd, переменные окружения не требуются вовсе. LoadCredential= размещает секрет в закрытом каталоге, доступном только этому сервису; он предоставляется как %d в файле юнита и как $CREDENTIALS_DIRECTORY внутри процесса. Значение никогда не попадает в /proc/<pid>/environ, поэтому ps eww не сможет его отобразить, а сам каталог удаляется при остановке сервиса.

Сначала зашифруйте учетные данные для конкретной машины. Эти команды взяты из документации systemd и работают в версии 250 и новее, что актуально для Ubuntu 24.04 и Debian 13:

echo -n 'sk-example-value' > /tmp/plain.txt
sudo systemd-creds encrypt --name=api_key /tmp/plain.txt /etc/credstore/api_key.cred
shred -u /tmp/plain.txt
sudo systemd-run -P --wait -p LoadCredentialEncrypted=api_key:/etc/credstore/api_key.cred \
  systemd-creds cat api_key

Последняя команда выводит sk-example-value. Это подтверждает, что зашифрованный файл успешно расшифровывается на данном хосте. Затем укажите путь к нему в юните:

[Service]
User=agent-review
LoadCredentialEncrypted=api_key:/etc/credstore/api_key.cred
Environment=AGENT_KEY_FILE=%d/api_key
ExecStart=/usr/local/bin/agent-worker

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

Отдавайте предпочтение краткосрочным токенам вместо долгосрочных ключей

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

aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/agent-readonly \
  --role-session-name agent-review \
  --duration-seconds 900

Пятнадцать минут — это минимальный срок, который принимает AWS STS (security token service), и этого обычно достаточно для одной задачи агента. Для GitHub создайте для агента отдельный gh логин с токеном с ограниченными правами (fine grained token), который действует только в рамках одного репозитория. В этом случае gh auth token внутри сессии вернет данные, которые не позволят получить доступ к чему-либо еще. Сначала ограничивайте область действия ресурсами, а затем — временем.

Проверка и постоянный мониторинг

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

sudo -u agent-review env | grep -iE 'key|token|secret'
sudo -u agent-review ls -la /home/you/ 2>&1 | head -3
sudo -u agent-review curl -s -o /dev/null -w '%{http_code}\n' --max-time 5 https://api.github.com/user

Первая команда не должна выводить ничего. Вторая должна вывести ls: cannot open directory '/home/you/': Permission denied. Третья команда показывает, какую идентификацию агент предъявляет в сети; именно на этот вопрос отвечает паттерн шлюза: значение 401 означает, что у агента нет собственных учетных данных GitHub, а 200 означает, что они присутствуют, поэтому вы должны знать, какой именно токен используется. Если вы запускаете агентов в автоматическом режиме, в разделе контроль расходов на AI-агентов на VPS описаны бюджетные лимиты, которые применяются совместно с этими ограничениями доступа.

FAQ

Могу ли я просто довериться модели, чтобы она не раскрыла мои ключи?

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

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

Они плохи по одной конкретной причине: они наследуются. Каждый дочерний процесс, порождаемый агентом, получает их копию, включая скрипты сборки, средства запуска тестов и любые хуки установки пакетов. Переменные также доступны для чтения через /proc/<pid>/environ тем же пользователем, поэтому любой процесс, запущенный агентом, может прочитать их без участия самого агента. Чтение файла непосредственно в момент использования с помощью LoadCredential= или шлюза ограничивает время воздействия этого секрета.

Решает ли эту проблему само по себе хранение секретов в хранилище (vault)?

Только частично. Хранилище решает вопрос хранения. Если вы используете self-hosted решение, оно требует отдельной настройки безопасности, так как сервер Vaultwarden обычно взламывают через токен администратора или файл резервной копии, а не через зашифрованные элементы, которые он содержит. Это не решает последний этап, на котором некий процесс извлекает секрет из хранилища и передает его агенту в виде переменной окружения, что возвращает вас к исходной точке. Важно то, кто именно выполняет подстановку. Если секрет получает агент, то секрет оказывается у агента. Если подстановку выполняет шлюз или система инициализации вне процесса агента, агент никогда не получает к нему доступ.

Как узнать, не допустил ли агент утечку чего-либо?

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

Что мне нужно сделать в первую очередь уже сегодня?

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