SQL Injection — een type aanval op de database van een applicatie waarbij een aanvaller schadelijke SQL-code in queryparameters injecteert en ongeautoriseerde toegang tot gegevens of de mogelijkheid om deze te wijzigen verkrijgt. Volgens gegevens van OWASP (2025) blijft SQL Injection een van de kritieke kwetsbaarheden die kan leiden tot volledige compromittering van de database. Injectie van SQL-code stelt de aanvaller in staat om records te lezen, te wijzigen en te verwijderen, en in sommige gevallen — toegang te krijgen tot het besturingssysteem van de server.
Belangrijkste punten
SQL Injection is een kwetsbaarheid die ontstaat wanneer een applicatie een SQL-query opbouwt door stringconcatenatie met gebruikersgegevens. De aanvaller stuurt een speciaal geconstrueerde string naar de queryparameter die de structuur van het SQL-commando verandert. In plaats van gegevens te zijn, wordt de ingevoerde string onderdeel van de SQL-code, waardoor de aanvaller willekeurige query's op de database kan uitvoeren. Volgens het Verizon Data Breach Report (2025) komt SQL Injection voor in 8% van alle onderzochte datalekken.
Ondanks de bekendheid van de kwetsbaarheid (eerste vermeldingen — eind jaren 1990) komt SQL Injection nog steeds voor in moderne applicaties. De oorzaak is menselijke factor: ontwikkelaars staan stringconcatenatie in code toe, legacy-code wordt niet gerefactord en ORM-frameworks worden onjuist gebruikt (bijvoorbeeld raw-queries met het substitueren van waarden via strings). Volgens Veracode (2026) bevat ongeveer 14% van alle gescande applicaties ten minste één SQLi-kwetsbaarheid.
Een succesvolle SQL Injection geeft de aanvaller een breed scala aan mogelijkheden: het lezen van elke tabel in de database, inclusief wachtwoordhashes en persoonlijke gebruikersgegevens; het wijzigen en verwijderen van records; het uitvoeren van administratieve bewerkingen (DROP TABLE, TRUNCATE); in sommige configuraties — het extern uitvoeren van opdrachten via xp_cmdshell (MSSQL) of INTO OUTFILE (MySQL). De gevolgen variëren van het lekken van gebruikersgegevens tot volledig verlies van controle over het systeem.
SQL-injecties worden geclassificeerd op basis van de manier waarop gegevens uit de database worden geëxtraheerd. De keuze van de methode hangt af van hoe de applicatie queryresultaten en foutmeldingen verwerkt. OWASP-classificatie onderscheidt drie hoofdtypen: In-band (gegevens worden via hetzelfde kanaal geëxtraheerd), Inferential/Blind (logische conclusies) en Out-of-band (gegevens worden via een ander kanaal verzonden).
| Type | Methode van gegevensextractie | Complexiteit | Frequentie |
|---|---|---|---|
| In-band (klassiek) | Direct via het queryresultaat | Laag | Hoog |
| Blind SQLi | Logische conclusies op basis van serverantwoorden | Hoog | Gemiddeld |
| Out-of-band | Via een extern kanaal (DNS, HTTP) | Gemiddeld | Laag |
Het meest voorkomende type. De aanvaller injecteert SQL-code in de queryparameter en het resultaat van de injectie is direct zichtbaar in het serverantwoord. Twee subtypes: Error-based (via foutmeldingen van de database) en UNION-based (via de UNION SELECT-operator). Error-based gebruikt informatie uit foutmeldingen, bijvoorbeeld een MySQL-syntaxisfout die de tabelnaam of querystructuur kan onthullen. UNION-based maakt het mogelijk om resultaten van een legitieme query te combineren met gegevens uit andere databasetabellen.
Wordt gebruikt wanneer de applicatie geen queryresultaten of foutmeldingen weergeeft. De aanvaller stelt „ja/nee”-vragen door queries met logische voorwaarden te sturen en het verschil in serverantwoord te analyseren (bijvoorbeeld responstijd of pagina-inhoud). Time-based Blind SQLi gebruikt een vertragingsfunctie (SLEEP, WAITFOR DELAY) om voorwaarden te bevestigen — als de pagina langer laadt, is de voorwaarde waar. Deze methode is erg traag — het extraheren van één record kan uren duren.
# Voorbeeld Blind SQL Injection (time-based)
# Als SQLi kwetsbaar is, wordt SLEEP(2) onder voorwaarde uitgevoerd
import requests
import time
payload = "' OR IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a',SLEEP(2),0) -- "
url = "http://example.com/user?id=1" + payload
start = time.time()
response = requests.get(url)
elapsed = time.time() - start
# Als het antwoord na >2 seconden kwam — de eerste letter van het wachtwoord is 'a'
print(f"Time: {elapsed:.2f}s - First letter is 'a'" if elapsed > 2 else "First letter is not 'a'")
Gegevens worden niet via het HTTP-antwoord verzonden, maar via alternatieve kanalen: DNS-query's, HTTP-verzoeken naar een externe server, SMTP. Wordt gebruikt wanneer de applicatie geen queryresultaten retourneert en geen fouten toont. MySQL ondersteunt de functie LOAD_FILE(), die een DNS-query kan initiëren, en MSSQL — xp_dirtree voor het verzenden van gegevens naar een externe SMB-server. Out-of-band SQL Injection is effectief, maar vereist extra configuratie op de server van de aanvaller en de aanwezigheid van bepaalde databasefuncties.
Het mechanisme van SQL Injection is gebaseerd op het feit dat SQL aanhalingstekens voor stringliterals gebruikt. Als de applicatie gebruikersinvoer direct in de SQL-query substitueert zonder escaping, kan de aanvaller de string „sluiten” en willekeurige SQL-code toevoegen. Bijvoorbeeld in de query SELECT * FROM users WHERE name = '$input' verandert substitutie van ' OR '1'='1 de query in SELECT * FROM users WHERE name = '' OR '1'='1', wat alle gebruikers retourneert.
Beschouw een loginformulier met de query SELECT * FROM users WHERE username = '$user' AND password = '$pass'. Als de aanvaller in het veld username admin' -- invoert en het wachtwoord leeg laat, wordt de resulterende query SELECT * FROM users WHERE username = 'admin' -- ' AND password = ''. De tekens -- commentariëren de rest van de query en de voorwaarde voor wachtwoordcontrole wordt uitgeschakeld. De server retourneert het record van gebruiker admin en de aanvaller logt in zonder het wachtwoord te kennen.
# Voorbeeld SQL Injection — omzeilen van authenticatie
# KWETSBAAR CODE: directe stringconcatenatie
def login_vulnerable(username, password):
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
cursor.execute(query)
return cursor.fetchone() is not None
# username = "admin' --" maakt wachtwoordcontrole ongedaan
# VEILIGE VERSIE: geparametriseerde query
def login_secure(username, password):
query = "SELECT * FROM users WHERE username = %s AND password = %s"
cursor.execute(query, (username, password))
return cursor.fetchone() is not None
De operator UNION SELECT maakt het mogelijk om de resultaten van twee SELECT-query's te combineren. Als de aanvaller een kwetsbare parameter vindt, voegt hij UNION SELECT toe met een query uit een andere tabel. Bijvoorbeeld: ' UNION SELECT username, password FROM admins --. Voorwaarde voor succes — het aantal kolommen in beide queries moet overeenkomen. Het aantal kolommen wordt bepaald via ORDER BY (substitutie van ' ORDER BY 1--, vervolgens 2, 3... tot een fout). Als de aanvaller het aantal kolommen kent, substitueert hij UNION SELECT met hetzelfde aantal velden.
Mobiele applicaties krijgen te maken met SQL Injection zowel aan de serverzijde (API) als aan de clientzijde — in lokale databases (SQLite, Realm). Hoewel het risico van server-SQLi in mobiele API vergelijkbaar is met webapplicaties, creëert lokale database een extra vector. Als de applicatie gegevens opslaat in SQLite en queries uitvoert met stringconcatenatie, kunnen schadelijke gegevens die via de API in de lokale database terechtkomen, bij latere verwerking SQLi veroorzaken.
De lokale SQLite-database op het apparaat is ook kwetsbaar voor SQL Injection als de applicatie queries vormt door stringconcatenatie. Content Providers op Android en Core Data op iOS gebruiken standaard parametrisatie, maar raw-queries vereisen aandacht van de ontwikkelaar. SQLite ondersteunt geen meerdere queries via puntkomma, wat de mogelijkheden van de aanvaller beperkt, maar niet beschermt tegen het lezen van gegevens via WHERE-voorwaarden. Gebruik altijd selectionArgs in Android en NSPredicate met parameters in iOS.
De API waarmee de mobiele applicatie communiceert is even kwetsbaar als elke webserver. Mobiele ontwikkelaars denken vaak dat SQLi alleen een backend-probleem is, maar de kwetsbaarheid ontstaat op het niveau van het API-endpoint dat parameters van de client ontvangt. Verdeling van verantwoordelijkheid beschermt niet: als de backend-ontwikkelaar vergeten is de query te parametriseren, wordt de mobiele applicatie van de gebruiker een aanvalsvector. Eis van de backend het gebruik van ORM of prepared statements.
// Voorbeeld SQL Injection in lokale SQLite op Android
// KWETSBAAR CODE: directe concatenatie
fun getUserVulnerable(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = $userId", null
)
}
// VEILIGE VERSIE: parametrisatie via selectionArgs
fun getUserSecure(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = ?",
arrayOf(userId)
)
}
Bescherming tegen SQL Injection is gebaseerd op een eenvoudig principe: vertrouw nooit op gebruikersinvoer in SQL-queries. De enige betrouwbare methode — parametrisatie van queries (prepared statements), waarbij SQL-code en gegevens apart worden verzonden. Alle andere methoden — escaping, validatie, WAF — zijn extra beschermingslagen, maar vervangen parametrisatie niet. Volgens OWASP (2025) voorkomt parametrisatie 100% van SQL Injection-aanvallen.
Bij gebruik van prepared statements wordt de SQL-query eerst zonder gegevens door de databaseserver gecompileerd en vervolgens worden de parameterwaarden apart verzonden. De database behandelt parameters als gegevens, niet als uitvoerbare code. Zelfs als de aanvaller ' OR '1'='1 verzendt, interpreteert de database dit als een stringwaarde, niet als SQL-code. Prepared statements worden ondersteund door alle moderne talen en frameworks: PDO in PHP, PreparedStatement in Java, cursor.execute in Python.
Moderne ORM's (Hibernate, Entity Framework, SQLAlchemy, Room) gebruiken automatisch parametrisatie bij het uitvoeren van queries, tenzij de ontwikkelaar overstapt op raw-queries. ORM biedt echter geen volledige bescherming: constructies zoals @Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true) in JPA vereisen het doorgeven van parameters via benoemde parameters, niet via concatenatie. Query Builders (Knex, jOOQ) parametriseren queries ook standaard, tenzij raw-methoden worden gebruikt.
Escaping van speciale tekens (mysql_real_escape_string) — een verouderde methode die niet beschermt tegen alle soorten SQL Injection. Probleem: escaping is afhankelijk van de codering en kan worden omzeild met multi-byte coderingen (bijv. GBK in Aziatische systemen). Gebruik escaping alleen in legacy-code waar parametrisatie niet mogelijk is, en altijd in combinatie met strikte validatie van het type invoergegevens.
| Methode | Effectiviteit | Aanbeveling |
|---|---|---|
| Prepared Statements | 100% | Verplicht voor alle queries |
| ORM (correct gebruik) | 99% | Aanbevolen |
| String-escaping | 70% (afhankelijk van codering) | Alleen legacy |
| Invoervalidatie (whitelist) | 50% (alleen voor getallen) | Aanvullend |
| WAF (Web Application Firewall) | 60% | Aanvullend |
Het account van de applicatie moet minimaal noodzakelijke rechten hebben: SELECT, INSERT, UPDATE, DELETE — alleen op tabellen die de applicatie echt nodig heeft. Verbied het gebruik van DROP, TRUNCATE, CREATE voor het applicatieaccount. Dit beperkt de schade, zelfs bij een succesvolle SQL Injection: de aanvaller kan geen tabellen verwijderen of administratieve bewerkingen uitvoeren.
Regelmatig testen op SQL Injection zou deel moeten uitmaken van de pijplijn voor veilige ontwikkeling. Een combinatie van statische analyse, dynamische scanning en handmatige pentest geeft de beste resultaten. Volgens Synopsys Cybersecurity Report (2025) vinden automatische scanners tot 70% van SQLi-kwetsbaarheden, maar complexe Blind-aanvallen vereisen handmatig testen.
Voor mobiele applicaties is ook analyse van lokale SQLite belangrijk: controleer alle rawQuery-aanroepen, ContentProvider-queries en Room-queries met rawQuery. Hulpmiddelen: Android Studio Lint (detecteert SQLi in SQLite), MobSF (Mobile Security Framework) voor automatische statische en dynamische analyse van APK/IPA. Het wordt ook aanbevolen om API-endpoints te testen via sqlmap met proxy-interceptie van mobiel applicatieverkeer.
Veelgestelde vragen
SQL Injection valt relationele databases aan via SQL-queries. NoSQL Injection tast niet-relationele databases (MongoDB, Couchbase) aan via hun query-operators ($gte, $ne, $where). In MongoDB is injectie mogelijk als de applicatie een BSON-document uit een JSON-string vormt. Beschermingsmechanismen zijn vergelijkbaar: parametrisatie en typevalidatie.
Vind alle plaatsen waar SQL-queries worden gevormd door stringconcatenatie met gebruikersgegevens. Zoek naar patronen zoals "SELECT ... WHERE id = " + userId of f"UPDATE ... SET name = '{name}'". Elke dergelijke string is een potentiële SQL-injectie. Vervang ze allemaal door geparametriseerde queries of prepared statements.
ORM-frameworks beschermen alleen automatisch als u hun Query Builder-methoden en benoemde parameters gebruikt. Als u raw-queries gebruikt (nativeQuery in JPA, rawQuery in Room), werkt de bescherming niet — u moet parameters doorgeven via prepared expressions, niet via stringconcatenatie.
Second-Order SQL Injection is een aanval waarbij schadelijke gegevens als veilig in de database worden opgeslagen maar vervolgens in een andere query zonder escaping worden gebruikt. Bijvoorbeeld, een aanvaller registreert zich met username ' OR '1'='1. De gegevens worden opgeslagen als string — bij registratie is er geen aanval. Maar als een andere query de username in SQL substitueert zonder parametrisatie, wordt de injectie geactiveerd.
NoSQL Injection is potentieel gevaarlijker vanwege mindere bewustwording bij ontwikkelaars. Ontwikkelaars weten van SQL Injection en de meeste gebruiken ORM, maar weinig mensen weten van NoSQL Injection. In MongoDB kan een onjuist gevormde query alle documenten van de collectie retourneren. Bescherming — dezelfde prepared statements (BSON-parametrisatie) en strikte validatie van invoergegevens.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook