Come tenere i segreti fuori dagli agenti AI
Una chiave API nell'ambiente può essere esfiltrata in una sola chiamata. Usa token con ambito limitato e breve durata tramite un gateway delle credenziali.
Cosa significa tenere i segreti fuori dagli agenti AI
Un agente AI è un normale processo Linux che esegue comandi. Il codice eseguito dal processo può leggere ogni variabile d'ambiente che il processo contiene. Di conseguenza, una chiave API presente nell'ambiente dell'agente può essere inviata dall'agente a qualsiasi host raggiungibile. Tenere i segreti fuori dall'agente significa fornirgli un riferimento invece della chiave: un token con ambito limitato e durata breve oppure un segnaposto che un altro componente sostituisce con il valore reale al confine di rete.
Non si tratta di un modello che diventa ostile. Il meccanismo è più semplice. Un agente legge una pagina web, un README o un commento in un issue che contiene istruzioni e le segue, perché per un modello linguistico non c'è differenza tra il testo scritto dall'utente e quello recuperato. Questo è il prompt injection. Quando accade, il danno è limitato da un solo elemento: ciò che il processo può leggere. Se non hai ancora impostato un confine, eseguire in sicurezza un coding agent su un server descrive i livelli di isolamento su cui si basa questa guida.
Il modello delle minacce in termini semplici
Esegui questo comando con lo stesso utente con cui viene eseguito il tuo agent.
tr '\0' '\n' < /proc/self/environ | grep -iE 'key|token|secret|password'Ogni riga che stampa equivale a una richiesta HTTP verso il server di uno sconosciuto. Ora verifica quali dati sono presenti sul disco vicino all'agent.
grep -rIl --exclude-dir=.git -e 'API_KEY' -e 'SECRET' ~/projects
find ~ -maxdepth 3 -name '.env' -o -name 'credentials' -o -name '*.pem'Un agent con accesso a una shell non ha bisogno di un exploit sofisticato per trasferire quei dati all'esterno. Quattro percorsi comuni sono sufficienti e, nei log, sembrano tutti normali attività:
- Una richiesta in uscita
curlofetchverso qualsiasi host, con il valore nella stringa di query. - Un
git commite ungit pushverso un repository su cui l'agent può scrivere. - Uno script di installazione di un pacchetto, che esegue codice arbitrario con l'utente dell'agent.
- Una ricerca DNS di un hostname che contiene il valore, che lascia il sistema anche quando il traffico HTTP in uscita è bloccato.
Non puoi risolvere il problema limitandoti a esaminare il codice. La soluzione consiste nell'assicurarti che non ci siano dati di valore raggiungibili.
Un segreto nell'albero di lavoro è un segreto nella finestra di contesto
Un agente legge i file. Un file .env nel repository in cui sta lavorando verrà letto e, una volta letto, si troverà nella finestra di contesto. Questo significa che sarà presente nella trascrizione, in qualsiasi log conservato e in ciò che l'agente scriverà dopo.
Prima, con la chiave nell'albero in cui lavora l'agente:
cd ~/projects/billing
cat .env
# STRIPE_SECRET_KEY=sk_live_...
# DATABASE_URL=postgres://app:hunter2@db.internal:5432/billingDopo, con il file spostato fuori dalla sua portata:
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/.envL'utente dell'agente non può più aprire il file, perché l'albero di lavoro non lo contiene più. Le regole di negazione nella configurazione dell'agente sono un secondo livello, non il primo. Claude Code legge le regole di autorizzazione da .claude/settings.json nel progetto:
{
"permissions": {
"deny": ["Read(./.env)", "Read(./secrets/**)", "Read(./**/*.pem)"]
}
}Questo impedisce all'agente di aprire accidentalmente un file durante l'esplorazione. Non impedisce a un'istruzione iniettata di eseguire base64 .env, perché si tratta di un comando shell e non della lettura di un file. Considera la configurazione una protezione e i permessi del filesystem una barriera. La stessa separazione si applica nei container: file env e segreti in Docker Compose tratta la versione di questo problema a un livello inferiore.
Assegna a ogni agente un proprio utente senza privilegi
Se l'agente viene eseguito con il tuo utente, eredita le tue chiavi SSH, le tue credenziali cloud e la cronologia della shell. Un utente separato richiede un solo comando e rimuove tutti questi elementi.
sudo adduser --disabled-password --gecos "" agent-review
sudo chmod 700 /home/agent-review
sudo -u agent-review cat ~/.ssh/id_ed25519L'ultima riga deve fallire con cat: /home/you/.ssh/id_ed25519: Permission denied. Se invece stampa una chiave, la directory home è leggibile dal gruppo o da tutti gli utenti e chmod 700 ~ risolve il problema. Non aggiungere l'utente dell'agente a sudo e non assegnargli una regola NOPASSWD più ampia dell'unico comando di cui ha effettivamente bisogno. Utenti con privilegi minimi su un VPS descrive in dettaglio i gruppi e la configurazione di sudoers.
Su un VPS cloud è opportuno aggiungere un'ulteriore barriera. Il servizio dei metadati dell'istanza risponde a un indirizzo link-local fisso e spesso fornisce le credenziali del ruolo a qualsiasi richiesta.
sudo iptables -A OUTPUT -m owner --uid-owner agent-review -d 169.254.169.254 -j REJECTVerificalo dal punto di vista dell'agente. sudo -u agent-review curl -s --max-time 3 http://169.254.169.254/ non dovrebbe stampare nulla e dovrebbe terminare con un codice diverso da zero, perché il pacchetto viene rifiutato prima di lasciare il sistema.
Iniettare la credenziale al confine
Il modello che risolve effettivamente questo problema è l'iniezione delle credenziali. L'agente non conserva mai una chiave reale. Invia la richiesta tramite un gateway locale, che sostituisce un segnaposto con il segreto reale durante l'invio. Il segreto risiede nello storage del gateway, in un processo distinto, eseguito da un utente diverso.
OneCLI è un'implementazione open source di questo modello, con licenza Apache-2.0, ed è eseguita in un container accanto all'agente. A luglio 2026 il progetto documenta questa configurazione:
git clone https://github.com/onecli/onecli.git
cd onecli
docker compose -f docker/docker-compose.yml up -d --waitLa dashboard è in ascolto sulla porta 10254 e il gateway sulla porta 10255. Si memorizza la credenziale reale una sola volta, quindi si assegna a ogni agente un valore segnaposto al posto della chiave, insieme al proprio token di accesso con ambito limitato, che l'agente invia in un'intestazione Proxy-Authorization. Il gateway individua la richiesta in uscita in base all'host e al percorso, decrittografa la credenziale corrispondente e la sostituisce. Nell'ambiente dell'agente non rimane nulla di utile da sottrarre.
Il vantaggio non risiede nella crittografia. Consiste nel fatto che la domanda "che cosa ha usato questo agente e quando" diventa una query nei log. Si consulta un unico audit trail invece di cercare di capire quale delle sei istanze dell'ambiente contenesse una copia della chiave.
Passare il segreto al processo, non all'ambiente
Se si esegue l'agent con systemd, non sono necessarie variabili d'ambiente. LoadCredential= inserisce il segreto in una directory privata che solo quel servizio può leggere. Nel file dell'unità viene esposto come %d e all'interno del processo come $CREDENTIALS_DIRECTORY. Il valore non compare mai in /proc/<pid>/environ, quindi ps eww non può mostrarlo. La directory scompare quando il servizio si arresta.
Crittografare prima la credenziale per la macchina. Questi comandi provengono dalla documentazione di systemd e funzionano con systemd 250 o versioni successive, incluse Ubuntu 24.04 e 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_keyL'ultimo comando stampa sk-example-value. Questo conferma che il file crittografato viene decrittografato correttamente su questo host. Quindi fare riferimento al file nell'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-workerIl codice dell'agent apre il file in $AGENT_KEY_FILE quando deve leggere il valore. La lettura di un file è momentanea. Una variabile d'ambiente rimane disponibile per tutta la durata del processo, in ogni processo figlio che questo avvia.
Preferisci token di breve durata alle chiavi di lunga durata
Una chiave che non scade resta valida ogni volta che ricompare, anche mesi dopo, in un log o in una trascrizione. Se il servizio offre un token di sessione, usa il token di sessione e imposta la durata più breve compatibile con l'attività.
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/agent-readonly \
--role-session-name agent-review \
--duration-seconds 900Quindici minuti è la durata minima accettata da AWS STS (security token service) e di solito è sufficiente per un'attività di un agente. Per GitHub, assegna all'utente dell'agente un proprio accesso gh con un token a granularità fine, limitato al singolo repository su cui lavora, in modo che gh auth token all'interno di quella sessione restituisca qualcosa che non possa accedere ad altro. Limita prima l'ambito per risorsa, poi per durata.
Verifica, poi continua a verificare
Dopo ogni modifica alla configurazione di un agent, esegui tre controlli. Eseguili come utente dell'agent, non come te.
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/userIl primo non dovrebbe stampare nulla. Il secondo dovrebbe stampare ls: cannot open directory '/home/you/': Permission denied. Il terzo indica quale identità presenta il percorso di rete dell'agent. Questa è la domanda a cui risponde il pattern del gateway: 401 indica che l'agent non sta usando credenziali GitHub proprie, mentre 200 indica che ne sta usando una. In questo caso devi sapere quale token sta utilizzando. Se esegui agent senza supervisione, controllare i costi degli agent AI su un VPS descrive i limiti di budget da abbinare a questi limiti di accesso.
FAQ
Posso fidarmi del modello e confidare che non divulghi le mie chiavi?
No, perché in questo modello di minaccia il modello non è l'attaccante. L'agent legge testo da pagine web, repository e sistemi di gestione delle issue, e quel testo può contenere istruzioni. Il modello non ha un modo affidabile per distinguere le tue istruzioni dal testo che ha recuperato. Qualsiasi controllo che dipenda dalla scelta corretta del modello fallisce la prima volta che un'istruzione iniettata risulta convincente. Il controllo deve quindi essere applicato nel sistema operativo o nella rete.
Le variabili d'ambiente sono davvero così rischiose per i secret dell'agent?
Sono rischiose per un motivo specifico: vengono ereditate. Ogni processo figlio avviato dall'agent ne riceve una copia, inclusi uno script di build, un test runner e qualsiasi hook di installazione di un package. Le variabili sono inoltre leggibili tramite /proc/<pid>/environ dallo stesso utente. Di conseguenza, qualsiasi processo avviato dall'agent può leggerle senza che l'agent le passi esplicitamente. Un file letto al momento dell'uso, tramite LoadCredential= o un gateway, limita l'esposizione a quel momento.
Inserire i secret in un vault risolve il problema da solo?
Solo in parte. Un vault risolve il problema dell'archiviazione. Non risolve l'ultimo passaggio: qualcosa estrae il secret dal vault e lo consegna all'agent come variabile d'ambiente, riportandoti al punto di partenza. Ciò che conta è chi esegue la sostituzione. Se l'agent recupera il secret, l'agent ne entra in possesso. Se la sostituzione viene eseguita da un gateway o dal sistema init al di fuori del processo dell'agent, l'agent non entra mai in possesso del secret.
Come posso sapere se un agent ha già divulgato qualcosa?
Di norma, non puoi saperlo a posteriori. Questo è il motivo per usare il gateway. Senza un gateway, le prove sono distribuite tra la cronologia della shell, il transcript dell'agent e i log delle connessioni in uscita, che probabilmente non stai conservando. Con un credential gateway, ogni uso di una credenziale è registrato su una riga con l'identità dell'agent e un timestamp. Se sospetti una divulgazione, esegui prima la rotazione della chiave e svolgi l'indagine dopo. La rotazione ha un costo ridotto, mentre la certezza non lo ha.
Qual è il minimo che dovrei fare oggi?
Sposta ogni file .env fuori dalle directory in cui lavorano gli agent e crea un utente non privilegiato per ogni agent. Queste due modifiche richiedono circa dieci minuti e chiudono il percorso più comune: un agent legge un file di credenziali che non aveva motivo di trovarsi accanto al codice. Il gateway e i token a breve durata sono il passaggio successivo, non quello iniziale. Lo stesso punto di partenza vale per qualsiasi runtime di agent, incluso eseguire in sicurezza un agent autonomo su un VPS.