SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-27

Come tenere i secret fuori dagli agenti AI

Una API key nell'ambiente può essere esfiltrata in una sola chiamata. Usa token con ambito limitato e breve durata tramite un credential gateway, mai la chiave reale.

Cosa significa tenere i secret fuori dagli agenti AI

Un agente AI è un normale processo Linux che esegue comandi. Il codice eseguito dal processo può leggere tutte le variabili d'ambiente disponibili. Di conseguenza, una API key presente nell'ambiente dell'agente può essere inviata dall'agente a qualsiasi host raggiungibile. Tenere i secret fuori dall'agente significa fornirgli un riferimento invece della chiave: un token con ambito limitato e validità breve, oppure un segnaposto che un altro componente sostituisce con il valore reale al confine della 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 a una issue che contiene istruzioni e le segue, perché per un modello linguistico non c'è differenza tra il testo scritto dall'utente e quello recuperato dal modello. Questo è il prompt injection. Dopo l'iniezione, l'entità del danno dipende da un solo elemento: ciò che il processo può leggere. Se non hai ancora definito un confine, eseguire in sicurezza un agente di coding su un server descrive i livelli di isolamento su cui si basa questa guida.

Il modello di minaccia in termini semplici

Esegui questo comando con lo stesso utente utilizzato dall'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 cosa si trova sul disco nelle vicinanze dell'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, tutti e quattro sembrano normali attività:

  • Un curl o fetch in uscita verso qualsiasi host, con il valore nella stringa di query.
  • Un git commit e git push verso 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; la richiesta esce anche quando il traffico HTTP in uscita è bloccato.

Non puoi risolvere il problema limitandoti a esaminare il codice. La soluzione consiste nell'assicurarsi che l'agent non possa raggiungere dati di valore.

Un secret presente nell’albero di lavoro è un secret nella finestra di contesto

Un agent legge i file. Un file .env presente nel repository su cui sta lavorando verrà letto e, una volta letto, entrerà nella finestra di contesto. Questo significa che sarà presente nella trascrizione, in qualsiasi log conservato e in tutto ciò che l’agent scriverà in seguito.

Prima, con la chiave presente nell’albero su cui lavora l’agent:

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

Dopo, con il file spostato fuori 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/.env

L’utente dell’agent non può più aprire il file, perché l’albero di lavoro non lo contiene più. Le regole di negazione nella configurazione dell’agent 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’agent 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. La richiesta di conferma prima dell’esecuzione del comando dipende dalla modalità di autorizzazione della sessione; inoltre, la modalità automatica diventerà quella predefinita di Claude Code nell’agosto 2026, quindi un server che non si sta monitorando eseguirà più comandi senza chiedere conferma. Lo stesso limite vale per tutto ciò che influenza le abitudini dell’agent invece delle sue autorizzazioni: una skill che vincola l’agent ad applicare la modifica minima funzionante impedisce a un’esecuzione di esplorare file che non avrebbe motivo di aprire, ma resta comunque un’indicazione da cui il modello può essere convinto a deviare. Considera la configurazione come una barriera di sicurezza e i permessi del filesystem come il muro. La stessa distinzione vale nei container: i file env e i secret in Docker Compose descrivono la variante di questo problema a un livello più interno.

Assegna a ogni agente un utente senza privilegi

Se l'agente viene eseguito con il tuo account, eredita le tue chiavi SSH, le tue credenziali cloud e la cronologia della shell. Un utente separato richiede un solo comando e rimuove tutto questo.

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

L'ultima riga deve fallire con cat: /home/you/.ssh/id_ed25519: Permission denied. Se 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 gruppi e sudoers. Mantieni questa separazione quando esegui più di una sessione sul server, perché una sessione di Claude Code può inviare testo direttamente a un'altra e tutto ciò che la prima sessione contiene può attraversare quel canale in un singolo messaggio.

Su un cloud VPS è utile aggiungere un ulteriore confine. Il servizio di metadati dell'istanza risponde su un indirizzo link-local fisso e spesso fornisce le credenziali del ruolo a qualsiasi client le richieda.

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

Verificalo dal punto di vista dell'agente. sudo -u agent-review curl -s --max-time 3 http://169.254.169.254/ deve non stampare nulla e terminare con un codice diverso da zero, perché il pacchetto viene rifiutato prima di lasciare il server.

