Wat is SQL-injectie?
SQL-injectie (SQLi) is een aanvalstechniek waarbij kwaadaardige SQL-code wordt geïnjecteerd via gebruikersinvoer, waardoor een aanvaller ongeautoriseerde database-operaties kan uitvoeren. Ondanks dat het een van de oudste webkwetsbaarheden is (bekend sinds 1998), staat het nog steeds in de OWASP Top 10 en wordt het actief misbruikt.
De gevolgen zijn ernstig: volledige database-extractie, data-wijziging, authenticatie-bypass, en in sommige gevallen overname van de server.
Hoe SQL-injectie werkt
Het basisprincipe
Een kwetsbare applicatie bouwt SQL-queries op door gebruikersinvoer direct in de query-string te plakken:
-- Kwetsbare query (NOOIT doen)
SELECT * FROM users WHERE username = '" + userInput + "' AND passwd = '" + passInput + "'
Als een aanvaller als gebruikersnaam invoert: admin' --
Wordt de query:
SELECT * FROM users WHERE username = 'admin' --' AND passwd = ''
Het -- maakt de rest van de query tot commentaar. De aanvaller logt in als admin zonder wachtwoord.
Typen SQL-injectie
| Type | Methode | Detectie | |---|---|---| | In-band (Classic) | Resultaat direct zichtbaar in response | Makkelijk te detecteren | | Error-based | Database-foutmeldingen onthullen informatie | Makkelijk te detecteren | | Union-based | UNION SELECT om data uit andere tabellen te lezen | Makkelijk te detecteren | | Blind (Boolean) | Waar/onwaar vragen stellen via condities | Moeilijker te detecteren | | Time-based blind | Vertragingen in response meten | Moeilijk te detecteren | | Out-of-band | Data via alternatief kanaal exfiltreren (DNS, HTTP) | Zeer moeilijk te detecteren |
De oplossing: parameterized queries
De primaire en meest effectieve bescherming tegen SQL-injectie is het gebruik van parameterized queries (ook bekend als prepared statements). Hierbij wordt de SQL-structuur gescheiden van de data.
Node.js (PostgreSQL)
// VEILIG — parameterized query
const result = await pool.query(
'SELECT * FROM users WHERE email = $1 AND status = $2',
[email, 'active']
);
// ONVEILIG — string concatenatie (NOOIT doen)
const result = await pool.query(
`SELECT * FROM users WHERE email = '${email}'`
);
ORM's (Prisma, Drizzle)
ORM's gebruiken standaard parameterized queries:
// Prisma — automatisch veilig
const user = await prisma.user.findUnique({
where: { email: userInput },
});
Let op: raw queries in ORM's omzeilen deze bescherming:
// ONVEILIG — raw query met string interpolatie
const result = await prisma.$queryRawUnsafe(
`SELECT * FROM users WHERE name = '${userInput}'`
);
// VEILIG — raw query met parameters
const result = await prisma.$queryRaw`
SELECT * FROM users WHERE name = ${userInput}
`;
Python (SQLAlchemy)
# VEILIG — parameterized
result = session.execute(
text("SELECT * FROM users WHERE email = :email"),
{"email": user_input}
)
PHP (PDO)
// VEILIG — prepared statement
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
Aanvullende beschermingslagen
Laag 2: Input validatie
Valideer invoer op type, lengte en formaat. Dit vangt een deel van de aanvallen op maar is geen vervanging voor parameterized queries.
const emailSchema = z.string().email().max(255);
const idSchema = z.number().int().positive();
Laag 3: Least privilege
Geef de database-gebruiker van uw applicatie alleen de rechten die nodig zijn:
| Recht | Nodig voor webapplicatie? | |---|---| | SELECT | Ja | | INSERT | Meestal ja | | UPDATE | Meestal ja | | DELETE | Beperkt (soft delete overwegen) | | DROP | Nee | | CREATE | Nee (migraties via apart account) | | GRANT | Nooit |
Laag 4: Stored procedures
Voor complexe queries kunt u stored procedures gebruiken, die server-side worden gecompileerd en minder vatbaar zijn voor injectie.
Laag 5: WAF (Web Application Firewall)
Een WAF kan bekende SQL-injectie patronen blokkeren, maar is een vangnet — niet een primaire verdediging.
Laag 6: Foutafhandeling
Toon nooit database-foutmeldingen aan gebruikers in productie:
// ONVEILIG
catch (error) {
res.status(500).json({ error: error.message }); // Onthult database-structuur
}
// VEILIG
catch (error) {
logger.error({ error: error.message }, 'Database query failed');
res.status(500).json({ error: 'Er is een fout opgetreden' });
}
Veelgemaakte fouten
| Fout | Risico |
|---|---|
| String concatenatie voor queries | Directe SQL-injectie kwetsbaarheid |
| Raw queries zonder parameters | Omzeilt ORM-bescherming |
| Vertrouwen op client-side validatie | Aanvaller stuurt direct HTTP-requests |
| Database-fouten tonen in productie | Informatielekkage over structuur |
| Te brede database-rechten | Aanvaller kan DROP TABLE uitvoeren |
| Alleen WAF als bescherming | WAF-bypass technieken zijn bekend |
| Vergeten van LIKE-clausules | % en _ zijn wildcards die moeten worden geëscaped |
SQL-injectie in context: NIS2 en AVG
SQL-injectie raakt direct aan compliance:
- NIS2: organisaties moeten passende beveiligingsmaatregelen implementeren. SQL-injectie kwetsbaarheden getuigen van nalatigheid.
- AVG: een datalek via SQL-injectie is meldingsplichtig bij de AP. Als u onvoldoende maatregelen had genomen, riskeert u een boete bovenop de reputatieschade.
Testen op SQL-injectie
Handmatige tests
Veelgebruikte testpatronen voor invoervelden:
- Enkele aanhalingsteken:
' - Boolean condities:
' OR '1'='1 - Commentaar:
--,#,/* */ - UNION queries:
' UNION SELECT null, null -- - Time-based:
' OR SLEEP(5) --
Geautomatiseerd
Geautomatiseerde security scanners testen systematisch alle invoerpunten op SQL-injectie kwetsbaarheden, inclusief blinde varianten die handmatig moeilijk te detecteren zijn.
Conclusie
SQL-injectie is een volledig te voorkomen kwetsbaarheid. Gebruik altijd parameterized queries, valideer invoer, beperk database-rechten en toon geen technische foutmeldingen in productie. Er is geen reden om in 2026 nog kwetsbaar te zijn voor SQLi.
Scan uw website gratis met WarDek — OWASP, NIS2, AVG, AI Act compliance in één scan.