SSD Nodes Learn 8GB RAM — $66/rok
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-01

Jak chronić klucze API przed agentami AI

Agent może ujawnić klucz API w jednym wywołaniu narzędzia. Zastosowanie tokenów krótkotrwałych i ograniczonych za bramą poświadczeń ogranicza skutki prompt injection.

Co oznacza przechowywanie danych uwierzytelniających poza agentami AI

Agent AI jest zwykłym procesem Linux, który wykonuje polecenia. Każda zmienna środowiskowa dostępna dla tego procesu może być odczytana przez kod, który on uruchamia. Klucz API znajdujący się w środowisku agenta może więc zostać wysłany przez agenta do dowolnego hosta, do którego ma on dostęp. Przechowywanie danych uwierzytelniających poza agentem oznacza przekazanie mu uchwytu zamiast klucza: krótkotrwałego tokenu o ograniczonym zakresie albo symbolu zastępczego, który inny komponent zamienia na rzeczywistą wartość na granicy sieci.

Nie chodzi tu o sytuację, w której model staje się wrogi. Mechanizm jest prostszy. Agent odczytuje stronę internetową, plik README albo komentarz do zgłoszenia zawierający instrukcje i wykonuje je, ponieważ dla modelu językowego nie ma różnicy między tekstem napisanym przez użytkownika a tekstem pobranym przez model. To jest prompt injection. Gdy do tego dojdzie, zakres szkód ogranicza dokładnie jedna rzecz: dane, które proces może odczytać. Jeśli granica nie została jeszcze skonfigurowana, sekcja bezpieczne uruchamianie agenta programistycznego na serwerze opisuje poziomy izolacji, na których opiera się ten przewodnik.

Model zagrożeń w prostych słowach

Należy uruchomić to jako użytkownik, z którego korzysta agent.

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

Każda drukowana przez niego linia oznacza jedno żądanie HTTP do serwera należącego do nieznanej osoby. Należy teraz sprawdzić, jakie dane znajdują się na dysku w pobliżu agenta.

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

Agent mający dostęp do powłoki nie potrzebuje zaawansowanego exploita, aby wyprowadzić te dane. Wystarczą cztery typowe ścieżki. Wszystkie cztery wyglądają w logu jak normalna praca:

  • Wychodzące curl lub fetch do dowolnego hosta, z wartością w ciągu zapytania.
  • git commit i git push do repozytorium, w którym agent ma uprawnienia zapisu.
  • Skrypt instalacyjny pakietu, który uruchamia dowolny kod jako użytkownik agenta.
  • Wyszukanie DNS nazwy hosta zawierającej tę wartość. Takie wyszukanie jest wykonywane nawet po zablokowaniu wychodzącego ruchu HTTP.

Nie można rozwiązać tego problemu samym przeglądaniem kodu. Należy zagwarantować, że w zasięgu agenta nie ma żadnych wartościowych danych.

Tajny plik w drzewie roboczym jest tajny w oknie kontekstu

Agent odczytuje pliki. Plik .env znajdujący się w repozytorium, w którym agent pracuje, zostanie odczytany. Po odczytaniu znajduje się w oknie kontekstu, czyli w transkrypcji, w każdym przechowywanym dzienniku oraz we wszystkim, co agent zapisze później.

Wcześniej, gdy klucz znajdował się w drzewie, w którym pracuje agent:

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

Później, gdy plik został przeniesiony poza jego zasięg:

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

Użytkownik agenta nie może już otworzyć pliku, ponieważ drzewo robocze go nie zawiera. Reguły odmowy w konfiguracji samego agenta stanowią drugą warstwę, a nie pierwszą. Claude Code odczytuje reguły uprawnień z .claude/settings.json w projekcie:

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

Zapobiega to przypadkowemu otwarciu pliku przez agenta podczas przeglądania zawartości. Nie zapobiega uruchomieniu przez wstrzykniętą instrukcję base64 .env, ponieważ jest to polecenie powłoki, a nie odczyt pliku. Konfigurację należy traktować jako zabezpieczenie, a uprawnienia systemu plików jako fizyczną barierę. Ten sam podział obowiązuje wewnątrz kontenerów: pliki env i sekrety w Docker Compose omawiają tę wersję problemu o jedną warstwę niżej.

Każdemu agentowi przypisz własnego użytkownika bez uprawnień

Jeśli agent działa z Twoimi uprawnieniami, dziedziczy Twoje klucze SSH, poświadczenia chmurowe i historię powłoki. Oddzielny użytkownik wymaga wykonania jednego polecenia i usuwa wszystkie te uprawnienia.

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

