[ kalizi.dev ]

Ho attaccato un ModernBERT con un algoritmo genetico. Ha funzionato.

Ogni hacker, a un certo livello, è qualcuno che ha deciso di non fidarsi della parola "sicuro".

Kevin Mitnick è entrato nei sistemi dell'FBI mentre lo stavano cercando, e ha lasciato un pacco sul posto con scritto "FBI Donuts." Frank Abagnale ha ingannato il sistema di verifica identità di un'intera compagnia aerea a 16 anni. Ogni sistema ha punti ciechi. Ed è lì che vivono gli avversari.

Per la mia tesi magistrale all'Università di Palermo, ho deciso di fare l'avversario. Non contro un istituto finanziario o una rete governativa. Contro un modello NLP allo stato dell'arte: un ModernBERT fine-tuned sul dataset di recensioni IMDB.

Il risultato: 100% di successo nell'attacco. Una media di 18 query al modello. Zero degradazione semantica.

Ecco come funziona.

La crepa nel NLP "robusto"

I modelli linguistici moderni sono davvero impressionanti. ModernBERT raggiunge il 93,9% di accuratezza nella classificazione del sentiment su IMDB dopo un fine-tuning di 3 epoche. Sembra robusto. Non lo è.

C'è una vulnerabilità strutturale che la ricerca sull'adversarial ML sfrutta da anni: i modelli imparano pattern dai dati di training, non il significato.

Un'illustrazione semplice. Prendi un testo di 10 parole dove ogni parola ha almeno 2 sinonimi. I sinonimi sono semanticamente equivalenti. Puoi sostituirli liberamente senza cambiare il significato della frase. Ottieni 3^10 - 1 = 59.048 versioni semanticamente equivalenti del testo originale. Il modello è stato addestrato su un corpus enorme. Quasi certamente non su tutte le 59.048 varianti.

Alcune parole sono abbastanza frequenti da essere ben rappresentate. I loro sinonimi potrebbero non esserlo. Una parola e il suo sinonimo possono finire in regioni completamente diverse della rappresentazione interna del modello. Anche se significano la stessa cosa.

Questo è il problema centrale: l'equivalenza semantica non garantisce l'equivalenza del classificatore. La domanda è come trovare la sostituzione giusta in modo efficiente, automatico, e senza trasformare il testo in qualcosa che nessuno scriverebbe mai.

L'evoluzione come strategia d'attacco

È qui che entrano in gioco gli algoritmi genetici.

Gli algoritmi genetici sono metodi di ricerca ispirati all'evoluzione biologica. Si mantiene una popolazione di soluzioni candidate (chiamate cromosomi), ognuna valutata rispetto a una fitness function, e si itera attraverso due operazioni:

  • Mutazione: piccole modifiche casuali a un candidato
  • Crossover: combinazione delle caratteristiche di due candidati genitori

Nel corso delle generazioni, la popolazione converge verso soluzioni migliori. In questo caso, testi che preservano il significato ma invertono la classificazione del modello da positivo a negativo (o viceversa).

Il nucleo della mia implementazione opera sui bigrammi: coppie di parole adiacenti, non token individuali. È una scelta deliberata. I bigrammi esplorano lo spazio di ricerca più velocemente delle sostituzioni a singolo token e riflettono meglio come cambia realmente il linguaggio: il contesto muta per blocchi, non in parole isolate.

Il vincolo difficile: l'onestà semantica

Qualsiasi esempio avversario che cambia il significato del testo originale non sta dimostrando una vulnerabilità del modello. Sta solo generando rumore. L'attacco ha senso solo se la perturbazione è genuinamente semanticamente equivalente.

Vocabolario bloccato. Non tutto è eleggibile per la sostituzione. Nomi propri, numeri, punteggiatura e token non riconosciuti sono bloccati. L'attacco opera solo sulle parole di contenuto che possono essere sostituite senza alterare il significato del testo.

Coerenza sintattica e morfologica. Ogni sostituzione candidata passa attraverso il POS-tagging via spaCy. Se la parola originale è un sostantivo plurale, anche la sostituzione deve esserlo. Se è un verbo alla terza persona singolare del presente, il sinonimo viene flesso di conseguenza, usando pyinflect sopra l'analisi morfologica di spaCy. È qui che gli approcci naïf alla sostituzione di sinonimi collassano: ignorano che "gioca" e "giocano" sono sintatticamente distinti anche se sono la stessa parola.

I sinonimi provengono da WordNet: non solo una lista, ma un grafo di relazioni semantiche con archi tipizzati (iperonimia, iponimia, antonimia, implicazione). Conta perché la prossimità nello spazio degli embedding non significa similarità semantica. Word2Vec è famoso per mettere i contrari vicini tra loro. WordNet non ha questo problema.

Verifica della similarità semantica. Dopo ogni mutazione, un modello paraphrase-MiniLM-L6-v2 misura la similarità coseno tra il testo originale e quello perturbato. Scendi sotto la soglia configurata: perturbazione rifiutata. Questo cattura la deriva sottile che si accumula quando si sostituiscono più parole. Sinonimi individualmente validi possono combinarsi in qualcosa di semanticamente sbagliato. Il modello è stato addestrato specificamente su coppie di parafrasi, il che lo rende particolarmente adatto a questo compito.

