SSD Nodes Learn 🎉 VPS da $5.50/mese
Guide Matt ConnorDi Matt Connor

Prompt injection nei coding agent: rischi e difese

Scopri dove un attaccante può inserire testo in un coding agent sul server e quali difese riducono davvero l'impatto, dai file ai risultati degli strumenti.

Che cos'è la prompt injection contro un coding agent

La prompt injection contro un coding agent è semplice da definire: il testo letto dall'agente viene trattato come un'istruzione che l'agente deve seguire. L'agente apre un file, un commento in una pull request, una pagina web o il risultato di una chiamata a uno strumento. Tutto arriva sotto forma dello stesso tipo di testo della richiesta dell'utente. Se un attaccante controlla una parte di quel testo, sta scrivendo nella sessione dell'utente.

Tutti gli attuali prodotti basati su agenti hanno questa caratteristica. Il modello riceve un'unica sequenza di token. La richiesta dell'utente, il system prompt, il contenuto dei file e i risultati degli strumenti vengono concatenati, quindi il modello predice quale sarà il token successivo. I token non hanno un livello di privilegio. Il formato non indica quale parte è stata autorizzata dall'utente e quale proviene dal file README di uno sconosciuto.

Questa pagina definisce il modello delle minacce: descrive in quali punti il testo controllato da un attaccante raggiunge un agente in esecuzione su un server, che cosa può ottenere l'attaccante in ciascun punto e quali difese giustificano l'impegno necessario per implementarle. Le indicazioni sul contenimento riportate nelle altre guide hanno senso solo dopo aver capito da che cosa si sta isolando l'agente.

Perché il modello non riesce a separare i contenuti dalle istruzioni

L'addestramento è utile, ma non risolve il problema. I modelli attuali sono addestrati a trattare con cautela il testo recuperato e rifiutano molti tentativi rudimentali. Un rifiuto esprime una probabilità, non una regola. Un attaccante può riformulare il testo, riprovare e nasconderlo in un formato che nessuno aveva previsto. Nulla limita il numero di formulazioni che può tentare.

Il progetto OWASP GenAI monitora questo problema come LLM01:2025 Prompt Injection e lo divide in due categorie. La prompt injection diretta consiste nel prompt dell'utente che modifica il comportamento del modello. La prompt injection indiretta consiste in contenuti esterni, come un sito web o un file, che modificano il comportamento mentre il modello li elabora. Su un server è la prompt injection indiretta a essere rilevante, perché un agente legge una quantità di testo molto maggiore rispetto a quella digitata dall'utente.

Il primo studio sistematico è quello di Greshake e colleghi, Non è ciò a cui avete aderito (2023). La conclusione da ricordare è questa: quando un'applicazione inserisce testo recuperato in un modello che può chiamare strumenti, l'elaborazione di quel testo è simile all'esecuzione arbitraria di codice.

La condizione che trasforma una lettura in una violazione

Leggere testo ostile non causa di per sé alcun danno. Il danno richiede un percorso di uscita dalla macchina.

Simon Willison ha definito questa combinazione la tripletta letale nel giugno 2025. Un agente che dispone di dati privati, che è esposto a contenuti non attendibili e che può inviare dati all'esterno può essere indotto a trasferire i primi dati attraverso il terzo elemento.

Un agente di coding sul tuo VPS presenta tutte queste condizioni fin dal primo giorno. I dati privati sono il tuo codice sorgente, il tuo file `.env, le tue chiavi SSH e la cronologia della shell. I contenuti non attendibili sono tutti i repository, le pagine e i risultati degli strumenti che legge. Il percorso di uscita può essere git push, curl, npm publish`, il corpo di una pull request oppure un link visualizzato nel terminale su cui fai clic.

Non puoi eliminare la seconda condizione, perché leggere testo non attendibile è il compito per cui hai ingaggiato l'agente. Pertanto, ogni difesa pratica interviene sulle altre due.

Dove il testo non attendibile raggiunge un agente di coding su un server

Il repository su cui opera l'agente

Ogni file presente nel checkout è un input. Commenti nel codice sorgente, README.md, changelog, fixture di test, codice vendorizzato e gli stessi file di istruzioni dell'agente: CLAUDE.md, AGENTS.md e gli equivalenti. Un agente incaricato di comprendere una codebase li legge, perché è quello che gli è stato chiesto.

