Vai al contenuto

MCP

Audit di Claude Code e dei server MCP in ambienti self-hosted

Scritto da Olivares AI 7 min di lettura

Un team di piattaforma distribuisce Claude Code a una dozzina di ingegneri. Per renderlo utile, lo collegano a qualche server MCP: uno che legge il monorepo, uno che interroga una replica di reporting, uno che apre ticket. Nel giro di una settimana, un agente legge il codice sorgente, opera su un database e richiama strumenti interni — e nessuno sa rispondere alla domanda che, prima o poi, un revisore porrà: quale agente ha fatto cosa, su quale risorsa, e puoi dimostrare che non è stato alterato in seguito?

Non è una lacuna ipotetica. Ricerche indipendenti del settore (CSA/Token, n=418) hanno rilevato che circa l’82% delle organizzazioni ha agenti AI in esecuzione di cui non è a conoscenza, mentre solo circa il 21% ne mantiene un inventario in tempo reale. Claude Code e MCP sono esattamente il tipo di capacità che si adotta in fretta e supera la capacità di tracciamento per l’audit. La buona notizia: una traccia difendibile è ottenibile interamente all’interno del tuo perimetro, senza che alcun payload o segreto ne esca.

Perché la configurazione ovvia non supera un audit

La prima distribuzione tipica condivide una sola credenziale. Ogni istanza di Claude Code si autentica verso il livello MCP e verso i database a valle con lo stesso account di servizio — mcp-runner, ci-bot, qualcosa di generico. Funziona, e silenziosamente distrugge l’attribuzione.

Quando dieci agenti agiscono come un unico principal, il log di audit del database mostra dieci scritture da mcp-runner e nient’altro. Non puoi sapere quale sessione di quale ingegnere abbia emesso l’UPDATE, se provenisse da un prompt interattivo di Claude Code o da un job non presidiato, né quale strumento MCP fosse nel percorso. Ricerche indipendenti (Optro) collocano intorno al 28% la quota di organizzazioni in grado di risalire dall’azione di un agente a una persona. Un account di servizio condiviso è una delle ragioni strutturali per cui l’attribuzione crolla: il log di audit può mostrare cosa è successo, ma mai quale agente l’abbia fatto.

Il secondo punto debole è l’integrità. Anche i team che registrano per singola azione di solito scrivono su uno store mutabile. Se i log risiedono nello stesso sistema che un attaccante (o un agente con un bug) può raggiungere, «abbiamo i log» non equivale a «abbiamo le prove». Un revisore distingue nettamente i due casi.

L’identità per singolo agente è la base

Tutto ciò che sta a valle dipende dall’attribuzione, quindi sistema prima l’identità. Invece di un unico token condiviso, emetti un’identità distinta e di breve durata per ogni agente — idealmente per ogni sessione. L’identità viaggia con la richiesta dentro il server MCP e dentro qualsiasi risorsa che l’agente raggiunge, così il principal registrato sul database è l’agente, non un runner generico.

È questo che rende significativo il least-privilege. Un agente di reporting può essere vincolato alla sola lettura sulla replica di reporting; un agente di release ottiene la scrittura solo sugli artefatti di sua competenza. A questo punto una singola scrittura osservata al di fuori di quel perimetro è un segnale preciso e attribuibile, anziché rumore in un account condiviso. Espressa come policy, l’intenzione è netta:

agent "reporting-assistant" {
  # Claude Code session via the reporting MCP server
  resource "prod-postgres/reporting_replica" {
    access     = "read"      # SELECT only
    deny       = ["write", "ddl"]
  }
  resource "s3://billing-exports" {
    access = "read"
  }
  on_violation {
    action = "block_and_alert"   # refuse at access time, not just log
  }
}

Il punto non è la sintassi — è che la policy nomina l’agente, nomina la risorsa e separa la lettura dalla lettura/scrittura. Quella separazione è tutto il gioco.

Tratta le annotazioni MCP come segnali non attendibili

Gli strumenti MCP possono descriversi con annotazioni come readOnlyHint e destructiveHint. Sono davvero utili per il triage — uno strumento che si dichiara distruttivo merita una policy più stringente. Ma la specifica MCP è esplicita nel dire che si tratta di suggerimenti, e i client non devono basarvi le decisioni di sicurezza. Provengono dal server, che è proprio ciò di cui stai facendo l’audit. Uno strumento può dichiarare readOnlyHint: true ed emettere comunque una scrittura, sia per un bug, sia per una configurazione errata, sia per un’elusione deliberata.

Quindi la postura corretta è la conferma, non la fiducia. Considera l’annotazione come un’affermazione, poi verificala rispetto alla verità di base proveniente da un livello che lo strumento non controlla:

  • I log di audit del database (per esempio pgAudit di PostgreSQL) ti dicono se un SELECT o un UPDATE è stato effettivamente eseguito.
  • Gli span OpenTelemetry dal server MCP e dai servizi a valle mostrano il grafo delle chiamate e le operazioni eseguite.
  • I segnali eBPF a livello kernel sono la rete di sicurezza anti-elusione: una syscall di scrittura su un file o su un socket è osservabile a livello kernel a prescindere da ciò che lo strumento ha dichiarato in user space.

Quando l’annotazione dice sola lettura e il kernel ha visto una scrittura, quella contraddizione è la scoperta in prima pagina — uno strumento con readOnlyHint che ha scritto su prod-postgres è esattamente la deriva di least-privilege per cui vale la pena svegliare qualcuno. La differenza tra ciò che la policy ha permesso e ciò che i collettori hanno osservato è dove risiede il rischio reale.

