ISMS Copilot Docs

SSE Redaction: Difesa dei System Prompt degli LLM nelle Architetture di Streaming

Di Better ISMS — Febbraio 2026

Di Better ISMS — Febbraio 2026

Se stai costruendo un prodotto basato su un LLM, il tuo system prompt è la logica del tuo prodotto. Quando qualcuno lo estrae, ottiene il tuo ragionamento, i tuoi guardrail, il tuo vantaggio competitivo — tutto. E se stai trasmettendo le risposte tramite Server-Sent Events (cosa che probabilmente stai facendo), difenderti dall'estrazione è più difficile di quanto pensi.

Questo post descrive la SSE redaction, una tecnica che abbiamo sviluppato per ISMS Copilot per rilevare e neutralizzare le fughe di system prompt durante lo streaming. Condividiamo l'architettura in modo che altri che costruiscono prodotti LLM possano implementare qualcosa di simile.

Il Problema

La maggior parte delle applicazioni LLM trasmette le risposte al client chunk per chunk utilizzando SSE. Ogni chunk viene inviato nel momento in cui viene generato. Non c'è uno step di "revisione della risposta completa prima dell'invio" — questo vanificherebbe lo scopo dello streaming.

Questo crea una falla di sicurezza: se un prompt di jailbreak convince il modello a rivelare le sue istruzioni di sistema, il contenuto è già in viaggio verso il client prima che tu possa fermarlo. Nel momento in cui ti rendi conto di cosa è successo, l'utente ha già visto centinaia o migliaia di caratteri del tuo system prompt.