Iniettare la credenziale al confine

Il modello che risolve realmente questo problema è l'iniezione delle credenziali. L'agent non conserva mai una chiave reale. Invia la richiesta tramite un gateway locale, che sostituisce un segnaposto con il secret reale prima di inoltrarla. Il secret risiede nello storage del gateway, in un processo diverso, eseguito da un altro utente.

OneCLI è un'implementazione open source di questo modello, distribuita con licenza Apache-2.0, ed esegue come container accanto all'agent. 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 --wait

La 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 agent un valore segnaposto al posto della chiave e un access token con ambito limitato dedicato, che l'agent invia in un header Proxy-Authorization. Il gateway associa la richiesta in uscita in base a host e percorso, decritta la credenziale corrispondente e la sostituisce. Nell'ambiente dell'agent non è presente nulla di utile da sottrarre.

Il vantaggio non è la cifratura. È che la domanda "quale credenziale ha usato questo agent e quando" diventa una query sui log. Si consulta un unico audit trail invece di cercare di capire in quale delle sei configurazioni fosse presente una copia della chiave.

Fornisci il secret al processo, non all'ambiente

Se esegui l'agent con systemd, non hai bisogno delle variabili d'ambiente. LoadCredential= inserisce il secret in una directory privata leggibile solo da quel servizio. 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. Di conseguenza, ps eww non può mostrarlo e la directory scompare quando il servizio viene arrestato.

Cifra prima la credenziale per la macchina. Questi comandi provengono dalla documentazione di systemd e funzionano con systemd 250 o versioni successive, quindi anche su 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_key

L'ultimo comando stampa sk-example-value. Questo dimostra che il file cifrato può essere decifrato su questo host. Fai quindi 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-worker

Il codice dell'agent apre il file in $AGENT_KEY_FILE quando deve leggere il valore. La lettura di un file dura solo un istante. Una variabile d'ambiente resta disponibile per tutta la durata del processo e per ogni processo figlio che questo avvia.

Preferisci i token a breve durata alle chiavi a lunga durata

Una chiave che non scade resta valida ogni volta che riemerge, 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 900

Quindici minuti è il valore minimo accettato da AWS STS (security token service) e di solito è sufficiente per un'attività dell'agente. Per GitHub, assegna all'utente dell'agente un proprio accesso gh con un token a granularità fine limitato al singolo repository su cui opera, in modo che gh auth token durante quella sessione restituisca qualcosa che non possa accedere ad altro. Limita prima per risorsa, poi per durata.

Verificare, quindi continuare a verificare

Dopo ogni modifica alla configurazione di un agent, è utile eseguire tre controlli. Eseguili come utente dell'agent, non come il tuo utente.

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

Il primo non deve produrre alcun output. Il secondo deve restituire ls: cannot open directory '/home/you/': Permission denied. Il terzo mostra quale identità presenta il percorso di rete dell'agent. È proprio la domanda a cui risponde il pattern del gateway: 401 significa che l'agent non dispone di credenziali GitHub proprie, mentre 200 significa che ne dispone. Devi quindi 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 associare a questi limiti di accesso.

FAQ

Posso fidarmi del modello e basta, senza che 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 non appena un'istruzione iniettata risulta convincente. Il controllo deve quindi risiedere 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 sia l'agent a passarle 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 dello storage. Se gestisci autonomamente il vault, devi applicare un proprio processo di hardening, perché un server Vaultwarden viene solitamente violato tramite il token amministrativo o il file di backup, non tramite gli elementi cifrati che contiene. Il vault non risolve l'ultimo passaggio: qualcosa estrae il secret dal vault e lo passa all'agent come variabile d'ambiente, riportandoti al punto di partenza. Conta chi esegue la sostituzione. Se recupera il secret l'agent, allora il secret è nelle mani dell'agent. 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 solito non puoi determinarlo a posteriori. È proprio questo il motivo per usare un gateway. Senza gateway, le evidenze 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 utilizzo di una credenziale è registrato su una riga con l'identità dell'agent e un timestamp. Se sospetti una divulgazione, ruota prima la chiave e indaga dopo. La rotazione è economica; la certezza non lo è.

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 passo successivo, non il primo. Lo stesso punto di partenza vale per qualsiasi runtime di agent, incluso eseguire un agent autonomo in sicurezza su un VPS.