Targeting gerarchico delle frasi. Per gli input multi-frase, l'attacco non sparge modifiche sull'intero documento. Valuta ogni frase per il suo contributo individuale alla probabilità di classificazione, poi concentra le mutazioni prima sulla frase più impattante. Questo è centrale per i numeri di efficienza.

I risultati

Ho confrontato i risultati con 9 metodi allo stato dell'arte del framework TextAttack: BAE, Checklist, DeepWordBug, Faster Alzantot, Input Reduction, Pruthi, PWWS, TextBugger e TextFooler.

Il Global Score composito pondera il tasso di successo dell'attacco (40%), l'efficienza delle query (20%) e la preservazione semantica (40%).

AttaccoGlobal ScoreTasso di successoQuery medie
custom (questo lavoro)97,2100%17,9
Pruthi89,889,4%3.242
Checklist86,993,8%260
BAE66,2-324
Faster Alzantot--10.677

Il divario di efficienza è il numero che conta di più. Faster Alzantot è anch'esso un algoritmo genetico. Ha bisogno di 10.677 query in media. Questa implementazione ne ha bisogno di 18. La differenza viene da tre cose: selezione gerarchica delle frasi, campionamento casuale dei bigrammi invece di valutare ogni candidato, e stop anticipato nel momento in cui l'etichetta cambia.

Il 100% di successo contro un modello con il 93,9% di accuratezza è significativo. La maggior parte degli attacchi pre-ModernBERT mostrava già alti tassi di fallimento contro di esso. La finestra di contesto più ampia di ModernBERT (8.192 token contro i 512 di BERT) e l'architettura migliorata hanno genuinamente aumentato la robustezza. Ha retto contro la maggior parte della competizione. Non ha retto contro questo.

Tutto gira su hardware consumer: Ryzen 9 5900x, RTX 3080, 128 GB di RAM. Nessun datacenter, nessun credito API, nessun accesso speciale.

Cosa rivela davvero

La lettura ovvia: anche i modelli allo stato dell'arte hanno punti ciechi sfruttabili. È vero, e non è banale.

La lettura più profonda: quando un testo e la sua versione avversaria semanticamente equivalente ricevono classificazioni diverse, il modello sta rispondendo alle distribuzioni superficiali di parole apprese dai dati di training. Non al significato. Il contenuto semantico è lo stesso. La predizione non lo è.

Questa è una proprietà strutturale, non un bug. E ha conseguenze dirette per qualsiasi deployment NLP in produzione dove un avversario ha un incentivo a invertire una classificazione:

  • Monitoraggio del sentiment per intelligence di mercato o politica
  • Sistemi di moderazione dei contenuti
  • Classificazione di testi medici o legali
  • Sistemi di raccomandazione che si affidano a valutazioni automatiche della qualità del testo

L'attacco funziona in modalità black-box: nessun accesso ai pesi, nessun gradiente, solo accesso alle query. Un endpoint API è sufficiente. L'avversario non ha bisogno di sapere nulla degli interni del modello.

Problemi aperti

Due direzioni che vale la pena esplorare.

Prima i domini ristretti. Un modello addestrato esclusivamente su testi legali o medici è probabilmente più fragile, non meno. Il vocabolario è più piccolo, i pattern più prevedibili, lo spazio di ricerca adversarial più vincolato. L'attacco richiederebbe probabilmente ancora meno query.

Gli LLM sono il problema più difficile. La letteratura sull'adversarial ML si concentra sempre di più lì: jailbreak, prompt injection, agenti dormienti che si attivano solo sotto condizioni trigger specifiche. L'approccio di ricerca evolutiva si generalizza in linea di principio. La sfida pratica è il costo delle query e la natura stocastica dell'output degli LLM, che rende la valutazione della fitness incoerente. Vale la pena esplorarlo.

Le difese sono la questione aperta. La risposta ovvia è l'adversarial training: includere esempi avversariali nel training set. Ma l'adversarial training su un metodo di attacco non si generalizza bene ad altri, e l'attacco evolve. La corsa agli armamenti è in corso.


Bruce Schneier l'ha detto bene: "Se pensi che la tecnologia possa risolvere i tuoi problemi di sicurezza, non capisci né i problemi né la tecnologia."

Un modello con il 93,9% di accuratezza è genuinamente buono. È anche un sistema con vulnerabilità strutturali che possono essere localizzate e sfruttate senza alcun accesso agli interni. Capire quelle vulnerabilità è il prerequisito per costruire sistemi davvero affidabili. Non solo accurati sul test set.

L'avversario sa sempre esattamente dove guardare. Costruire la difesa richiede prima capire l'attacco.

Ho costruito l'attacco. La difesa è il problema aperto.


La tesi completa è disponibile su richiesta. L'implementazione gira su Python 3.9-3.11, costruita su spaCy, WordNet (via NLTK), HuggingFace Transformers, BitsAndBytes e pyinflect.