SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-28

Як не передавати секрети AI-агентам

API key у змінній середовища доступний усьому процесу: prompt injection може витягнути його одним викликом. Використовуйте короткоживучі токени через gateway.

Що означає не передавати секрети AI-агентам

AI-агент — це звичайний процес Linux, який виконує команди. Код, який запускає цей процес, може прочитати кожну змінну середовища, доступну процесу. Тому API key у середовищі агента може бути надісланий агентом на будь-який доступний йому хост. Не передавати секрети агенту означає надати йому дескриптор замість ключа: короткоживучий токен з обмеженою областю дії або заповнювач, який інший компонент замінює реальним значенням на межі мережі.

Це не історія про те, що модель стала ворожою. Механізм простіший. Агент читає вебсторінку, README або коментар до issue з інструкціями й виконує їх, оскільки для language model немає різниці між текстом, який написали ви, і текстом, який вона отримала ззовні. Це prompt injection. Після цього масштаб збитків обмежує рівно одна річ: те, що може прочитати процес. Якщо ви ще не встановили межу, у матеріалі як безпечно запускати coding agent на сервері описано рівні ізоляції, на яких базується цей посібник.

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

Виконайте це від імені користувача, під яким працює ваш агент.

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 до будь-якого хоста зі значенням у query string.
  • git commit і git push до репозиторію, у який агент може записувати.
  • Скрипт встановлення пакета, який виконує довільний код від імені користувача агента.
  • DNS-запит до hostname, що містить значення. Він залишає систему, навіть якщо HTTP egress заблоковано.

Перевірками це не виправити. Потрібно зробити так, щоб у зоні доступу не було нічого цінного.

Секрет у робочому дереві є секретом у контекстному вікні

Агент читає файли. Файл .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, оскільки це shell-команда, а не читання файлу. Чи буде перед виконанням цієї команди запит на підтвердження, залежить від режиму дозволів сеансу. У серпні 2026 auto mode стане режимом за замовчуванням у Claude Code, тому сервер, за яким ви не стежите, виконуватиме більше таких команд без запиту.

Це саме обмеження діє для всього, що формує звички агента, а не його дозволи. Навичка, яка вимагає від агента виконувати найменшу працездатну зміну, не дає виконанню перейти до файлів, які агент не мав відкривати. Але це все одно лише рекомендація, яку можна змусити модель проігнорувати. Сприймайте конфігурацію як захисне обмеження, а права доступу до файлової системи — як фізичну межу. Такий самий поділ діє всередині контейнерів: env-файли та секрети в Docker Compose описують цю проблему на рівень нижче.

Надайте кожному агенту окремого непривілейованого користувача

Якщо агент працює від вашого імені, він успадковує ваші SSH-ключі, облікові дані хмарних сервісів і історію shell. Окремий користувач потребує виконання однієї команди та усуває весь цей доступ.

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 варто додати ще один бар’єр. Сервіс метаданих інстансу відповідає за фіксованою link-local адресою і часто надає облікові дані ролі будь-якому клієнту, який їх запитує.

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 — це одна з open source реалізацій такого підходу. Вона ліцензована за Apache-2.0 і запускається як контейнер поруч з агентом. Станом на July 2026 проєкт описує таку конфігурацію:

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

Dashboard прослуховує порт 10254, а gateway — порт 10255. Ви один раз зберігаєте справжні облікові дані, а потім надаєте кожному агенту значення-заповнювач замість ключа та власний токен доступу з обмеженою областю дії. Агент передає цей токен у заголовку Proxy-Authorization. Gateway визначає вихідний запит за хостом і шляхом, розшифровує відповідні облікові дані та підставляє їх. У середовищі агента не зберігається нічого, що має цінність для зловмисника.

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

Передавайте секрет процесу, а не середовищу