Il registro che un revisore accetterà

Una traccia è una prova solo se è a prova di manomissione. Scrivi ogni evento in un registro append-only con catena di hash: ogni record include l’hash di quello precedente, così qualsiasi modifica o cancellazione a valle rompe la catena ed è rilevabile. Ogni riga attribuisce l’azione allo specifico agente, nomina la risorsa, registra la lettura rispetto alla lettura/scrittura e porta con sé un livello di confidenza — attributed quando identità ed esito sono entrambi confermati, approximate quando qualcosa è stato dedotto. La confidenza è mostrata in modo onesto; una corrispondenza approssimativa non viene mai spacciata per una dimostrata.

ts=2026-06-08T09:14:02Z agent=reporting-assistant@s3f1 tool=sql-read     resource=prod-postgres/reporting_replica  op=R   outcome=allow   conf=attributed   prev=8a1c…
ts=2026-06-08T09:14:05Z agent=reporting-assistant@s3f1 tool=export-writer resource=s3://billing-exports          op=R   outcome=allow   conf=attributed   prev=2f90…
ts=2026-06-08T09:17:48Z agent=data-export-job@b22e     tool=sql-write     resource=prod-postgres/customers          op=RW  outcome=DENY    conf=attributed   prev=c7d3…  policy=read_only_violation

Quella terza riga è quella che interessa a un revisore: un tentativo di scrittura su una tabella per cui l’agente non aveva alcuna concessione di scrittura, negato al momento dell’accesso, attribuito a un agente con nome e ancorato nella catena. Poiché la policy viene applicata quando avviene l’accesso — non solo registrata dopo — il diniego è un controllo, non un’analisi a posteriori.

Altre due proprietà rendono la traccia solida. Le viste privilegiate sono a loro volta sottoposte ad audit: consultare la mappa degli accessi viene registrato, così «chi ha visto cosa» è una domanda con risposta. E l’esportazione delle prove è a prova di manomissione — consegni a un revisore una porzione firmata della catena che può verificare in modo indipendente, anziché un CSV da accettare sulla fiducia.

ProprietàAccount di servizio condivisoIdentità per singolo agente + registro con catena di hash
AttribuzioneUn unico principal per tutti gli agentiAzione legata allo specifico agente/sessione
Lettura vs scritturaConfuseDistinte e verificate dalla policy
IntegritàLog mutabiliAppend-only, la catena si rompe alla modifica
Export pronto per l’auditCSV accettato sulla fiduciaPorzione firmata e verificabile in modo indipendente

Perché il self-hosted conta qui

Tutto questo funziona senza che nulla esca dalla tua rete. Il collettore osserva — log, OpenTelemetry, eBPF come rete di sicurezza a livello kernel — anziché collocarsi nel percorso dati dell’agente, così se va in errore non rompe mai l’agente né la richiesta di produzione che vi sta dietro. Il registro memorizza le relazioni di accesso, non i payload: registra che reporting-assistant ha letto reporting_replica, non le righe restituite. Gli input che potrebbero contenere segreti o PII vengono oscurati e sottoposti a scansione dei segreti prima che qualsiasi cosa venga scritta. Per gli ambienti air-gapped, vincolati dal GDPR o soggetti a vincoli di residenza dei dati, questa è la differenza tra una capacità di audit che puoi difendere e una che non puoi: nulla del tuo utilizzo di Claude Code e MCP «telefona a casa», perché il sistema non vede i dati in primo luogo.

Claude Code e MCP meritano di essere distribuiti. Hanno solo bisogno di una traccia di audit costruita come la costruiresti per qualsiasi altra automazione privilegiata — l’identità prima di tutto, gli esiti confermati, le prove ancorate. Se vuoi vedere come la mappa degli accessi e il registro a prova di manomissione si combinano, il modello di sicurezza e la panoramica del prodotto approfondiscono ciascun aspetto.

Domande frequenti

Posso fidarmi del readOnlyHint di uno strumento MCP per dimostrare che un'azione è stata di sola lettura?

No. La specifica MCP afferma che le annotazioni degli strumenti, come readOnlyHint e destructiveHint, sono semplici suggerimenti e vanno trattate come non attendibili, perché provengono dal server e non da un livello di enforcement. Usale per il triage e per segnalare le contraddizioni, ma dimostra l'effettivo esito di lettura/scrittura con la telemetria o con segnali a livello kernel (span OpenTelemetry, log di audit del database, eBPF). Fidati dell'esito confermato, non dell'annotazione.

Come attribuisco un'azione di Claude Code a un agente specifico anziché a un account di servizio condiviso?

Emetti un'identità distinta e di breve durata per ogni agente o per ogni sessione, invece di un unico token condiviso. Quando l'agente raggiunge un database o un server MCP, quell'identità viaggia con la richiesta, così il registro di audit registra quale agente ha fatto cosa. Un account di servizio condiviso accorpa tutti gli agenti in un unico principal e rende impossibile, a posteriori, applicare il least-privilege per singolo agente e ottenere un'attribuzione a prova di manomissione.

Scopri cosa possono raggiungere i tuoi agenti

Olivares AI è la piattaforma aperta e in self-hosting per il tuo parco AI. Distribuiscila sulla tua infrastruttura e ottieni la mappa degli accessi che i tuoi team security e platform richiedono.