> ## Content Index
> Fetch the complete content index at: https://adrianoamalfi.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# I server MCP hanno un problema di sicurezza che arriva dal 1978
- URL: https://adrianoamalfi.com/sicurezza-mcp-ansi-escape/
- Published: 2026-07-24T06:18:47.000Z
- Updated: 2026-07-26T17:13:28.000Z
- Description: Le sequenze ANSI escape nascondono comandi agli umani lasciandoli leggibili dagli LLM. BrightSec ha appena pubblicato la prima ricerca sistematica: il problema è strutturale.
- Author: Adriano Amalfi
- Tags: AI Agentica, MCP, Sicurezza AI, Prompt Injection, ANSI Escape, LLM

Immagina di essere un revisore umano che controlla l'**output di un agente AI**. Vedi una pagina pulita, ben formattata, nessuna anomalia. **Approvi**. Quello che non vedi sono i ***byte di controllo*** nascosti nel testo: sequenze che il tuo terminale interpreta come **comandi di formattazione** e che quindi non renderizza mai sullo schermo.

Il **modello li vede** eccome. Ogni singolo byte.

Questo è il cuore dell'**ANSI Escape Sequence Injection** (AESI), una classe di attacchi che [Bright Security ha appena documentato](https://brightsec.com/research/detecting-ansi-escape-sequence-injection-in-mcp-servers-with-dast/?ref=adrianoamalfi.com) in modo sistematico per i server MCP.

## Il meccanismo: cosa succede davvero

Le sequenze ANSI escape **nascono negli *anni '70*** con lo standard [ANSI X3.64](https://en.wikipedia.org/wiki/ANSI%5Fescape%5Fcode?ref=adrianoamalfi.com). Erano la soluzione a un **problema concreto**: ogni produttore di terminali parlava il proprio **dialetto di controllo**, e serviva uno **standard unificato** per muovere il cursore, cambiare colore, cancellare lo schermo. Funzionavano così: un **byte di escape** (`ESC`, hex `0x1B`) seguito da un **codice di controllo** che il terminale **interpreta come comando**, non come testo da visualizzare.

L'umano **vede il risultato** del comando: il colore che cambia, il testo che scompare. Il modello linguistico, quando legge quello stesso contenuto, **vede il flusso di byte** grezzo. La sequenza di controllo e le istruzioni nascoste dopo di essa sono perfettamente intatte nel suo contesto.

> Stiamo usando un formato pensato per uno schermo come canale di comunicazione con un sistema che processa testo grezzo.

## Due varianti, due superfici d'attacco

BrightSec descrive due scenari, distinti per vettore ma identici nel principio.

**Direct-fetch AESI*.* Un server MCP espone un **tool che recupera contenuti** da un URL: il classico "leggi questa pagina e riassumila". L'attaccante **ospita a un URL** sotto il suo controllo del testo contenente **sequenze *ANSI escape*** e istruzioni nascoste. Quando **l'agente chiama il tool** con quell'URL, il server recupera il contenuto e lo **riversa in un campo** consumabile dal modello: uno dei campi JSON che l'**agente effettivamente processa**.

Ecco il punto: non tutto ciò che un server **MCP restituisce** arriva al modello. ***BrightSec*** identifica **tre percorsi** precisi:

- `result.content[].text` nei **risultati** dei tool
- `result.contents[].text` nelle **risorse**
- `result.messages[].content.text` nei ***prompt*** template

Solo questi contano. Un **payload** che finisce altrove **non è sfruttabile**. Questa distinzione è ciò che rende la **rilevazione** mirata possibile e i **falsi positivi** evitabili.

> La differenza tra direct-fetch e stored è la stessa che passa tra un messaggio ingannevole e una bomba a orologeria sepolta in un archivio.

***Stored AESI.*** Qui l'attacco è più subdolo. Invece di un ***fetch* diretto**, l'attaccante scrive il **payload in un database**: un commento, una nota, un record qualsiasi, attraverso un normale ***endpoint HTTP***. Nulla appare sospetto al momento della scrittura. Il ***payload*** resta lì, dormiente, finché un **agente** diverso, in una sessione diversa, **legge quel record** attraverso un tool o una **risorsa MCP.**

Questa variante è **più pericolosa** del direct-fetch per tre ragioni:

- **Persistenza**: il *payload* **sopravvive tra sessioni** e utenti
- ***Disaccoppiamento***: la scrittura (***HTTP***) e la lettura (***MCP***) usano **protocolli e percorsi diversi**: non c'è modo di dedurre il flusso dall'analisi del codice
- ***Esposizione ritardata*:** il momento della scrittura è pulito; **la detonazione avviene dopo**, quando un agente legge il dato in un contesto completamente diverso

