SQLFix Blog

Natuurlijke taal naar SQL: veiligheidscontroles die ertoe doen

Het vertalen van een vraag naar SQL is echt nuttig voor eerste concepten, maar het automatisch uitvoeren van modeluitvoer tegen een live database is hoe een typefout een verwijderde tabel wordt. De veilige houding is alleen concepten: het model stelt voor, een mens of een beveiligde pijplijn beslist. SQLForm volgt dat alleen-conceptenmodel en voert nooit uit wat het genereert zonder een beoordelingsstap.

1. Begin met een concept-pipeline

Ik heb SQLForm gebouwd rond het idee dat een taalmodel een hulpmiddel voor concepten is, niet een uitvoeringsengine. Wanneer een gebruiker vraagt “laat me de verkoopcijfers van vorige maand per regio zien”, retourneert het model een SELECT-instructie. Deze instructie wordt in een conceptwachtrij geplaatst en nooit naar de productieconnector gestuurd. Een menselijke operator of een geautomatiseerde poort moet de concept expliciet goedkeuren voordat deze de wachtrij verlaat. Deze scheiding creëert een duidelijk audittrail: elke query die gegevens aanraakt, heeft een geregistreerde goedkeuringsstap.

De conceptwachtrij wordt opgeslagen in een lichtgewicht-tabel met kolommen voor gebruikers-ID, tijdstempel, ruwe prompt, gegenereerde SQL en goedkeuringsstatus. Als het systeem crasht, kan de wachtrij opnieuw worden afgespeeld en gaat geen enkele query verloren. Ik heb deze ontwerpkeuze gemaakt nadat ik teams heb zien gegevens verliezen omdat een demscript een DELETE-instructie uitvoerde die het model dacht dat een SELECT was. De wachtrij voorkomt die klasse van fouten door constructie.

2. Detecteer muterende instructies voordat ze de wachtrij bereiken

De eerste verdedigingslinie is een eenvoudige lexicaal scan. Ik zoek naar de tokens DELETE, UPDATE, INSERT, DROP, TRUNCATE, ALTER, GRANT, REVOKE en CREATE. Als een van deze verschijnt, wordt het ontwerp gemarkeerd als muterend en in een holdingstate geplaatst die expliciete goedkeuring van een senior reviewer vereist. Zelfs een SELECT met een WITH-clausule die een muterende functie aanroept, wordt gevangen omdat de scan wordt uitgevoerd op de ruwe SQL-tekenreeks voordat er sprake is van parsing.

Ik controleer ook op instructiescheiders. Een puntkomma gevolgd door een ander token betekent dat de gebruiker mogelijk probeert om bewerkingen te ketenen. Bijvoorbeeld, „SELECT * FROM users; DELETE FROM logs” zou een naïeve SELECT-only-controle passeren, maar wordt geblokkeerd omdat de scanner het DELETE-token ziet na de eerste puntkomma.

Om valse positieven op legitieme objectnamen te voorkomen (zoals een kolom met de naam „delete_flag”), behandelt de scanner tokens alleen als trefwoorden wanneer ze gevolgd worden door spaties of een haakje, overeenkomstig typische SQL-grammatica. Dit houdt de regel precies zonder nuttige identificatoren te verbieden.

3. Concepten uitvoeren in een alleen-lezen-zandbak

Zodra een concept is goedgekeurd, voer ik het uit tegen een alleen-lezen-replica van het productieschema. De replica wordt elke nacht vanuit de primaire bron vernieuwd en heeft geen schrijfrechten toegewezen aan de verbindingrol die door SQLForm wordt gebruikt. Als het model op de een of andere manier een muterende instructie produceert, retourneert de database een fout en worden geen gegevens gewijzigd. Dit verandert een potentieel kostbare fout in een zichtbare fout die de reviewer onmiddellijk kan zien.

Ik dwing het alleen-lezen-karakter ook af op verbindingniveau door de PostgreSQL-parameter default_transaction_read_only = on in te stellen voor de sessie. In MySQL start ik de sessie met SET SESSION TRANSACTION ISOLATION LEVEL READ ONLY;. Deze instellingen zorgen ervoor dat de server elke WRITE-, ALTER- of DROP-instructie met een duidelijke foutcode weigert, die ik in de UI weergeef.

Het gebruik van een replica heeft een extra voordeel: de query kan worden uitgevoerd tegen licht verouderde gegevens zonder de SLA's te beïnvloeden, en ik kan de lengte van de query en het resourcegebruik controleren zonder het productievolume te riskeren. Als de replica verder achterloopt dan een instelbare drempel, houdt het systeem het concept aan en verwittigt de goedkeurder dat de gegevens mogelijk niet synchroon zijn.

4. Parameteriseer goedgekeurde queries

Nadat een ontwerp de menselijke review-stap heeft doorstaan, verplaats ik de SQL naar de applicatielaag als een voorbereide instructie. De model-gegenereerde tekst wordt een sjabloon waarin alle door de gebruiker opgegeven waarden zijn gebonden als parameters. Ik concatenate nooit raw strings, zelfs niet na goedkeuring, omdat dit het injectierisico opnieuw introduceert dat de ontwerp bedoeld was te vermijden.

Ik volg de richtlijnen van de OWASP SQL Injection Prevention Cheat Sheet, die voorbereide instructies als de primaire verdediging noemt. In de praktijk gebruik ik de native API van de database-driver: pg_prepare / pg_execute voor PostgreSQL, en PreparedStatement voor MySQL. De driver zorgt voor escaping en typeveiligheid, zodat een waarde met een aanhalingsteken of een puntkomma als gegevens wordt behandeld, niet als SQL.