Якщо ви запускаєте агент через systemd, змінні середовища взагалі не потрібні. LoadCredential= розміщує секрет у приватному каталозі, який може читати лише цей сервіс. У unit-файлі цей каталог доступний як %d, а всередині процесу — як $CREDENTIALS_DIRECTORY. Значення ніколи не з’являється в /proc/<pid>/environ, тому ps eww не може його показати. Каталог видаляється після зупинки сервісу.

Спочатку зашифруйте облікові дані для цієї машини. Ці команди наведено в документації systemd. Вони працюють у 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. Це підтверджує, що зашифрований файл можна розшифрувати на цьому хості. Потім додайте посилання на нього в unit:

[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 токеном, обмеженим одним репозиторієм, з яким він працює, щоб 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. Третя показує, яку ідентичність представляє мережевий шлях агента. Саме на це запитання має відповідати схема з gateway: значення 401 означає, що агент не передає власні облікові дані GitHub, а 200 — що передає. У такому разі потрібно знати, який саме токен він використовує. Якщо ви запускаєте агентів без нагляду, у матеріалі контроль витрат AI-агентів на VPS описано ліміти бюджету, які доповнюють ці обмеження доступу.

FAQ

Чи можу я просто довірити моделі не розкривати мої ключі?

Ні, оскільки в цій моделі загроз модель не є атакувальником. Агент читає текст із вебсторінок, репозиторіїв і систем відстеження завдань, а цей текст може містити інструкції. Модель не має надійного способу відрізнити ваші інструкції від тексту, який вона отримала. Будь-який захист, що залежить від правильного рішення моделі, не спрацює, щойно нав’язана інструкція виявиться переконливою. Тому контроль потрібно реалізувати в операційній системі або мережі.

Чи справді змінні середовища настільки небезпечні для секретів агента?

Вони небезпечні з однієї конкретної причини: вони успадковуються. Кожен дочірній процес, який запускає агент, отримує їхню копію, зокрема build script, test runner і будь-який install hook пакета. Змінні також можна прочитати через /proc/<pid>/environ від імені того самого користувача. Тому все, що запускає агент, може прочитати їх без передавання агентом. Файл, який читається безпосередньо в момент використання, за допомогою LoadCredential= або gateway, обмежує доступ цим моментом.

Чи вирішує розміщення секретів у vault проблему само по собі?

Лише частково. Vault вирішує проблему зберігання. Якщо ви розміщуєте цей vault самостійно, його також потрібно окремо захистити, оскільки сервер Vaultwarden зазвичай зламують через його admin token або backup file, а не через зашифровані елементи, які він зберігає. Це не вирішує проблему останнього кроку, коли щось отримує секрет із vault і передає його агенту як змінну середовища. У результаті ви повертаєтеся до початкової ситуації. Важливо, хто виконує підстановку. Якщо секрет отримує агент, секрет опиняється в агента. Якщо підстановку виконує gateway або init system поза процесом агента, агент ніколи не отримує секрет.

Як дізнатися, чи агент уже витік щось назовні?

Зазвичай після події це неможливо визначити напевно, і саме це є аргументом на користь gateway. Без нього докази розподілені між shell history, transcript агента та журналами вихідних з’єднань, які ви, найімовірніше, не зберігаєте. З credential gateway кожне використання облікових даних фіксується одним рядком із ідентифікатором агента та міткою часу. Якщо ви підозрюєте витік, спочатку замініть ключ, а потім проводьте розслідування. Ротація коштує недорого, а впевненість — ні.

Що мінімально потрібно зробити сьогодні?

Перемістіть усі .env файли за межі каталогів, у яких працюють ваші агенти, і створіть окремого непривілейованого користувача для кожного агента. Ці дві зміни займають приблизно десять хвилин і усувають найпоширеніший шлях атаки: агент читає файл з обліковими даними, якому не потрібно було перебувати поруч із кодом. Gateway і короткоживучі токени — це наступний крок, а не перший. Цей самий початковий підхід застосовується до будь-якого середовища виконання агента, зокрема безпечного запуску автономного агента на VPS.