SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-28

Jak bezpiecznie zarządzać kluczami API w agentach AI

Dowiedz się, jak chronić klucze API przed wyciekiem w wyniku ataku typu prompt injection. Zastosuj krótkotrwałe tokeny i bramy dostępowe zamiast przekazywania pełnych uprawnień.

Co oznacza utrzymywanie sekretów poza agentami AI

Agent AI to standardowy proces systemu Linux wykonujący polecenia. Każda zmienna środowiskowa przypisana do tego procesu jest odczytywalna przez uruchamiany przez niego kod, więc klucz API w środowisku agenta jest kluczem, który agent może wysłać do dowolnego dostępnego hosta. Utrzymywanie sekretów poza agentem oznacza przekazanie mu uchwytu zamiast klucza: krótkotrwałego tokena o ograniczonym zakresie lub symbolu zastępczego, który jest wymieniany na rzeczywistą wartość na granicy sieci przez inny komponent.

Nie jest to historia o modelu stającym się wrogim. Mechanizm jest bardziej prozaiczny. Agent odczytuje stronę internetową, plik README lub komentarz w zgłoszeniu 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 z zewnątrz. To jest wstrzykiwanie promptu (prompt injection). Gdy do tego dojdzie, zakres szkód jest ograniczony dokładnie przez jedną rzecz: to, co proces może odczytać. Jeśli granice nie zostały jeszcze wyznaczone, bezpieczne uruchamianie agenta programistycznego na serwerze opisuje poziomy izolacji, na których opiera się ten przewodnik.

Model zagrożeń w prostych słowach

Uruchom to jako użytkownik, na którego koncie działa Twój agent.

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

Każda linia, którą wypisuje, znajduje się w odległości jednego żądania HTTP od serwera nieznanej osoby. Teraz spójrz, co znajduje 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 z dostępem do powłoki nie potrzebuje wyrafinowanego exploita, aby wyprowadzić te dane. Cztery zwykłe ścieżki wykonują to zadanie, a wszystkie cztery wyglądają w logach jak normalna praca:

  • Wychodzące połączenie curl lub fetch do dowolnego hosta, z wartością umieszczoną w ciągu zapytania (query string).
  • git commit oraz git push do repozytorium, do którego agent ma uprawnienia zapisu.
  • Skrypt instalacyjny pakietu, który uruchamia dowolny kod jako użytkownik agenta.
  • Zapytanie DNS o nazwę hosta zawierającą tę wartość, co działa nawet wtedy, gdy ruch wychodzący HTTP jest zablokowany.

Nie da się rozwiązać tego problemu poprzez samą weryfikację kodu. Rozwiązaniem jest upewnienie się, że w zasięgu agenta nie znajduje się nic wartościowego.

Sekret w drzewie roboczym to sekret w oknie kontekstowym

Agent odczytuje pliki. Plik .env w repozytorium, w którym pracuje agent, zostanie odczytany, a po odczytaniu trafi do okna kontekstowego. Oznacza to, że znajdzie się w transkrypcji, w każdym prowadzonym dzienniku oraz w każdej treści wygenerowanej przez agenta.

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

Po przeniesieniu pliku poza 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ę zabezpieczeń, a nie pierwszą. Claude Code odczytuje reguły uprawnień z pliku .claude/settings.json w projekcie:

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

Zapobiega to przypadkowemu otwarciu pliku przez agenta podczas eksploracji. Nie powstrzymuje to jednak wstrzykniętej instrukcji przed uruchomieniem base64 .env, ponieważ jest to polecenie powłoki, a nie odczyt pliku. To, czy użytkownik otrzyma zapytanie przed uruchomieniem takiego polecenia, zależy od trybu uprawnień sesji, a tryb auto stanie się domyślnym dla Claude Code w sierpniu 2026, więc serwer, który nie jest monitorowany, będzie uruchamiał więcej takich poleceń bez monitu. To samo ograniczenie dotyczy wszystkiego, co kształtuje nawyki agenta, a nie jego uprawnienia: umiejętność nakazująca agentowi wprowadzanie najmniejszych działających zmian zapobiega wykraczaniu poza pliki, których agent nie powinien otwierać, jednak nadal jest to tylko porada, od której model można odwieść. Należy traktować konfigurację jako barierkę ochronną, a uprawnienia systemu plików jako mur. Ten sam podział dotyczy kontenerów: pliki env i sekrety w Docker Compose opisuje wersję tego problemu na niższym poziomie.

Nadaj każdemu agentowi osobnego użytkownika bez uprawnień

Jeśli agent działa na Twoim koncie, dziedziczy Twoje klucze SSH, poświadczenia chmurowe oraz historię powłoki. Osobny użytkownik wymaga wykonania tylko jednego polecenia i eliminuje wszystkie te zagrożenia.

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