Ostatni wiersz musi zakończyć się niepowodzeniem z komunikatem cat: /home/you/.ssh/id_ed25519: Permission denied. Jeśli zamiast tego zostanie wyświetlony klucz, katalog domowy jest czytelny dla grupy lub wszystkich użytkowników. Problem ten naprawia chmod 700 ~. Nie dodawaj użytkownika agenta do sudo i nie nadaj mu reguły NOPASSWD szerszej niż ta, której rzeczywiście wymaga jedno polecenie. Użytkownicy z zasadą najmniejszych uprawnień na VPS omawia szczegóły grup i sudoers.

Na serwerze cloud VPS warto dodać jeszcze jedno ograniczenie. Usługa metadanych instancji odpowiada pod stałym lokalnym adresem link-local i często udostępnia poświadczenia roli każdemu klientowi, który o nie poprosi.

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

Sprawdź to z poziomu agenta. sudo -u agent-review curl -s --max-time 3 http://169.254.169.254/ powinno niczego nie wyświetlić i zakończyć działanie kodem różnym od zera, ponieważ pakiet jest odrzucany, zanim opuści serwer.

Wstrzykiwanie danych uwierzytelniających na granicy

Wzorzec, który rzeczywiście rozwiązuje ten problem, polega na wstrzykiwaniu danych uwierzytelniających. Agent nigdy nie przechowuje prawdziwego klucza. Wysyła żądanie przez lokalną bramę, a brama po drodze zastępuje wartość zastępczą prawdziwym sekretem. Sekret znajduje się w magazynie bramy, w innym procesie, należącym do innego użytkownika.

OneCLI jest jedną z implementacji open source tego rozwiązania, udostępnianą na licencji Apache-2.0. Działa jako kontener obok agenta. Stan na lipiec 2026 opisano w dokumentacji projektu:

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

Panel administracyjny nasłuchuje na porcie 10254, a brama na porcie 10255. Rzeczywiste dane uwierzytelniające są przechowywane jeden raz. Każdy agent otrzymuje wartość zastępczą zamiast klucza oraz własny token dostępu o ograniczonym zakresie, który wysyła w nagłówku Proxy-Authorization. Brama dopasowuje żądanie wychodzące na podstawie hosta i ścieżki, odszyfrowuje pasujące dane uwierzytelniające i podstawia je. Środowisko agenta nie zawiera danych, które warto ukraść.

Wartość tego rozwiązania nie polega na samym szyfrowaniu. Chodzi o to, że pytanie „czego ten agent użył i kiedy” staje się zapytaniem do dziennika. Odczytuje się jeden dziennik audytowy, zamiast zgadywać, w którym z sześciu środowisk znajdowała się kopia klucza.

Przekaż sekret procesowi, a nie środowisku

Jeśli agent jest uruchamiany przez systemd, zmienne środowiskowe nie są w ogóle potrzebne. LoadCredential= umieszcza sekret w prywatnym katalogu, do którego dostęp ma tylko ta usługa. W pliku jednostki jest on udostępniany jako %d, a wewnątrz procesu jako $CREDENTIALS_DIRECTORY. Wartość nigdy nie pojawia się w /proc/<pid>/environ, dlatego ps eww nie może jej wyświetlić. Katalog jest usuwany po zatrzymaniu usługi.

Najpierw zaszyfruj poświadczenie dla tego komputera. Poniższe polecenia pochodzą z dokumentacji systemd i działają w systemd 250 lub nowszym. Obejmuje to Ubuntu 24.04 i 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

Ostatnie polecenie wyświetla sk-example-value. Potwierdza to, że zaszyfrowany plik można odszyfrować na tym hoście. Następnie odwołaj się do niego w jednostce:

