SQLFix Blog

Geparametriseerde queries vs gegenereerde SQL

Een model dat een correct uitziende query schrijft, heeft het injectieprobleem nog niet opgelost. De beveiligingseigenschap die ertoe doet, leeft in de manier waarop de query de data ontmoet, en dat is de taak van geparametriseerde queries met voorbereide statements. Gegenereerde tekst is een startpunt voor beoordeling; het is geen vervanging voor de grens die voorbereide statements afdwingen tussen code en data.

Wat een voorbereide verklaring eigenlijk doet

Wanneer de driver een voorbereidingsverzoek ontvangt, verzendt deze de SQL-tekst naar de server. De server parseert de tekst, bouwt een uitvoeringsplan en slaat dat plan op. Later, wanneer de toepassing execute aanroept, verzendt de driver alleen de parameterwaarden. De server past het opgeslagen plan toe op die waarden zonder de SQL-tekst opnieuw te parseren. Omdat de waarden nooit deel worden van de statement-string, wordt een payload zoals ' OR 1=1-- als een letterlijke waarde behandeld, niet als onderdeel van het commando.

Ik heb dit zien werken in MySQL 5.0, PostgreSQL 8.0, SQL Server 2005 en Oracle 9i. Elke mainstream-taal biedt een binding-API: JDBC's PreparedStatement, Python's cursor.execute met placeholders, Go's database/sql.Query en Node's mysql2 pool.execute. Het model kan de vorm SELECT * FROM orders WHERE customer_id = ? AND status = ? ; produceren, waarbij de omliggende code de twee waarden bij runtime levert.

Als u het aanhalingsteken binnen de statement-tekst ziet, gebruikt u geen echte voorbereide verklaring; u voert waarschijnlijk client-zijde string-interpolatie uit, wat geen injectieveiligheid biedt.

Waarom beoordeelde SQL nog steeds parameters nodig heeft

Menselijke beoordeling kan bevestigen dat de vorm van de query overeenkomt met de bedoelde bedrijfsregel. Het kan ontbrekende joins, verkeerde kolomnamen of inefficiënte filters opsporen. Echter, de waarden die in die placeholders terechtkomen, komen nog steeds van externe bronnen: webformulieren, API-payloads, berichtenwachtrijen of legacy-bestanden. Deze bronnen vallen niet onder de directe controle van de ontwikkelaar op het moment van uitvoering.

De SQL Injection Prevention Cheat Sheet van OWASP vermeldt duidelijk: „Defensie-optie 1: Prepared Statements (Parameterized Queries)”. De cheat sheet vermeldt „menselijke goedkeuring” niet als vervanging. Goedkeuring richt zich op logische correctheid; parameterisatie richt zich op de vertrouwensgrens tussen code en gegevens. Het laten vallen van een van beide laat de andere onbeschermd.

Zelfs als de menselijke beoordelaar een verstandige INSERT-instructie zag, vond de injectie plaats bij het waarde-bindingsstap, dat de beoordeling niet heeft behandeld.

Op deze manier blijft de werk van de beoordelaar waardevol, en de uitvoeringsstap behoudt zijn veiligheidsgarantie.

Opgeslagen procedures zijn geen automatische veiligheid

Veel teams gaan ervan uit dat het verplaatsen van logica naar een opgeslagen procedure het injectierisico elimineert. Deze veronderstelling faalt wanneer de procedure zelf dynamische SQL bouwt.

Bekijk een procedure geschreven 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 Als @Customer de waarde '); DROP TABLE Orders;-- bevat, wordt de resulterende string een geldige batch die de tabel verwijdert. De procedure-wrapper bood geen bescherming voor de gegevens; de concatenatie binnen de procedure deed dit.

Hetzelfde patroon verschijnt in PL/SQL met EXECUTE IMMEDIATE, in PostgreSQL met EXECUTE format, en in MySQL met PREPARE/EXECUTE opgebouwd uit een variabele.

Na het herschrijven, voer de procedure uit met een waarde die een aanhalingsteken bevat en verifieer dat de fout een gegevenstype-mismatch is, en geen syntaxisfout.

Houd generatie en uitvoering als afzonderlijke lagen

De veiligste architectuur behandelt het AI-model als een suggestiemotor, niet als een data-toegangsonderdeel. Het model produceert een kandidaatqueryvorm. Een menselijke of geautomatiseerde beleidsgate beoordeelt die vorm op correctheid, naleving en prestaties. Pas na goedkeuring neemt de toepassingscode de vorm over, bereidt deze voor met de stuurprogramma en koppelt de realtimeparameters.

Omdat het model nooit een liveverbinding ziet, kan het niet worden misleid om schemadetails via foutmeldingen te onthullen. Omdat de uitvoerlaag nooit vertrouwt op onbewerkte tekenreeksen, kan zelfs een gecompromitteerd model geen injectie veroorzaken.

Dit proces houdt de uitvoer van het model in de beoordelingslus en garandeert dat de uitvoerstap alleen geparametreerde instructies gebruikt.

Wat dit niet is

Ik wil duidelijk zijn over de beperkingen van de hierboven beschreven aanpak. Het garandeert niet dat elke mogelijke bug wordt opgevangen. Het belooft geen 100% bescherming tegen alle klassen van fouten. Het beweert niet de noodzaak voor andere verdedigingsmechanismes zoals invoervalidatie, uitvoerencoding of databaseaccounts met minimale rechten te elimineren.