In questo modo un attaccante può raggiungere chiunque cloni il repository e indirizzi un agente verso di esso. I file di istruzioni sono il percorso più diretto, perché esistono per essere letti come istruzioni. Una pull request che aggiunge quattro righe utili a CLAUDE.md e una riga che reindirizza l'agente è una modifica che un revisore umano può facilmente ignorare.

Issue, pull request e commenti nella code review

Qualsiasi contenuto che uno sconosciuto può inserire nel tracker raggiunge l'agente non appena gli chiedi di eseguire il triage. A maggio 2025, Invariant Labs ha pubblicato un problema di sicurezza di GitHub MCP con esattamente questa dinamica. L'agente di uno sviluppatore aveva accesso a un repository pubblico e a repository privati. Un attaccante ha aperto una issue nel repository pubblico. Quando lo sviluppatore ha chiesto all'agente di esaminare le issue aperte, l'agente ha letto contenuti dei repository privati e li ha inseriti in una pull request nel repository pubblico.

Il report descrive un problema architetturale, non un difetto del codice del server MCP. L'agente disponeva di un unico token di accesso con autorizzazioni ampie, leggeva da una casella di posta pubblica e aveva il permesso di scrivere. Nulla era configurato in modo errato nel senso usuale. Per questo la soluzione consiste nel definire l'ambito delle autorizzazioni, non nell'applicare una patch.

Pagine Web recuperate dall'agente

Documentazione, risposte nei forum, una pagina di un fornitore, un risultato di ricerca. Ognuno di questi contenuti può contenere testo scritto per l'agente anziché per te. La conversione dell'HTML in testo offre all'attaccante spazio aggiuntivo, perché anche i contenuti che un browser non visualizza mai raggiungono il modello.

Un attaccante ottiene il controllo nel momento in cui l'agente è sottoposto al minor controllo. Nessuno legge per intero il testo di una pagina recuperata dall'agente durante una ricerca.

Output degli strumenti MCP

MCP (model context protocol) è il metodo comune con cui gli agenti si collegano agli strumenti esterni. I risultati tornano sotto forma di testo e vengono inseriti direttamente nella finestra di contesto. Le superfici di attacco sono due, non una. La prima è costituita dai dati restituiti dallo strumento. La seconda è costituita dal nome e dalla descrizione dello strumento, che il modello legge per decidere quando chiamarlo; inoltre, un server non sotto il tuo controllo può modificare entrambi tra una chiamata e l'altra.

Un attaccante che inserisce testo nell'output di uno strumento raggiunge ogni altro strumento disponibile all'agente. È così che un'iniezione in un componente di poco valore finisce per controllare un componente di valore elevato.

Log CI, output delle build e metadati delle dipendenze

npm install stampa testo proveniente da pacchetti che non hai scritto tu. Un test fallito stampa un messaggio di asserzione proveniente da una libreria. Il log di un job di continuous integration (CI) contiene migliaia di righe di output di terze parti. Se chiedi a un agente di correggere una build fallita, l'agente legge tutto questo contenuto.

In questo caso l'attaccante raggiunge la macchina di build, che di solito contiene credenziali di deploy e token dei registry e riceve meno attenzione di un laptop.

Cosa ottiene realmente un attaccante

È opportuno prepararsi a quattro possibili esiti.

Furto di credenziali. È incluso tutto ciò che il processo dell’agente può leggere: variabili d’ambiente, ~/.aws/credentials, ~/.ssh, un token gh, un file di configurazione Docker. Per inviarli all’esterno non serve curl. Un commit su un branch, la descrizione di una pull request, un pacchetto pubblicato in un registry o una richiesta DNS per un nome controllato dall’attaccante trasferiscono tutti dati fuori dal server.

Modifiche al codice che approvi. Scrivere codice è ciò che fa l’agente. Indurlo a scrivere una riga sottilmente errata è quindi l’esito più facile da ottenere. Può trattarsi di una dipendenza aggiuntiva o di una chiamata di logging che inserisce un token in un log inviato altrove.

Persistenza. Un file scritto una volta continua a funzionare senza coinvolgere il modello: un hook in .git/hooks, uno script postinstall in package.json, una riga aggiunta a un file di avvio della shell o una riga aggiuntiva in CLAUDE.md. Il comando successivo lo esegue.