[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

Kod agenta otwiera plik $AGENT_KEY_FILE, gdy potrzebuje tej wartości. Odczyt pliku trwa tylko chwilę. Zmienna środowiskowa istnieje przez cały czas działania procesu oraz w każdym uruchomionym przez niego procesie potomnym.

Preferuj tokeny krótkotrwałe zamiast kluczy długoterminowych

Klucz, który nigdy nie wygasa, pozostaje ważny za każdym razem, gdy pojawi się po kilku miesiącach w dzienniku lub transkrypcji. Jeżeli usługa udostępnia token sesji, należy użyć tokenu sesji i ustawić najkrótszy czas życia dopuszczalny dla danego zadania.

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

Fifteen minutes is the minimum AWS STS (security token service) accepts, and it is usually enough for one agent task. W GitHub należy przydzielić użytkownikowi agenta osobny login gh z tokenem o szczegółowym zakresie uprawnień, ograniczonym do pojedynczego repozytorium, na którym agent pracuje, aby gh auth token w ramach tej sesji zwracało dane, które nie umożliwiają dostępu do innych zasobów. Zakres należy najpierw ograniczyć według zasobu, a następnie według czasu.

Weryfikuj, a następnie stale weryfikuj

Po każdej zmianie konfiguracji agenta warto wykonać trzy testy. Należy uruchomić je jako użytkownik agenta, a nie jako bieżący użytkownik.

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

Pierwszy test nie powinien wyświetlić żadnych danych. Drugi powinien wyświetlić ls: cannot open directory '/home/you/': Permission denied. Trzeci pokazuje tożsamość prezentowaną przez ścieżkę sieciową agenta. Jest to informacja, której uzyskaniu służy wzorzec bramy: 401 oznacza, że agent nie używa własnych poświadczeń GitHub, a 200 oznacza, że ich używa. Należy wtedy ustalić, którego tokenu używa. W przypadku agentów uruchamianych bez nadzoru opis kontrolowanie kosztów agentów AI na VPS przedstawia limity budżetowe odpowiadające tym limitom dostępu.

FAQ

Czy można po prostu zaufać modelowi, że nie ujawni kluczy?

Nie, ponieważ w tym modelu zagrożeń atakującym nie jest model. Agent odczytuje tekst ze stron internetowych, repozytoriów i systemów śledzenia zgłoszeń. Taki tekst może zawierać instrukcje. Model nie ma niezawodnego sposobu odróżnienia własnych instrukcji od pobranego tekstu. Każda kontrola, która zależy od prawidłowej decyzji modelu, zawiedzie przy pierwszej przekonującej instrukcji wstrzykniętej do treści. Dlatego kontrola musi znajdować się w systemie operacyjnym lub w sieci.

Czy zmienne środowiskowe rzeczywiście są tak niebezpieczne w przypadku sekretów agenta?

Są niebezpieczne w konkretny sposób: są dziedziczone. Każdy proces potomny uruchamiany przez agenta otrzymuje ich kopię, w tym skrypt kompilacji, narzędzie do uruchamiania testów i każdy hook instalacyjny pakietu. Zmienne można również odczytać przez /proc/<pid>/environ, jeśli proces działa jako ten sam użytkownik. Wszystko, co agent uruchomi, może więc odczytać te zmienne bez przekazywania ich przez agenta. Odczyt pliku w chwili użycia, za pomocą LoadCredential= lub bramy, ogranicza ujawnienie do tego momentu.

Czy umieszczenie sekretów w skarbcu samo rozwiązuje ten problem?

Tylko częściowo. Skarbiec rozwiązuje problem przechowywania. Nie rozwiązuje ostatniego etapu, w którym jakiś komponent pobiera sekret ze skarbca i przekazuje go agentowi jako zmienną środowiskową. Wtedy sytuacja wraca do punktu wyjścia. Decydujące znaczenie ma to, który komponent wykonuje podstawienie. Jeśli sekret pobiera agent, agent go posiada. Jeśli podstawienie wykonuje brama lub system init poza procesem agenta, agent nigdy go nie przechowuje.

Jak ustalić, czy agent już coś ujawnił?

Zwykle nie można tego ustalić po fakcie. Jest to argument za użyciem bramy. Bez niej dowody są rozproszone między historią powłoki, transkrypcją agenta i logami połączeń wychodzących, których prawdopodobnie nie przechowuje się. W przypadku bramy poświadczeń każde użycie poświadczenia jest jednym wierszem zawierającym tożsamość agenta i znacznik czasu. W razie podejrzenia wycieku należy najpierw przeprowadzić rotację klucza, a dopiero potem rozpocząć analizę. Rotacja jest tania, a pewność nie.

Co należy zrobić co najmniej dzisiaj?

Należy przenieść każdy plik .env poza katalogi, w których pracują agenci, oraz utworzyć jednego nieuprzywilejowanego użytkownika dla każdego agenta. Te dwie zmiany zajmują około 10 minut i zamykają najczęstszą ścieżkę ataku: agent odczytuje plik poświadczeń, który nie miał powodu znajdować się obok kodu. Brama i tokeny krótkotrwałe są kolejnym etapem, a nie pierwszym. Ten sam punkt wyjścia dotyczy każdego środowiska uruchomieniowego agenta, w tym bezpiecznego uruchamiania autonomicznego agenta na VPS.