Ostatnia linia musi zakończyć się niepowodzeniem z komunikatem cat: /home/you/.ssh/id_ed25519: Permission denied. Jeśli zamiast tego wyświetlony zostanie klucz, oznacza to, że Twój katalog domowy jest dostępny do odczytu dla grupy lub wszystkich użytkowników, a polecenie chmod 700 ~ naprawi ten problem. Nie dodawaj użytkownika agenta do grupy sudo i nie nadawaj mu reguły NOPASSWD szerszej niż ta, która jest niezbędna do wykonania pojedynczego polecenia. Użytkownicy z minimalnymi uprawnieniami na VPS szczegółowo omawia kwestie grup i pliku sudoers. Pamiętaj o tej separacji, gdy uruchamiasz więcej niż jedną sesję na serwerze, ponieważ jedna sesja Claude Code może wysłać tekst bezpośrednio do drugiej, a wszystko, do czego ma dostęp pierwsza sesja, może zostać przesłane tym kanałem w pojedynczej wiadomości.

Warto dodać jeszcze jedną barierę na chmurowym serwerze VPS. Usługa metadanych instancji odpowiada pod stałym lokalnym adresem łącza i często udostępnia poświadczenia roli każdemu, kto o nie zapyta.

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

Sprawdź to z poziomu agenta. Polecenie sudo -u agent-review curl -s --max-time 3 http://169.254.169.254/ powinno nie wyświetlić nic i zakończyć się kodem wyjścia innym niż zero, ponieważ pakiet jest odrzucany, zanim opuści serwer.

Wstrzykiwanie poświadczeń na granicy systemu

Wzorcem, który faktycznie rozwiązuje ten problem, jest wstrzykiwanie poświadczeń. Agent nigdy nie przechowuje rzeczywistego klucza. Wysyła żądanie przez lokalną bramę, a brama w trakcie przesyłania zamienia symbol zastępczy na właściwy sekret. Sekret znajduje się w pamięci bramy, w innym procesie, należącym do innego użytkownika.

OneCLI to otwartoźródłowa implementacja tego rozwiązania, dostępna na licencji Apache-2.0, która działa jako kontener obok agenta. Według stanu na lipiec 2026 projekt dokumentuje tę konfigurację:

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

Panel sterowania nasłuchuje na porcie 10254, a brama na 10255. Rzeczywiste poświadczenie przechowuje się jednokrotnie, a następnie każdemu agentowi przypisuje się wartość zastępczą zamiast klucza oraz własny token dostępu o ograniczonym zakresie, przesyłany w nagłówku Proxy-Authorization. Brama dopasowuje wychodzące żądanie na podstawie hosta i ścieżki, odszyfrowuje pasujące poświadczenie i dokonuje podstawienia. Środowisko agenta nie zawiera żadnych danych, które warto byłoby wykraść.

Wartość tego rozwiązania nie leży w samym szyfrowaniu. Polega ona na tym, że pytanie „czego użył ten agent i kiedy” staje się zapytaniem do dziennika zdarzeń. Analizuje się jeden ślad audytowy zamiast zgadywać, które z sześciu środowisk zawierało kopię klucza.

Przekazywanie sekretu do procesu, a nie do środowiska

W przypadku uruchamiania agenta przez systemd zmienne środowiskowe nie są wymagane. LoadCredential= umieszcza sekret w prywatnym katalogu, który może odczytać tylko dana usługa. Jest on udostępniany jako %d w pliku jednostki oraz jako $CREDENTIALS_DIRECTORY wewnątrz procesu. Wartość ta nigdy nie pojawia się w /proc/<pid>/environ, więc ps eww nie może jej wyświetlić, a katalog znika po zatrzymaniu usługi.

Najpierw należy zaszyfrować poświadczenie dla danej maszyny. Poniższe polecenia pochodzą z dokumentacji systemd i działają w wersji 250 lub nowszej, co obejmuje systemy Ubuntu 24.04 oraz 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 jest poprawnie odszyfrowywany na tym hoście. Następnie należy odwołać się do niego w pliku jednostki:

[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 w lokalizacji $AGENT_KEY_FILE, gdy potrzebuje danej wartości. Odczyt pliku jest operacją chwilową. Zmienna środowiskowa trwa przez cały czas życia procesu i jest dziedziczona przez każdy proces potomny.

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

Klucz, który nigdy nie wygasa, pozostaje ważny w każdym momencie, gdy po miesiącach pojawi się w logach lub zapisie sesji. Jeśli usługa oferuje token sesji, należy go użyć i ustawić najkrótszy czas życia, na jaki pozwala wykonywane zadanie.

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

Piętnaście minut to minimalny czas akceptowany przez AWS STS (security token service), co zazwyczaj wystarcza na wykonanie jednego zadania przez agenta. W przypadku GitHub należy utworzyć dla użytkownika-agenta osobny login gh z tokenem o ograniczonym zakresie, przypisanym tylko do jednego repozytorium, w którym agent pracuje. Dzięki temu gh auth token w ramach tej sesji zwróci wynik, który nie pozwoli na dostęp do żadnych innych zasobów. Zakres uprawnień należy ograniczać najpierw według zasobów, a następnie według czasu.

Weryfikacja i ciągłe monitorowanie

Po każdej zmianie w konfiguracji agenta należy wykonać trzy testy. Należy je uruchomić z uprawnieniami użytkownika agenta, a nie jako administrator.

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

Pierwsze polecenie nie powinno zwrócić żadnego wyniku. Drugie powinno wyświetlić ls: cannot open directory '/home/you/': Permission denied. Trzecie polecenie wskazuje tożsamość, którą przedstawia ścieżka sieciowa agenta; jest to kluczowa informacja w kontekście wzorca gateway: wynik 401 oznacza, że agent nie posiada własnych poświadczeń GitHub, natomiast 200 oznacza, że agent je posiada, co pozwala zidentyfikować używany token. W przypadku uruchamiania agentów w trybie bezobsługowym, sekcja kontrola kosztów agentów AI na VPS zawiera informacje o limitach budżetowych, które należy stosować wraz z tymi ograniczeniami dostępu.

FAQ

Czy mogę po prostu zaufać modelowi, że nie ujawni moich kluczy?

Nie, ponieważ w tym modelu zagrożeń to nie model jest atakującym. Agent odczytuje tekst ze stron internetowych, repozytoriów i systemów śledzenia zgłoszeń, a tekst ten może zawierać instrukcje. Model nie posiada niezawodnego sposobu na odróżnienie Twoich instrukcji od pobranego tekstu. Każde zabezpieczenie polegające na poprawnym wyborze modelu zawiedzie, gdy tylko wstrzyknięta instrukcja okaże się przekonująca. Dlatego mechanizm kontrolny musi znajdować się w systemie operacyjnym lub w sieci.

Czy zmienne środowiskowe są rzeczywiście tak złe dla sekretów agenta?

Są one szkodliwe w jednym konkretnym aspekcie: są dziedziczone. Każdy proces potomny uruchamiany przez agenta otrzymuje ich kopię, w tym skrypty budowania, narzędzia testowe oraz wszelkie skrypty instalacyjne pakietów. Zmienne te są również odczytywalne przez /proc/<pid>/environ przez tego samego użytkownika, więc każdy proces uruchomiony przez agenta może je odczytać bez konieczności przekazywania ich przez agenta. Odczyt pliku w momencie użycia, za pomocą LoadCredential= lub bramy, ogranicza ekspozycję tylko do tego konkretnego momentu.

Czy umieszczenie sekretów w skarbcu (vault) rozwiązuje ten problem samodzielnie?

Tylko częściowo. Skarbiec rozwiązuje kwestię przechowywania. Jeśli hostujesz go samodzielnie, wymaga on własnego procesu utwardzania, ponieważ serwer Vaultwarden jest zazwyczaj przełamywany przez token administratora lub plik kopii zapasowej, a nie przez zaszyfrowane elementy, które zawiera. Nie rozwiązuje to ostatniego etapu, w którym proces pobiera sekret ze skarbca i przekazuje go agentowi jako zmienną środowiskową, co przywraca stan wyjściowy. Kluczowe jest to, kto wykonuje podstawienie. Jeśli sekret pobiera agent, to agent go posiada. Jeśli podstawienie wykonuje brama lub system init poza procesem agenta, agent nigdy nie wchodzi w jego posiadanie.

Skąd mam wiedzieć, czy agent już coś ujawnił?

Zazwyczaj nie da się tego stwierdzić po fakcie, co stanowi argument za stosowaniem bramy. Bez niej dowody są rozproszone w historii powłoki, transkrypcjach agenta oraz logach połączeń wychodzących, których prawdopodobnie nie przechowujesz. Dzięki bramie poświadczeń każde użycie danych uwierzytelniających to jedna linia z identyfikatorem agenta i znacznikiem czasu. Jeśli podejrzewasz wyciek, najpierw zrotuj klucz, a dopiero potem prowadź dochodzenie. Rotacja jest tania, a pewność nie.

Co powinienem zrobić dzisiaj w pierwszej kolejności?

Przenieś każdy plik .env poza katalogi, w których pracują Twoi agenci, i utwórz jednego użytkownika bez uprawnień dla każdego agenta. Te dwie zmiany zajmują około dziesięciu minut i zamykają najczęstszą ścieżkę ataku, czyli odczyt przez agenta pliku z poświadczeniami, który nie powinien znajdować się obok kodu. Brama i krótkotrwałe tokeny to kolejny krok, a nie pierwszy. Ten sam punkt wyjścia dotyczy każdego środowiska uruchomieniowego agenta, w tym bezpiecznego uruchamiania autonomicznego agenta na VPS.