Wat de methode doet, is:

Als u alleen vertrouwt op de uitvoer van het model en de review of de parameterbinding overslaat, verliest u de bescherming. Als u de uitvoerlaag toestaat waarden na voorbereiding te concatenaten, introduceert u het risico opnieuw. De techniek is een onderdeel van een verdedigingsstrategie in diepte, geen op zich staande zilveren kogel.

Echt-wereldvoorbeeld van workflow

Stel je een SaaS-product voor dat managers toelaat om een lijst van vervallen facturen te exporteren. Het product gebruikt SQLForm om de query op te stellen.

Deze trace toont aan hoe de generatiestap onafhankelijk blijft van de uitvoeringsstap, en hoe de parametergrens injectie blokkeert, zelfs wanneer de gebruiker probeert om SQL in een veld te smokkelen dat het model nooit ziet.

Gangbare valkuilen en hoe we deze vermijden

Zelfs met een solide ontwerp, vallen teams soms in gewoontes die de beveiliging verzwakken. Hieronder staan veelvoorkomende fouten die ik heb waargenomen en de concrete stappen die we nemen om het systeem veilig te houden.

Sommige ontwikkelaars bouwen SELECT * FROM tags WHERE tag IN (?,?,?) door eerst een string te maken met het juiste aantal placeholders, en vervolgens de waarden rechtstreeks in de SQL te concatenaten voordat ze deze voorbereiden. Dit tenietdoet het doel, omdat de waarden onderdeel worden van de statementtekst.

Een statement dat is voorbereid voor SELECT * FROM orders WHERE status =? wordt later uitgevoerd met een waarde die eigenlijk een hele nieuwe SELECT-statement bevat, waarbij de driver wordt vertrouwd om de waarde als gegevens te behandelen. Hoewel de waarde veilig wordt afgehandeld, kan de applicatielogica verkeerd zijn omdat de ontwikkelaar aannam dat de statement elke SQL kon verwerken.

Interne dashboards omzeilen soms de menselijke reviewpoort omdat het team aanneemt dat de gegevens betrouwbaar zijn. Later wordt een serviceaccount gehackt en begint het kwaadaardige payloads in het dashboard te voeden.

Voor debugging loggen teams soms de exacte parameterwaarden. Als deze logbestanden worden blootgesteld, kunnen ze gevoelige gegevens zoals wachtwoorden of persoonlijk identificeerbare informatie lekken.

Een ORM kan een methode bieden die lijkt parameters te accepteren, maar onder de motorkap een string bouwt en ExecuteNonQuery aanroept op een raw SQL-string.

Door naar deze valkuilen te kijken en de overeenkomstige fixes toe te passen, blijft de scheiding tussen generatie en uitvoering sterk, en blijft de parameterized boundary het werk doen waarvoor het is ontworpen.

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

Nee. Injectie hangt af van hoe waarden voldoen aan de query, niet van of de query logisch correct is. Geparameteriseerde queries houden waarden gescheiden van opdrachttekst, ongeacht de inhoud van de waarden.

Niet automatisch. Een procedure die invoer samenvoegt en deze uitvoert met EXECUTE IMMEDIATE, kan nog steeds injectie veroorzaken. Veiligheid komt van parameterbinding binnen de procedure.

Een ORM is alleen veilig wanneer u de geparameteriseerde interface gebruikt. Het opbouwen van een string en deze doorgeven aan de ORM introduceert opnieuw injectie. De grens tussen code en gegevens moet nog steeds worden afgedwongen.

Nee. Houd generatie en uitvoering gescheiden: het model maakt een ontwerp, een poort keurt goed en applicatiecode bindt parameters tijdens runtime. Het model mag nooit het uitvoerpad zijn.

Probeer het product: SQLForm

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

Gerelateerde artikelen

Natuurlijke taal naar SQL: veiligheidscontroles die ertoe doen

Verken de volledige AI micro-SaaS onderwerpmatrix →

Beschermt een correcte query tegen SQL-injectie?

Nee. Injectie hangt af van hoe waarden de query ontmoeten, niet van of de query logisch correct is. Geparameteriseerde queries houden waarden gescheiden van opdrachttekst, ongeacht de inhoud van de waarden.

Zijn opgeslagen procedures standaard veilig?

Niet automatisch. Een procedure die invoer concateneert en deze uitvoert met EXECUTE IMMEDIATE, kan nog steeds injectie veroorzaken. Veiligheid komt van parameterbinding binnen de procedure.

Kan een ORM geparameteriseerde queries vervangen?

Een ORM is alleen veilig wanneer u de geparameteriseerde interface gebruikt. Het opbouwen van een string en deze doorgeven aan de ORM, herintroduceert injectie. De grens tussen code en gegevens moet nog steeds worden afgedwongen.

Moet een model ooit de databaseverbinding bevatten?

Nee. Houd generatie en uitvoering gescheiden: het model ontwerpt, een poort keurt goed en toepassingscode bindt parameters tijdens runtime. Het model mag nooit het uitvoerpad zijn.

Alleen voor beslissingsondersteuning. Geen vervanging voor DBA-beoordeling of productieloadtesting. Geen prestatiegaranties.

← Terug naar blog

Was dit resultaat nuttig?Feedback