Blog di SQLFix

Linguaggio naturale in SQL: controlli di sicurezza che contano

Tradurre una domanda in SQL è veramente utile per le prime bozze, ma l'esecuzione automatica dell'output del modello su un database live è il modo in cui un errore di battitura diventa una tabella cancellata. La postura sicura è solo bozza: il modello propone, un essere umano o una pipeline protetta dispone. SQLForm segue quel modello di sola bozza e non esegue mai ciò che genera senza un passaggio di revisione.

1. Inizia con un pipeline in sola bozza

Ho costruito SQLForm intorno all'idea che un modello linguistico sia un assistente di stesura, non un motore di esecuzione. Quando un utente chiede "mostrami le vendite dello scorso mese per regione", il modello restituisce un'istruzione SELECT. Quell'istruzione viene inserita in una coda di bozza e non viene mai inviata al connettore di produzione. Un operatore umano o un cancello automatizzato deve approvare esplicitamente la bozza prima che esca dalla coda. Questa separazione crea un chiaro registro di audit: ogni query che tocca i dati ha un passaggio di approvazione registrato.

La coda di bozza è persistita in un table leggero con colonne per ID utente, timestamp, prompt grezzo, SQL generato e stato di approvazione. Se il sistema si blocca, la coda può essere riprodotta e nessuna query viene persa. Ho scelto questo design dopo aver visto team perdere dati a causa di uno script di demo che eseguiva un'istruzione DELETE che il modello pensava fosse una SELECT. La coda impedisce quella classe di errore per costruzione.

2. Rileva le istruzioni di mutazione prima che raggiungano la coda

La prima linea di difesa è una semplice scansione lessicale. Cerco i token DELETE, UPDATE, INSERT, DROP, TRUNCATE, ALTER, GRANT, REVOKE e CREATE. Se uno di questi appare, la bozza viene contrassegnata come mutante e inserita in uno stato di attesa che richiede l'approvazione esplicita di un revisore senior. Anche un SELECT che contiene una clausola WITH che chiama una funzione di mutazione viene catturato perché la scansione viene eseguita sulla stringa SQL grezza prima di qualsiasi analisi.

Controllo anche i separatori di istruzioni. Un punto e virgola seguito da un altro token significa che l'utente potrebbe stare tentando di concatenare operazioni. Ad esempio, „SELECT * FROM users; DELETE FROM logs“ supererebbe un controllo naïve basato solo su SELECT, ma viene bloccato perché lo scanner vede il token DELETE dopo il primo punto e virgola.

Per evitare falsi positivi su nomi di oggetti legittimi (come una colonna denominata „delete_flag“), lo scanner tratta i token come parole chiave solo quando sono seguiti da spazi bianchi o da una parentesi, in base alla grammatica SQL tipica. Ciò mantiene la regola precisa senza bandire identificatori utili.

3. Eseguire bozzine in un sandbox in sola lettura

Una volta approvata una bozza, la eseguo contro una replica in sola lettura dello schema di produzione. La replica viene aggiornata ogni notte dal primario e non ha privilegi di scrittura assegnati al ruolo di connessione utilizzato da SQLForm. Se il modello produce in qualche modo una istruzione di mutazione, il database restituisce un errore e non vengono modificati dati. Ciò trasforma un errore potenzialmente costoso in un errore visibile che il revisore può vedere immediatamente.

Inoltre, impongo la proprietà di sola lettura a livello di connessione impostando il parametro PostgreSQL default_transaction_read_only = on per la sessione. In MySQL, avvio la sessione con SET SESSION TRANSACTION ISOLATION LEVEL READ ONLY;. Queste impostazioni causano il rifiuto del server di qualsiasi comando WRITE, ALTER o DROP con un codice di errore chiaro, che visualizzo nell'interfaccia utente.

L'utilizzo di una replica ha un vantaggio aggiuntivo: la query può essere eseguita su dati leggermente obsoleti senza influenzare gli SLA e posso monitorare la lunghezza della query e l'utilizzo delle risorse senza rischiare di sovraccaricare la produzione. Se la replica supera una soglia configurabile, il sistema tiene in sospeso la bozza e notifica all'approvatore che i dati potrebbero essere fuori sincrono.

4. Parametrizza le query approvate

Dopo che una bozza supera il passaggio di revisione umana, sposto il SQL nel livello di applicazione come istruzione preparata. Il testo generato dal modello diventa un modello in cui tutti i valori forniti dall'utente sono vincolati come parametri. Non concateno mai stringhe grezze, anche dopo l'approvazione, perché ciò reintroduce il rischio di iniezione che la bozza doveva evitare.

Seguo le linee guida del foglio di riferimento per la prevenzione dell'iniezione SQL di OWASP, che elenca le istruzioni preparate come difesa principale. In pratica, utilizzo l'API nativa del driver del database: pg_prepare / pg_execute per PostgreSQL e PreparedStatement per MySQL. Il driver si occupa della gestione degli escape e della sicurezza dei tipi, quindi un valore che contiene un apice o un punto e virgola viene trattato come dati, non come SQL.

Per assicurarmi che il modello rimanga valido, analizzo il SQL approvato con un parser SQL leggero (come sqlparse) e verifico che ogni segnaposto corrisponda a un indice di parametro. Se il parser trova un token vagante che sembra un valore ma non è vincolato, la bozza viene rinviata per revisione.

5. Revisione umana come punto di controllo

Il revisore non è tenuto ad essere un esperto di SQL; la checklist è deliberatamente breve. In primo luogo, verificano che l'elenco SELECT corrisponda alle colonne menzionate nel prompt originale. Se l'utente ha richiesto il "totale del fatturato per regione" e la query restituisce COUNT(*), il revisore la segnala. In secondo luogo, confermano che non appaiano token di mutazione, affidandosi allo scanner ma anche eseguendo un controllo visivo per eventuali obfuscamenti intelligenti. In terzo luogo, danno un'occhiata alla clausola WHERE per vedere se inavvertitamente recupera più righe del previsto (ad esempio, se manca un filtro di data che potrebbe causare una scansione dell'intera tabella).

Tratto la query generata come una suggerimento, esattamente come se stessi revisionando il SQL scritto da un nuovo membro del team. Il revisore può approvare, richiedere una modifica o rifiutare. Se richiede una modifica, può modificare direttamente la bozza nell'interfaccia utente; la versione modificata diventa una nuova bozza e passa attraverso gli stessi controlli di nuovo. Questo ciclo garantisce che l'output del modello non sia mai l'autorità finale.

Il guadagno di velocità deriva dall'eliminazione della necessità di scrivere il SQL boilerplate da zero. Ho visto team ridurre il tempo per produrre una query di prima bozza da dieci minuti a meno di due minuti, mantenendo comunque un umano nel loop per motivi di sicurezza.

Cosa questo non è

Non affermo che SQLForm catturi ogni possibile errore. L'analizzatore lessicale può essere evaso utilizzando un codifica insolita o utilizzando una sintassi specifica del database che sembra un commento. Un attaccante determinato potrebbe creare una richiesta che fa in modo che il modello produca una dichiarazione che utilizza una procedura memorizzata con effetti collaterali, che lo scanner non segnala perché il nome della procedura non è nella nostra lista di parole chiave. La replica in sola lettura protegge contro scritture accidentali, ma se la replica è configurata in modo errato e eredita i diritti di scrittura, la rete di sicurezza scompare.

La revisione umana è essenziale, ma i revisori possono perdere errori logici sottili, specialmente quando la query è lunga o utilizza funzioni di finestra complesse. Il processo riduce il rischio, ma non lo elimina. Tratto SQLForm come uno strumento di riduzione del rischio, non come una soluzione miracolosa.

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

Solo contro una replica in sola lettura o con un gate rigido che blocca le istruzioni di mutazione. L'esecuzione automatica di SQL generato contro la produzione è il modo in cui un errore di bozza diventa una perdita di dati. Mantenere il modello in un ruolo di sola proposta.

Una bozza del modello è un input non attendibile fino a quando non viene esaminata, nella stessa classe di CWE-89 iniezione SQL. Un ruolo in sola lettura significa che anche un'esecuzione accidentale non può modificare i dati di produzione.

No. Una volta che una query si sposta nel codice dell'applicazione, utilizzare le istruzioni preparate. La concatenazione dei valori nuovamente in una stringa reintroduce il rischio di iniezione. La revisione e la parametrazione sono controlli separati.

Le istruzioni DELETE, UPDATE, DROP, TRUNCATE e di concessione di privilegi non dovrebbero mai essere eseguite in un percorso di esecuzione automatica. Richiedono un'approvazione esplicita e separata.

Prova il prodotto: SQLForm

2026-09-16 · solo fonti primarie · nessuna metrica di traffico fabbricata

Articoli correlati

Query parametrizzate vs SQL generato

Esplora la matrice completa degli argomenti micro-SaaS AI →

NL2SQL può essere sicuro per l'esecuzione automatica?

Solo contro una replica in sola lettura o con un gate rigido che blocca le istruzioni di mutazione. L'esecuzione automatica di SQL generato contro la produzione è il modo in cui un errore di bozza diventa perdita di dati. Mantieni il modello in un ruolo di sola proposta.

Perché un contesto in sola lettura è importante? Una bozza del modello è un input non attendibile fino a quando non viene esaminata, nella stessa classe di CWE-89 SQL injection. Un ruolo in sola lettura significa che anche un'esecuzione accidentale non può modificare i dati di produzione. L'esame di SQL significa che posso saltare la parametrazione?

Una bozza del modello è un input non attendibile fino a quando non viene esaminata, nella stessa classe di CWE-89 SQL injection. Un ruolo in sola lettura significa che anche un'esecuzione accidentale non può modificare i dati di produzione.

Quali istruzioni dovrebbe bloccare uno strato di sicurezza per impostazione predefinita?

Le istruzioni DELETE, UPDATE, DROP, TRUNCATE e quelle che concedono privilegi non dovrebbero mai essere eseguite in un percorso di esecuzione automatica. Richiedono un'approvazione esplicita e separata.

Supporto alle decisioni solo. Non sostituisce la revisione del DBA o il test del carico di produzione. Nessuna garanzia di prestazioni.

← Torna al blog

Questo risultato è stato utile?Feedback