arrow_backTerug naar veldaantekeningen
WEB SECURITY Gepubliceerd 8 Aug 2026

Wat is SQL-injectie en hoe voorkom je het?

Een praktische uiteenzetting van SQL-injectieaanvallen, echte voorbeelden en concrete oplossingen die ervan echt afhelpen in productiecode.

SQL-injectie bestaat sinds het einde van de jaren negentig en het is nog steeds een van de meest voorkomende manieren waarop webapplicaties worden gecompromitteerd. De bug is eenvoudig: een aanvaller stuurt invoer die wordt geïnterpreteerd als SQL-code in plaats van gewone gegevens, en de database doet iets wat nooit de bedoeling was.

Hoe de aanval werkelijk werkt

StellĄ je voor een loginformulier dat in PHP een query als volgt bouwt:

$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";

Als de app invoer niet opschoont, voert een aanvaller admin' -- in het gebruikersnaamveld in. De query wordt:

SELECT * FROM users WHERE username = 'admin' --' AND password = ''

De -- becommentarieert de rest van de regel, dus de wachtwoordcontrole vindt nooit plaats. Dit is een klassieke auth bypass. Aanvallers gebruiken ook UNION SELECT om gegevens uit andere tabellen op te halen, of stapelen queries met een puntkomma om iets destructiefs uit te voeren zoals DROP TABLE users; als het stuurprogramma meerdere statements toestaat.

Er is ook blind SQL-injectie, waarbij de app queryresultaten niet rechtstreeks toont. Aanvallers leiden informatie af via timing (SLEEP(5) in MySQL) of booleaanse reacties (gedraagt de pagina zich anders voor waar versus onwaar). Tools zoals sqlmap automatiseren dit soort extractie zodra een kwetsbare parameter is gevonden.

Waarom tekenreeksaaneenschakeling het kernprobleem is

Elke variant van deze aanval komt terug op één ding: code en gegevens in dezelfde tekenreeks mengen. De database kan het verschil tussen een legitieme waarde en geïnjecteerde syntaxis niet onderscheiden omdat ze via hetzelfde kanaal binnenkomen. Aanhalingstekens ontsnappen helpt in sommige gevallen, maar het is fragiel — verschillende coderingen, second-order injection (gegevens opgeslagen en later onveilig hergebruikt) en stuurprogramma-specifieke eigenaardigheden creëren allemaal omzeiltradepaden. Ontsnappen is een pleister, geen oplossing.

Geparameteriseerde queries zijn de echte oplossing

De oplossing is SQL-code van gebruikersgegevens scheiden op stuurprogrammaniveau, met geparameteriseerde queries (ook wel prepared statements genoemd). De database ontvangt eerst de querystructuur en bindt daarna waarden, dus gebruikersinvoer kan de betekenis van de query nooit veranderen.

In Python met psycopg2:

cur.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password))

In Node.js met mysql2:

connection.execute('SELECT * FROM users WHERE username = ? AND password = ?', [username, password]);

In Java met JDBC:

PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE username = ? AND password = ?");
stmt.setString(1, username);
stmt.setString(2, password);

Merk het patroon op: aanduidingen (%s, ?) houden een plaats voor gegevens, en de werkelijke waarden worden apart doorgegeven. Geen tekenreeksaaneenschakeling, geen handmatig ontsnappen nodig. Dit werkt voor de overgrote meerderheid van queries die je in een normale applicatie schrijft.

Wat met dynamische tabel- of kolomnamen?

Parameterisering verwerkt waarden maar niet identificatoren — je kunt een tabelnaam niet als parameter binden. Als je app dynamisch een tabel moet selecteren (zeldzaam, en vaak een designrood signaal), zet de toegestane waarden op een whitelist tegen een hardcoded lijst in plaats van gebruikersinvoer rechtstreeks te vertrouwen:

allowed_tables = {'orders', 'invoices', 'customers'}
if table_name not in allowed_tables:
    raise ValueError("Invalid table")

Bouw identificatornamen nooit door tekenreeksformattering van gebruikersinvoer, zelfs niet met ontsnatten.

Gelaagde verdediging verder dan de query zelf

Geparameteriseerde queries zijn de primaire controle, maar een paar andere dingen zijn belangrijk:

  • Minimale bevoegdheden op de databaseaccount. De DB-gebruiker van de app mag niet DROP, ALTER of toegang tot niet-gerelateerde schema's hebben. Als injectie toch door de zeef gaat, beperken beperkte bevoegdheden de schade.
  • ORMs helpen standaard. Django's ORM, SQLAlchemy en Hibernate parameteriseren queries automatisch wanneer je hun standaard query-bouwmethoden gebruikt. Het risico verschijnt opnieuw wanneer ontwikkelaars in ruwe SQL duiken of .extra()/text()-aanroepen met tekenreeksinterpolatie gebruiken — dus controleer die plekken specifiek.
  • Invoervalidatie is een secundaire laag, geen vervanging. Controleren of een e-mailveld eruit ziet als een e-mail is goed praktijk, maar het stopt injectie op zichzelf niet — aanvallers vinden creatieve payloads die nog steeds losse validatie doorstaan.
  • Een WAF kan bekende aanvalspatronen opvangen, maar het is een detectielaag, geen reparatie van de onderliggende code.

Je eigen code op deze bug testen

Voer je queries uit via een statische analysegereedschap (Bandit voor Python, Semgrep met SQL-injectierulesets) als onderdeel van CI. Voor handmatig testen, probeer een enkel aanhalingsteken (') in elk invoerveld in te voegen en kijk naar SQL-foutmeldingen die in het antwoord uitlekken — dat is vaak het eerste teken dat een query niet geparameteriseerd is.

Als je dieper op dit onderwerp wilt ingaan, behandelt Korra Studio's Web Security track injectie naast XSS en auth bypass, en de Databases-segmenten doorlopen querydesignpatronen die deze klasse bugs helemaal vermijden.

Geschreven met AI-ondersteuning, herzien en gepubliceerd door Michal Pilch (CISSP), Korra Studio.

Klaar om verder te gaan?

Dit is één aantekening uit de kennisbasis van Korra Studio — het platform koppelt elk onderwerp aan 1-op-1 mentoring.

Gratis beginnenarrow_forward