Movimento all’interno della rete. L’agente viene eseguito nel punto in cui lo si installa. Se quel server può raggiungere un database su loopback, un servizio amministrativo interno, il servizio metadata del provider cloud o un altro host sulla rete privata, può farlo anche qualsiasi componente che controlli l’agente.

Le modalità di approvazione automatica eliminano l'ultimo controllo

Nella modalità predefinita, Claude Code chiede conferma prima di eseguire un comando o modificare un file. Questa richiesta rappresenta il controllo umano tra ciascuna delle superfici descritte sopra e un'azione effettiva. Le modalità che eliminano la richiesta rimuovono anche questo controllo.

La documentazione è chiara su bypassPermissions: usatelo solo in ambienti isolati, come container o VM, dove Claude Code non può causare danni. La modalità automatica è meno restrittiva e approva automaticamente le chiamate agli strumenti, eseguendo controlli di sicurezza in background per verificare che le azioni siano coerenti con la richiesta. Questi controlli intercettano molti problemi. Restano comunque valutazioni del modello sull'output del modello, quindi considerateli un filtro e non un limite di sicurezza.

Un amministratore può rimuovere entrambi i controlli. Impostate permissions.disableBypassPermissionsMode o permissions.disableAutoMode su "disable" in un file di configurazione e inserite il file nelle impostazioni gestite, in modo che un progetto sottoposto a checkout non possa sovrascriverlo. La nostra guida alla modalità automatica e alle regole dei permessi di Claude Code spiega dove ha effetto ciascuna regola.

Difese, in ordine di efficacia

Nessuna di queste misure risolve il problema. O limita ciò che l'agente può contenere, oppure limita ciò che può fare con quei dati.

  1. Una macchina che puoi distruggere e ricreare, così una compromissione ti costa un'ora invece di un incidente.
  2. Credenziali separate dalle tue, limitate a un solo repository e con durata breve.
  3. Nessun secret a lunga durata nell'ambiente ereditato dai comandi dell'agente.
  4. Controlli del sistema operativo sull'egress di rete e sull'accesso ai file, applicati a ogni processo avviato dall'agente.
  5. Richieste di approvazione mantenute attive per le scritture e le chiamate di rete.
  6. Hook come meccanismo di riserva deterministico per le azioni specifiche che puoi descrivere.
  7. Leggere il diff prima di fare il merge.

L'ordine è importante. Le misure da 1 a 4 restano efficaci anche quando il modello è completamente sotto il controllo di un attaccante. Le misure da 5 a 7 dipendono dall'attenzione di una persona, che è proprio ciò che tende a venire meno durante un'esecuzione prolungata dell'agente.

Esegui l'agente su una macchina sacrificabile

Un VPS che contiene un checkout e un token limitato è un obiettivo molto meno prezioso di un laptop che contiene le tue chiavi. Esegui l'agente con un utente non privilegiato dedicato, non con il tuo account di accesso e non come root. Le nostre guide su una VM sacrificabile per gli agenti di coding e sugli utenti con privilegi minimi su un VPS descrivono la configurazione; eseguire Claude Code in sicurezza su un VPS descrive l'uso quotidiano.

Rimuovi i secret dall'ambiente

Una variabile d'ambiente è leggibile da ogni processo figlio, quindi da ogni comando eseguito dall'agente. La sandbox di Claude Code può rimuovere variabili nominate prima di ogni comando eseguito nella sandbox. Su Linux servono prima due pacchetti:

sudo apt-get install bubblewrap socat

Poi, in ~/.claude/settings.json:

{
  "sandbox": {
    "enabled": true,
    "network": {
      "allowedDomains": ["github.com", "*.npmjs.org"]
    },
    "credentials": {
      "envVars": [
        { "name": "GITHUB_TOKEN", "mode": "deny" },
        { "name": "NPM_TOKEN", "mode": "deny" }
      ]
    }
  }
}

Una voce deny rimuove quella variabile prima dell'esecuzione di ogni comando nella sandbox, mentre allowedDomains contiene i comandi eseguiti nella sandbox verso gli host indicati. Il blocco credentials richiede Claude Code v2.1.187 o versione successiva, verificata ad agosto 2026. Esegui /sandbox in una sessione per vedere quali livelli sono attivi e quali dipendenze mancano. Stabilire quali secret debbano esistere su quella macchina è già una parte sostanziale del lavoro; tenere i secret fuori dalla portata di un agente AI affronta proprio questo aspetto.