Il filtraggio tradizionale dell'output non funziona qui. Non puoi bufferizzare l'intera risposta (la latenza uccide l'UX), e non puoi controllare ogni piccolo chunk in isolamento (un frammento di 5 parole non sembra un system prompt).

Per strategie generali di prevenzione dei jailbreak, consulta Mitigare Jailbreak e Prompt Injection. La SSE redaction è una misura di difesa in profondità per quando quelle prevenzioni falliscono.

L'Architettura

La SSE redaction funziona in quattro fasi.

Fase 1 — Fingerprinting. Prima che qualsiasi conversazione abbia luogo, estrai un insieme di frasi fingerprint dal tuo system prompt. Queste sono stringhe distintive che apparirebbero insieme solo se il modello stesse riproducendo le sue istruzioni. Vuoi frasi distribuite in diverse sezioni del prompt — definizioni di ruolo, nomi di vincoli, regole comportamentali. Il numero di fingerprint e la soglia di matching sono parametri regolabili che mantieni segreti.

Fase 2 — Accumulo e controllo periodico. Mentre il modello trasmette i chunk, un guard accumula il testo completo della risposta. A intervalli regolari (misurati per conteggio dei caratteri, non per chunk), verifica il contenuto accumulato rispetto all'insieme di fingerprint. Controllare ogni chunk sarebbe uno spreco — i fingerprint necessitano di abbastanza contesto circostante per un matching significativo.

Fase 3 — Propagazione dell'errore. Quando il guard rileva un numero sufficiente di corrispondenze di fingerprint, lancia un errore tipizzato (nel nostro caso, SystemPromptLeakError). Qui risiede la sottigliezza. In un'architettura di streaming, il ciclo di elaborazione dei chunk tipicamente ha un blocco try/catch per gestire dati SSE malformati (JSON non valido, formati inattesi). Quel blocco catch generico inghiottirà il tuo errore di sicurezza se non stai attento. Hai bisogno di una clausola di guardia che rilanci il tuo specifico tipo di errore prima che venga eseguito il gestore generico:

catch (e) {
  if (e instanceof Error && e.name === 'SystemPromptLeakError') throw e;
  // la gestione degli errori generici continua per tutto il resto
}

Questo è un one-liner, ma senza di esso, l'intero sistema di rilevamento è inerte. Il guard rileva le fughe, le registra nei log, e lo stream continua felicemente a consegnare il tuo system prompt all'attaccante. Lo abbiamo imparato a nostre spese — il nostro guard rilevava perfettamente le fughe nei log mentre non faceva assolutamente nulla per fermarle.

Fase 4 — Redaction. Una volta che l'errore si propaga fino al controller dello stream, questo invia un evento SSE di redaction al client. Il client sostituisce tutto ciò che era già stato renderizzato con un messaggio di rifiuto. Il server sostituisce simultaneamente il contenuto memorizzato nel database in modo che la fuga non persista.

Cosa Vede l'Utente

L'attaccante vede brevemente contenuti parziali trasmessi in streaming — forse qualche secondo di contenuto — poi l'intera risposta viene sostituita con un rifiuto generico. L'esperienza è: appare del testo, poi scompare e viene sostituito. Il contenuto parziale che hanno intravisto è incompleto e mescolato con il testo della risposta normale, rendendolo inaffidabile per l'estrazione.

Scopri di più su come funzionano i messaggi di rifiuto in Gestire Rifiuti e Limiti di Ambito.

Il Problema del Blocco Catch

Questo merita enfasi perché è il tipo di bug che passa tutti i test ma fallisce in produzione.

Se stai utilizzando async generator per lo streaming, il tuo ciclo di parsing SSE probabilmente assomiglia a questo:

for (const line of sseLines) {
  try {
    const data = JSON.parse(line);
    const text = extractText(data);
    await onChunkCallback(text);  // <-- il guard viene eseguito qui
    yield text;
  } catch (e) {
    console.error('Error parsing chunk:', e);
    // continua alla riga successiva
  }
}

Il callback è all'interno del blocco try. Se il guard lancia un errore, il catch lo registra come errore di parsing e passa oltre. Nel nostro caso, il guard rilevava correttamente la fuga su ogni singolo chunk dopo la soglia — i log mostravano SystemPromptLeakError che si attivava ripetutamente — mentre lo stream si completava normalmente, salvava l'intero prompt trapelato nel database e lo inviava al client.

La complicazione aggiuntiva: questo comportamento dipende dal runtime. In Node.js, gli errori degli async generator provenienti dai callback possono propagarsi in modo diverso rispetto a Deno. I nostri test passavano nell'ambiente di test di Node.js perché l'errore si propagava per caso. In produzione su Deno, veniva inghiottito. Se stai costruendo questo, testa nel tuo runtime di produzione effettivo, non solo nel tuo test runner.

Decisioni di Design da Notare

Perché i fingerprint invece della similarità di embedding o del matching esatto? I fingerprint sono veloci (string matching), deterministici (nessuna chiamata al modello) e robusti contro la parafrasi. Il modello raramente parafrasa il proprio system prompt durante una fuga — lo riproduce alla lettera o quasi. La similarità di embedding aggiunge latenza per ogni controllo e introduce il rischio di falsi positivi su contenuti di compliance legittimi. Il matching esatto di sottostringhe è troppo fragile (differenze di spazi bianchi, formattazione).

Perché controllare periodicamente invece di ogni chunk? I chunk sono piccoli (spesso 3–10 caratteri). Un singolo chunk è insignificante per il rilevamento. Accumulare fino a una soglia minima prima di controllare riduce il calcolo e garantisce abbastanza contesto per un matching affidabile.

Perché non bufferizzare l'intera risposta? La bufferizzazione uccide l'UX dello streaming. Gli utenti si aspettano di vedere il testo apparire in tempo reale. Un buffer di 2 secondi è percepibile; bufferizzare una risposta completa di 4000+ caratteri è inaccettabile. La SSE redaction preserva lo streaming in tempo reale per il 99,99% delle conversazioni e interviene solo durante una fuga attiva.

Perché sostituire anche nel database? Se redigi solo sul client, il contenuto trapelato persiste lato server. Chiunque abbia accesso al database, qualsiasi funzionalità di esportazione, qualsiasi endpoint di cronologia delle conversazioni lo esporrebbe.

Cosa Questo Non Risolve

La SSE redaction è una misura di difesa in profondità, non una soluzione miracolosa.

Non impedisce al modello di tentare di trapelare. Questo è ciò che gestiscono le istruzioni del tuo system prompt (istruzioni esplicite di rifiuto, sezioni di vincolo). La SSE redaction è la rete di sicurezza per quando quelle istruzioni falliscono — e con abbastanza creatività, i jailbreak occasionalmente riescono.

Non impedisce fughe più brevi della soglia di rilevamento. Se qualcuno convince il modello a rivelare una singola frase del system prompt, il conteggio dei fingerprint non raggiungerà la soglia. Questo è per design — stai facendo un trade-off tra il rilevare estrazioni complete (alta confidenza) e segnalare menzioni parziali (alto rischio di falsi positivi).

L'attaccante vede comunque contenuti parziali prima della redaction. Per alcuni secondi, il testo trasmesso è visibile. Questo è intrinseco alle architetture di streaming. Il contenuto parziale è incompleto e privo di struttura, ma non è un'esposizione nulla.

La SSE redaction integra ma non sostituisce le best practice di sicurezza dei system prompt. Consulta System Prompts e Proteggere Workspace e Istruzioni Personalizzate per misure di sicurezza fondamentali.

Checklist di Implementazione

Se vuoi costruire questo per il tuo prodotto LLM:

  1. Estrai frasi fingerprint dal tuo system prompt — scegli stringhe distintive che coprono diverse sezioni.
  2. Costruisci un guard che accumula il contenuto trasmesso in streaming e lo controlla periodicamente rispetto ai fingerprint.
  3. Definisci una classe di errore tipizzata con un nome distintivo per il rilevamento delle fughe.
  4. Verifica ogni blocco catch nella tua pipeline di streaming — aggiungi guardie di rilancio per il tuo tipo di errore.
  5. Nel tuo stream controller, gestisci l'errore inviando un evento di redaction e sostituendo il contenuto memorizzato.
  6. Sul client, gestisci l'evento di redaction sostituendo il contenuto renderizzato con un messaggio di rifiuto.
  7. Testa nel tuo runtime di produzione, non solo nel tuo test runner.
  8. Mantieni segreti i tuoi fingerprint, le soglie e gli intervalli di controllo.

Pensiero Finale

La parte più difficile di tutto questo non è stato l'algoritmo di rilevamento — è stato un bug di una riga in un blocco catch che disabilitava silenziosamente l'intero sistema. La sicurezza nelle architetture di streaming fallisce a livello di plumbing, non a livello di algoritmo. Se stai costruendo funzionalità di sicurezza per LLM, traccia l'intero percorso dell'errore dalla rilevazione all'azione visibile all'utente, e verificalo nel tuo ambiente di produzione effettivo.

Per una visione più ampia delle pratiche di sicurezza dell'IA in ISMS Copilot, consulta Panoramica su Sicurezza dell'IA e Uso Responsabile.

Better ISMS costruisce strumenti di conformità per i team di sicurezza delle informazioni. ISMS Copilot è il nostro assistente AI per ISO 27001, SOC 2, GDPR e framework correlati.

On this page