Un singolo **record avvelenato** in una *knowledge base* condivisa può **influenzare ogni *workflow*** agente che lo **incrocia in futuro**.

## Non è un incidente isolato

Qui sta il problema per chi **costruisce infrastruttura** per agenti. **AESI** non è un bug nuovo. È un ***pattern*** che si ripete **con regolarità** imbarazzante.

| Anno | Prodotto     | CVE                                                                                                        | CWE               | Impatto                                            |
| ---- | ------------ | ---------------------------------------------------------------------------------------------------------- | ----------------- | -------------------------------------------------- |
| 2021 | kubectl      | [CVE-2021-25743](https://nvd.nist.gov/vuln/detail/CVE-2021-25743?ref=adrianoamalfi.com)                    | CWE-150           | Escape sequence in output non neutralizzate        |
| 2024 | Git sideband | [CVE-2024-52005](https://github.com/git/git/security/advisories/GHSA-7jjc-gg6m-3329?ref=adrianoamalfi.com) | CWE-150 + CWE-116 | Payload sideband passato non filtrato al terminale |
| 2026 | Server MCP   | N/A                                                                                                        | CWE-150           | Stessa classe, nuovo consumatore: un LLM           |

Il copione è sempre lo stesso: un sistema nuovo **eredita un protocollo** pensato per umani, ignora i ***byte* di controllo**, e apre una **superficie d'attacco**. Nel caso di ***kubectl***, la gravità era bassa: l'attaccante poteva solo **sporcare l'output** del terminale. Nel caso di **Git**, la gravità è schizzata a ***HIGH 7.5*** perché il payload poteva nascondere **comandi malevoli** all'utente. Con MCP, il danno è **potenzialmente peggiore**: il consumatore finale **non è più un umano** che potrebbe insospettirsi, **ma un modello** che esegue istruzioni senza capacità di distinguere tra **contenuto legittimo** e **comandi iniettati**.

💡

Ogni tecnologia che eredita protocolli terminale senza neutralizzarne i rischi riproduce esattamente la stessa vulnerabilità. MCP è solo l'ultimo anello di una catena che include kubectl e Git: stesso ****CWE-150, stesso meccanismo, vittima diversa**.

## Perché MCP è un bersaglio naturale

***MCP*** non è un **protocollo di nicchia**. Il repository ufficiale dei server ha *88.8k stelle* su GitHub, il [MCP Registry](https://registry.modelcontextprotocol.io/?ref=adrianoamalfi.com) è attivo, e centinaia di server vengono creati ogni settimana. Il ***README*** del repository [avverte esplicitamente](https://github.com/modelcontextprotocol/servers?ref=adrianoamalfi.com) che i server di riferimento ***"non sono pronti per la produzione"*** e che ***"gli sviluppatori devono valutare i propri requisiti di sicurezza"***. Ma queste avvertenze, nella pratica, **vengono ignorate** sistematicamente.

Il problema di fondo è **architetturale**. Il protocollo MCP non ha un **modello di minaccia** formalizzato, e la **specifica** attuale (versione 2025-11-25) **non affronta** queste problematiche.

Un **umano** ha **due livelli**: il **testo** visibile (dati) e le **sequenze** di escape (controllo). Il **modello** ha **un solo livello**: tutto ciò che arriva nel **contesto** è testo, e tutto il **testo** è potenzialmente **un'istruzione**. Non c'è un parser che distingue, non c'è un renderer che nasconde. Qualsiasi byte entra, viene processato.

> Negli LLM non esiste separazione tra canale di controllo e canale dati.

## Cosa puoi fare oggi

Per fortuna le **difese esistono** e sono **implementabili** subito, senza aspettare aggiornamenti del protocollo.

### Sanitizza i byte di controllo al confine

Prima che qualsiasi contenuto esterno o archiviato venga inserito in un campo consumabile dal modello, **rimuovi le sequenze ANSI** escape. Una regex su `\x1B[...` non basta: devi coprire l'intero spettro dei codici di controllo: concealment (`\x1B[8m`), clear-screen (`\x1B[2J`), cursor-move, line-erase. BrightSec dimostra che server diversi sopravvivono a payload diversi: la varietà dei payload di test è essenziale.

### Non fidarti di niente che arriva da fuori

Questo include URL recuperati, record di database scritti da utenti, risposte API di terze parti. Il problema della variante stored è che il flusso dalla scrittura alla lettura è opaco: non puoi dedurlo dai nomi dei parametri o dalle descrizioni dei tool. L'unico modo per sapere dove i dati riemergono è **testarlo empiricamente**.

### Integra DAST nella CI

La rilevazione automatizzata descritta da BrightSec usa tre segnali per confermare una vulnerabilità:

- un ***byte ANSI escape*** sopravvissuto
- una **frase di istruzione** intatta
- un **marker univoco** del payload presente nello stesso campo consumabile dal modello

Questa tripletta elimina i falsi positivi ed è **automatizzabile in pipeline.**

📖

Ogni volta che il tuo server MCP prende testo da un'origine esterna e lo mette in \`result.content\[\].text\`, chiediti: "Cosa succede se questo testo contiene \`ESC \[8m\` seguito da un'istruzione?" Se la risposta è "il modello la leggerebbe", hai un problema.

La **sicurezza negli agenti AI** non può limitarsi all'ispezione delle richieste. Deve **modellare il *flusso dei dati***: da dove **arrivano**, dove vengono **archiviati**, dove **riemergono** nel contesto del modello.

***ANSI escape*** è solo il primo esempio. L'intero arsenale degli **attacchi testuali** (caratteri Unicode bidirezionali, caratteri a larghezza zero, attacchi omoglifici) sfrutta lo stesso disallineamento: ciò che un **umano vede** renderizzato non corrisponde a ciò che il **modello legge** come input grezzo.

Il byte `0x1B` che nel 1978 serviva a controllare la formattazione del testo su un VT100, nel 2026 è diventato un vettore di *prompt injection*.

> Non perché sia cambiato il byte, ma perché è cambiato chi lo legge.

## Further Reading

[Detecting ANSI Escape Sequence Injection in MCP Servers with DAST | Bright SecurityANSI escape sequences were designed for terminals, not for language models. They are invisible control codes that tell a terminal to change colors, hide text,![](https://adrianoamalfi.com/content/images/icon/favicon-2-150x150.png-98ea5210-6d3c-45fc-97ec-50419c19d01a.webp)Bright Security![](https://brightsec.com/research/detecting-ansi-escape-sequence-injection-in-mcp-servers-with-dast/images/Homepage-1-1024x576.jpg)](https://brightsec.com/research/detecting-ansi-escape-sequence-injection-in-mcp-servers-with-dast/?ref=adrianoamalfi.com)

[NVD - CVE-2021-25743![](https://adrianoamalfi.com/content/images/icon/favicon-32x32-f3774f88-e5cc-47be-a5b9-f41ca1761d33.png)National Institute of Standards and Technology logo![](https://adrianoamalfi.com/content/images/thumbnail/us_flag_small-b7d95481-a974-4fba-b2f9-5551c2067e98.png)](https://nvd.nist.gov/vuln/detail/CVE-2021-25743?ref=adrianoamalfi.com)

[The sideband payload is passed unfiltered to the terminal\### Impact When cloning from a server (or fetching, or pushing), informational or error messages are transported from the remote Git process to the client via the so-called “sideband channel”. T…![](https://adrianoamalfi.com/content/images/icon/favicon-c89b33cd-e88c-44aa-9234-334394b57835.svg)GitHubgit![](https://adrianoamalfi.com/content/images/thumbnail/GHSA-7jjc-gg6m-3329-f2e14262-6120-41ce-aa06-11c43c5e95a0)](https://github.com/git/git/security/advisories/GHSA-7jjc-gg6m-3329?ref=adrianoamalfi.com)

[GitHub - modelcontextprotocol/servers: Model Context Protocol ServersModel Context Protocol Servers. Contribute to modelcontextprotocol/servers development by creating an account on GitHub.![](https://adrianoamalfi.com/content/images/icon/favicon-42f6e16d-a016-488b-92e9-6ad644b14a5c.svg)GitHubmodelcontextprotocol![](https://adrianoamalfi.com/content/images/thumbnail/servers-0d17ba45-1fbc-46fb-8bab-fd2bb5f63ffc)](https://github.com/modelcontextprotocol/servers?ref=adrianoamalfi.com)

[Structural defense for tool rug-pulls (CVE-2025-54136): tools as identities with hash-pinned manifests + knowledge recall · modelcontextprotocol · Discussion #813Pre-submission Checklist This post follows the MCP community guidelines What would you like to share? Scanners detect a swapped tool description; the gap between detection and action is where incid…![](https://adrianoamalfi.com/content/images/icon/favicon-264c64bc-77b6-4725-abd2-ecc0b1fa30b0.svg)GitHubiotlodge![](https://adrianoamalfi.com/content/images/thumbnail/813-e274fa79-0478-4405-bf8d-be047ecd56d8)](https://github.com/orgs/modelcontextprotocol/discussions/813?ref=adrianoamalfi.com)