Blocca l'egress di rete a livello del sistema operativo

Una regola firewall non considera ciò che ha deciso il modello. Esegui l'agente con un utente agent dedicato, quindi blocca il traffico inviato da quell'utente:

table inet agentcage {
  chain output {
    type filter hook output priority filter; policy accept;
    meta skuid "agent" ct state established,related accept
    meta skuid "agent" oif lo accept
    meta skuid "agent" counter drop
  }
}

In questo modo l'utente agent mantiene soltanto l'accesso al loopback. Il suo traffico deve quindi passare da un proxy eseguito sulla stessa macchina, che contiene la allowlist degli hostname. Con https_proxy impostato su quel proxy, il client invia una richiesta CONNECT e il proxy esegue la risoluzione del nome; all'agente non serve quindi un proprio DNS in uscita. Verifica il risultato con sudo nft list ruleset e controlla che il contatore della regola di blocco aumenti quando l'agente tenta di raggiungere una nuova destinazione.

Mantieni aperta una seconda sessione SSH mentre applichi modifiche al firewall. Verifica anche il comportamento del runtime dei container rispetto a queste regole: Docker crea catene proprie e le porte Docker pubblicate bypassano ufw descrive il problema che ne deriva.

Hook: il controllo che il modello non può aggirare con le parole

Le regole di autorizzazione e gli hook vengono applicati da Claude Code, non dal modello. La documentazione lo afferma chiaramente: le istruzioni nel prompt o CLAUDE.md influenzano ciò che Claude tenta di fare, ma non modificano ciò che Claude Code consente. Questa distinzione è il valore principale. Una riga in CLAUDE.md con il testo "non eseguire mai curl" è un suggerimento che un paragrafo iniettato può contestare. Un hook è un processo che restituisce un codice di uscita.

Registra un hook PreToolUse in .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/no-egress.sh"
          }
        ]
      }
    ]
  }
}

L'hook riceve la chiamata allo strumento come JSON sullo standard input. Il codice di uscita 2 blocca la chiamata e mostra a Claude il motivo ricevuto dallo standard error. Il codice di uscita 0 consente alla chiamata di proseguire nel normale flusso delle autorizzazioni.

#!/usr/bin/env bash
# PreToolUse: stdin holds the tool call, exit 2 blocks it.
cmd=$(jq -r '.tool_input.command // ""')
if printf '%s' "$cmd" | grep -qE '(^|[;&|]|\s)(curl|wget|nc|ncat)(\s|$)'; then
  echo "Blocked: this repository does not allow outbound network commands." >&2
  exit 2
fi
exit 0

Ora la parte importante. Si tratta di una denylist applicata a una stringa shell, e le denylist sulle stringhe shell presentano delle falle. python3 -c apre un socket senza usare la parola curl. Un target make deploy nasconde la stessa chiamata a un livello ulteriore. Scrivi hook per gli errori che sai descrivere e applica il confine su cui fai realmente affidamento nel kernel o nella rete.

Le regole di negazione delle autorizzazioni hanno un limite di corrispondenza che è importante conoscere. Le regole Read e Edit coprono gli strumenti file propri di Claude e i comandi sui file che Claude riconosce in Bash, come cat, head, tail e sed. Non coprono uno script Python o Node che apre direttamente il file. Le regole vengono valutate prima con deny, poi con ask e infine con allow; una regola deny non può quindi contenere un'eccezione basata su una allowlist.

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

Controlla ciò che esce e leggi il diff

Un'esecuzione dell'agente produce un diff e un insieme di chiamate di rete. Entrambi devono essere esaminati prima di eseguire il merge o il deployment. Un controllo di sicurezza self-hosted sul diff rileva una classe di modifiche diversa rispetto a una lettura rapida eseguita da una persona; sapere cosa invia un agente di coding fuori dalla macchina permette di conoscere il traffico normale, così una richiesta anomala risalta.

Cosa non è ancora risolto