Om ervoor te zorgen dat de sjabloon geldig blijft, parseer ik de goedgekeurde SQL met een lichtgewicht SQL-parser (zoals sqlparse) en verifieer ik dat elke placeholder overeenkomt met een parameterindex. Als de parser een los token vindt dat eruitziet als een waarde maar niet is gebonden, wordt het ontwerp teruggestuurd voor herziening.

5. Menselijke controle als controlepunt

De reviewer hoeft geen SQL-expert te zijn; de controlelijst is met opzet kort gehouden. Eerst verifieert hij of de SELECT-lijst overeenkomt met de kolommen die in de oorspronkelijke prompt worden genoemd. Als de gebruiker om 'totaal omzet per regio' heeft gevraagd en de query COUNT(*) retourneert, markeert de reviewer dit. Ten tweede bevestigt hij dat er geen muterende token verschijnt, waarbij hij zich verlaat op de scanner, maar ook een visuele scan uitvoert op eventuele slimme verhulling. Ten derde kijkt hij even naar de WHERE-clausule om te zien of deze onbedoeld meer rijen ophaalt dan de bedoeling is (bijvoorbeeld een ontbrekende datumfilter die een volledige tabelscan zou veroorzaken).

Ik behandel de gegenereerde query als een suggestie, net zoals ik SQL zou controleren dat is geschreven door een nieuwe teamgenoot. De reviewer kan goedkeuren, een wijziging aanvragen of afkeuren. Als hij een wijziging aanvraagt, kan hij de conceptversie rechtstreeks in de UI bewerken; de bewerkte versie wordt een nieuwe conceptversie en gaat opnieuw door dezelfde controles. Deze lus zorgt ervoor dat de output van het model nooit de definitieve autoriteit is.

De tijdwinst komt voort uit het elimineren van de noodzaak om boilerplate-SQL van scratch te schrijven. Ik heb gezien dat teams de tijd om een eerste conceptquery te produceren van tien minuten terugbrengen tot minder dan twee minuten, terwijl ze nog steeds een mens in de lus houden voor veiligheid.

Wat dit niet is

Ik beweer niet dat SQLForm elke mogelijke fout vindt. De lexicon-scanner kan worden omzeild door ongebruikelijke codering of door database-specifieke syntaxis te gebruiken die eruitziet als een opmerking. Een vastberaden aanvaller kan een prompt maken die de modeloutput een statement laat produceren dat een opgeslagen procedure met neveneffecten gebruikt, wat de scanner niet zou markeren omdat de procedurenaam niet in onze keywordlijst staat. De read-only replica beschermt tegen onbedoelde schrijfacties, maar als de replica verkeerd is geconfigureerd en schrijfrechten heeft overgenomen, verdwijnt het veiligheidsnet.

Menselijke controle is essentieel, maar reviewers kunnen subtiele logische fouten missen, vooral wanneer de query lang is of complexe window-functies gebruikt. Het proces vermindert het risico, maar het elimineert het niet. Ik behandel SQLForm als een risicoverminderingstool, niet als een zilveren kogel.

Referenties

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

Veelgestelde vragen

Alleen tegen een read-only replica of met een harde poort die muterende statements blokkeert. Auto-uitvoeren van gegenereerde SQL tegen productie is hoe een ontwerpfout tot gegevensverlies leidt. Houd het model in een alleen-voorstel-rol.

Een modelontwerp is onbetrouwbare invoer totdat het is beoordeeld, dezelfde klasse als CWE-89 SQL-injectie. Een read-only-rol betekent dat zelfs een onopzettelijke uitvoering productiegegevens niet kan wijzigen.

Nee. Zodra een query in applicatiecode terechtkomt, gebruik dan voorbereide statements. Waarden terug samenstellen in een string herintroduceert injectierisico. Beoordeling en parameterisatie zijn separate controles.

DELETE-, UPDATE-, DROP-, TRUNCATE- en privilege-verlenende statements mogen nooit in een auto-uitvoerpad worden uitgevoerd. Ze vereisen expliciete, separate goedkeuring.

Probeer het product: SQLForm

2026-09-16 · alleen primaire bronnen · geen gefabriceerde verkeersmetrieken

Gerelateerde artikelen

Geparametriseerde queries vs gegenereerde SQL

Verken de volledige AI micro-SaaS onderwerpmatrix →

Kan NL2SQL veilig zijn om automatisch uit te voeren?

Alleen tegen een read-only replica of met een harde poort die muterende statements blokkeert. Het automatisch uitvoeren van gegenereerde SQL tegen productie is hoe een conceptfout verandert in dataverlies. Houd het model in een voorstel-only rol.

Waarom is een read-only context belangrijk? Een modelconcept is onbetrouwde invoer totdat het is beoordeeld, dezelfde klasse als CWE-89 SQL-injectie. Een read-only rol betekent dat zelfs een onopzettelijke uitvoering productiegegevens niet kan wijzigen. Betekent het beoordelen van SQL dat ik parameterisatie kan overslaan?

Een modelconcept is onbetrouwde invoer totdat het is beoordeeld, dezelfde klasse als CWE-89 SQL-injectie. Een read-only rol betekent dat zelfs een onopzettelijke uitvoering productiegegevens niet kan wijzigen.

Welke statements moet een veiligheidslaag standaard blokkeren?

DELETE, UPDATE, DROP, TRUNCATE en privilege-verlenende statements mogen nooit worden uitgevoerd in een automatische uitvoerpad. Zij vereisen expliciete, separate goedkeuring.

Alleen voor besluitsondersteuning. Geen vervanging voor DBA-beoordeling of productielasttesten. Geen prestatiegaranties.

← Terug naar blog

Was dit resultaat nuttig?Feedback