Blog di SQLFix
Query parametrizzate vs SQL generato
Un modello che scrive una query dall'aspetto corretto non ha risolto l'iniezione. La proprietà di sicurezza che conta vive nel modo in cui la query incontra i dati, e questo è il lavoro delle query parametrizzate con istruzioni preparate. Il testo generato è un punto di partenza per la revisione; non è un sostituto del confine che le istruzioni preparate impongono tra codice e dati.
Cosa fa effettivamente una dichiarazione preparata
Quando il driver riceve una richiesta di preparazione, invia il testo SQL al server. Il server analizza il testo, costruisce un piano di esecuzione e lo memorizza. In seguito, quando l'applicazione chiama l'esecuzione, il driver invia solo i valori dei parametri. Il server applica il piano memorizzato a quei valori senza riparsing il testo SQL. Poiché i valori non diventano mai parte della stringa di istruzioni, un payload come ' OR 1=1-- viene trattato come valore letterale, non come parte del comando.
Ho visto funzionare questo in MySQL 5.0, PostgreSQL 8.0, SQL Server 2005 e Oracle 9i. Ogni linguaggio mainstream fornisce un'API di binding: il PreparedStatement di JDBC, il cursor.execute di Python con segnaposti, il database/sql.Query di Go e il pool.execute di Node. Il modello può output la forma SELECT * FROM orders WHERE customer_id = ? AND status = ? ; il codice circostante fornisce i due valori in fase di runtime.
Se vedi la virgolette all'interno del testo dell'istruzione, non stai utilizzando una vera e propria dichiarazione preparata; probabilmente stai eseguendo un'interpolazione di stringhe lato client, che non fornisce la sicurezza contro le iniezioni.
Perché l'SQL revisionato ha ancora bisogno di parametri
La revisione umana può confermare che la forma della query corrisponde alla regola aziendale prevista. Può rilevare join mancanti, nomi di colonne errati o filtri inefficienti. Tuttavia, i valori che fluiscono in quei segnaposto provengono ancora da fonti esterne: moduli web, payload API, code di messaggi o file legacy. Queste fonti non sono sotto il controllo diretto dello sviluppatore nel momento dell'esecuzione.
La scheda di prevenzione dell'iniezione SQL di OWASP afferma chiaramente: "Opzione di difesa 1: istruzioni preparate (query parametrizzate)“. La scheda non elenca "approvazione umana“ come sostituto. L'approvazione riguarda la correttezza logica; la parametrizzazione riguarda il confine di fiducia tra codice e dati. Omettere uno dei due lascia l'altro senza protezione.
Anche se il revisore umano ha visto un'istruzione INSERT sensata, l'iniezione è avvenuta al passaggio di associazione dei valori, che la revisione non ha coperto.
In questo modo, il lavoro del revisore rimane prezioso e il passaggio di esecuzione mantiene la sua garanzia di sicurezza.
Le procedure archiviate non sono automaticamente sicure
Molte squadre suppongono che spostare la logica all'interno di una procedura archiviata elimini il rischio di iniezione. Questa supposizione fallisce quando la procedura stessa costruisce SQL dinamico.
Considera una procedura scritta in T‑SQL:
CREATE PROCEDURE GetOrders @Customer NVARCHAR(50) AS BEGIN DECLARE @sql NVARCHAR(MAX) = N'SELECT * FROM Orders WHERE Customer = ''' + @Customer + ''''; EXEC sp_executesql @sql; END Se @Customer contiene '); DROP TABLE Orders;-- , la stringa risultante diventa un batch valido che elimina la tabella. La procedura wrapper non ha protetto i dati; la concatenazione all'interno della procedura ha fallito.
Lo stesso pattern appare in PL/SQL con EXECUTE IMMEDIATE, in PostgreSQL con EXECUTE format e in MySQL con PREPARE/EXECUTE costruito da una variabile.
Dopo la riscrittura, esegui la procedura con un valore che contiene un apice e verifica che l'errore sia un errore di tipo di dati, non un errore di sintassi.
Mantenere la generazione e l'esecuzione come strati separati
L'architettura più sicura tratta il modello di intelligenza artificiale come un motore di suggerimenti, non come un componente di accesso ai dati. Il modello produce una forma di query candidata. Un gate di politica umano o automatizzato esamina quella forma per la correttezza, la conformità e le prestazioni. Solo dopo l'approvazione, il codice dell'applicazione prende la forma, la prepara con il driver e associa i parametri in tempo reale.
Poiché il modello non vede mai una connessione live, non può essere ingannato per rivelare dettagli dello schema attraverso messaggi di errore. Poiché lo strato di esecuzione non si fida mai di stringhe grezze, anche un modello compromesso non può causare iniezioni.
Questo processo mantiene l'output del modello nel ciclo di revisione e garantisce che il passaggio di esecuzione utilizzi solo istruzioni parametrizzate.
Cosa questo non è
Voglio essere chiaro sui limiti dell'approccio descritto sopra. Non garantisce che ogni possibile errore verrà rilevato. Non promette una protezione del 100% contro tutte le classi di vulnerabilità. Non afferma di eliminare la necessità di altre difese come la convalida dell'input, la codifica dell'output o gli account del database con privilegi minimi.
Cosa fa il metodo è:
Se ti affidi esclusivamente all'output del modello e salti la revisione o il passaggio di binding dei parametri, perdi la protezione. Se consenti allo strato di esecuzione di concatenare valori dopo la preparazione, riintroduci il rischio. La tecnica è un componente di una strategia di difesa in profondità, non una soluzione universale autonoma.
Esempio di flusso di lavoro nel mondo reale
Immagina un prodotto SaaS che consente ai manager di esportare un elenco di fatture scadute. Il prodotto utilizza SQLForm per creare la query.
Questo tracciato mostra come il passaggio di generazione rimanga indipendente dal passaggio di esecuzione e come il limite dei parametri blocchi l'iniezione anche quando l'utente tenta di contrabbandare SQL in un campo che il modello non vede mai.
Errori comuni e come li evitiamo
Anche con un design solido, i team a volte scivolano in abitudini che indeboliscono la protezione. Di seguito sono riportati gli errori frequenti che ho osservato e i passi concreti che prendiamo per mantenere il sistema sicuro.
Alcuni sviluppatori costruiscono SELECT * FROM tags WHERE tag IN (?, ?, ?) creando prima una stringa con il numero giusto di segnaposti, quindi concatenando direttamente i valori nel SQL prima di prepararlo. Ciò vanifica lo scopo perché i valori diventano parte del testo dell'istruzione.
Un'istruzione preparata per SELECT * FROM orders WHERE status = ? viene successivamente eseguita con un valore che contiene effettivamente una nuova istruzione SELECT, affidandosi al driver per trattare il valore come dati. Sebbene il valore sia citato in modo sicuro, la logica dell'applicazione potrebbe essere errata perché lo sviluppatore ha assunto che l'istruzione potesse gestire qualsiasi SQL.
I cruscotti interni a volte bypassano il gate di revisione umana perché il team presume che i dati siano attendibili. Successivamente, un account di servizio viene compromesso e inizia a fornire payload maligni al cruscotto.
Per il debug, i team a volte registrano i valori esatti dei parametri. Se tali registri vengono esposti, possono far perdere dati sensibili come password o informazioni personali identificabili.
Un ORM può offrire un metodo che sembra accettare parametri, ma sotto il cofano costruisce una stringa e chiama ExecuteNonQuery su una stringa SQL grezza.
Osservando questi errori e applicando le correzioni corrispondenti, la separazione tra generazione ed esecuzione rimane forte e il confine parametrizzato continua a svolgere il lavoro per cui è stato progettato.
Riferimenti
https://cheatsheetseries.owasp.org/cheatsheets/LLM_Prompt_Injection_Prevention_Cheat_Sheet.html
https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
https://cwe.mitre.org/data/definitions/798.html
https://cwe.mitre.org/data/definitions/89
Domande frequenti
No. L'iniezione dipende da come i valori soddisfano la query, non dal fatto che la query sia logicamente corretta. Le query parametrizzate mantengono i valori separati dal testo del comando indipendentemente dal contenuto dei valori.
Non automaticamente. Una procedura che concatena l'input e lo esegue con EXECUTE IMMEDIATE può ancora iniettare. La sicurezza deriva dal binding dei parametri all'interno della procedura.
Un ORM è sicuro solo quando si utilizza la sua interfaccia parametrizzata. Costruire una stringa e passarla all'ORM reintroduce l'iniezione. Il confine tra codice e dati deve ancora essere applicato.
No. Mantenere la generazione e l'esecuzione separate: il modello crea una bozza, un gate approva e il codice dell'applicazione associa i parametri in fase di esecuzione. Il modello non dovrebbe mai essere il percorso di esecuzione.
Prova il prodotto: SQLForm
2026-09-16 · solo fonti primarie · nessuna metrica di traffico fabbricata
Articoli correlati
Lingua naturale a SQL: controlli di sicurezza che contano
Esplora la matrice completa dei temi AI micro-SaaS →
Un query corretto protegge contro l'iniezione SQL?
No. L'iniezione dipende da come i valori soddisfano la query, non dal fatto che la query sia logicamente corretta. Le query parametrizzate mantengono i valori separati dal testo del comando indipendentemente dal contenuto dei valori.
Le stored procedure sono sicure per default?
Non automaticamente. Una procedura che concatena input ed esegue con EXECUTE IMMEDIATE può ancora iniettare. La sicurezza deriva dal binding dei parametri all'interno della procedura.
Un ORM può sostituire le query parametrizzate?
Un ORM è sicuro solo quando si utilizza la sua interfaccia parametrizzata. Costruire una stringa e passarla all'ORM reintroduce l'iniezione. Il confine tra codice e dati deve ancora essere applicato.
Un modello dovrebbe mai contenere la connessione al database?
No. Mantenere separata la generazione e l'esecuzione: il modello crea una bozza, un gate approva e il codice dell'applicazione lega i parametri in fase di runtime. Il modello non dovrebbe mai essere il percorso di esecuzione.
Solo per supporto decisionale. Non sostituisce la revisione del DBA o il test di carico di produzione. Nessuna garanzia di prestazioni.