Oggi non esiste una separazione affidabile tra contenuto e istruzioni. Ogni difesa disponibile è un filtro con un tasso di errore oppure un limite alle conseguenze. Nessun componente dello stack contrassegna una porzione di testo come dati che non devono mai essere eseguiti.

I filtri sono utili, ma possono fallire. Un classificatore che rileva la maggior parte dei tentativi di injection deve comunque essere corretto ogni volta, mentre all'attaccante basta riuscire una volta. Questa asimmetria spiega perché un tasso di successo pubblicato per una difesa rappresenti il punto di partenza per il tentativo successivo, non una garanzia.

Il lavoro più promettente riguarda il livello di progettazione, non quello del modello. CaMeL, descritto in Defeating Prompt Injections by Design (Debenedetti e colleghi, 2025), estrae prima il flusso di controllo e il flusso dei dati dalla richiesta attendibile, in modo che i dati non attendibili non possano modificare il comportamento del programma. In seguito applica controlli sulle capability quando vengono chiamati gli strumenti. Le cifre riportate dagli autori sul benchmark AgentDojo mostrano il costo di questo approccio.

ChartAgentDojo tasks solved, published figures from the CaMeL paper (2025)
The data behind this chart
[
  {
    "label": "Undefended agent",
    "tasks_solved_pct": 84
  },
  {
    "label": "CaMeL",
    "tasks_solved_pct": 77
  }
]

L'agente senza difese ha risolto il 84 percento delle attività. CaMeL ha risolto il 77 percento, con una garanzia di sicurezza associata. Sono le cifre pubblicate dagli autori per un singolo benchmark e non rappresentano una misurazione del vostro carico di lavoro. La differenza tra i due valori corrisponde approssimativamente al costo di una garanzia reale oggi.

Finché un progetto di questo tipo non sarà disponibile negli strumenti che usate ogni giorno, pianificate come se l'agente potesse essere compromesso prima o poi e fate in modo che l'evento abbia conseguenze minime. Questo è il motivo per usare una macchina usa e getta, credenziali con ambito limitato, egress controllato e l'abitudine di esaminare il diff.

FAQ

Posso bloccare la prompt injection dicendo all'agente di ignorare le istruzioni nei file?

No. Quella frase è testo nella stessa finestra di contesto dell'attacco e compete con il testo dell'attaccante a parità di condizioni. La documentazione di Claude Code traccia chiaramente il limite: le istruzioni nel prompt o CLAUDE.md influenzano ciò che l'agente tenta di fare, ma non modificano ciò che lo strumento può consentire. Considera un file di istruzioni come una dichiarazione di intenti e inserisci tutto ciò su cui fai affidamento nelle regole delle autorizzazioni, in un hook PreToolUse o in una regola del firewall.

La prompt injection è un rischio reale se l'agente accede soltanto al mio repository?

Sì, perché il repository contiene molto testo che non hai scritto tu. I file README delle dipendenze, gli URL nei lockfile, i test fixture, il codice vendorizzato e l'output di npm install vengono acquisiti durante una normale attività. Tutto ciò che proviene da un issue tracker o da un sito di documentazione arriva nello stesso modo. Il rischio aumenta con la quantità di contenuti che l'agente legge, e un agente utile legge molto.

Eseguire l'agente in un container risolve il problema?

Limita i danni, ma solo se rimuovi anche le credenziali. Un container con l'SSH agent inoltrato, le credenziali cloud nell'ambiente e l'accesso di rete senza restrizioni consegna a un attaccante quasi tutto ciò che avrebbe sull'host. Il vantaggio reale del container è un filesystem che puoi eliminare e un ambiente pulito in cui applicare regole per il traffico in uscita. Abbinalo a un token limitato a un solo repository.

Quale singola modifica riduce maggiormente il rischio?

Rimuovi dall'ambiente ereditato dai comandi dell'agente le credenziali a lunga durata, quindi assegna a quella macchina una policy predefinita di deny per il traffico in uscita. Insieme, queste due modifiche interrompono la terza condizione della tripletta letale: il testo può ancora dirottare l'agente, ma i dati che l'agente può raggiungere non possono essere inviati verso una destinazione utile. Anche le richieste di approvazione e la revisione dei diff sono utili, ma dipendono dal fatto che una persona resti vigile durante un'esecuzione lunga; per questo sono meno efficaci delle due modifiche precedenti.