Wat is SQL Injection en hoe voorkom je het?
Auteur: Redactie Domeny.ForSale Gepubliceerd
In het kort: SQL Injection is een kwetsbaarheid waarmee speciaal samengestelde invoer de werking van een databasequery kan veranderen. Deze ontstaat vooral wanneer een applicatie SQL-opdrachten samenvoegt met tekst die een gebruiker heeft ingevoerd. De belangrijkste bescherming bestaat uit geparametriseerde query’s, die de opdracht gescheiden houden van de bijbehorende gegevens.
Hoe gebruikersinvoer een opdracht wordt
SQL is een taal voor het lezen en wijzigen van gegevens in relationele databases. Een applicatie kan daarmee een account zoeken, een bestelling ophalen of een nieuw klantadres opslaan. SQL Injection, oftewel het injecteren van SQL, treedt op wanneer invoer wordt behandeld als onderdeel van de syntaxis van zo’n opdracht.
Stel dat een applicatie een account zoekt op gebruikersnaam. De programmeur schrijft het begin van de query, voegt de waarde uit het formulier toe en sluit die af met een apostrof. Een gewone gebruikersnaam levert het verwachte resultaat op, maar tekst met een apostrof en een extra voorwaarde kan de betekenis van de hele zoekopdracht veranderen.
De gevaarlijke waarde hoeft niet uit een inlogformulier te komen. De bron kan ook een parameter in een URL, een productfilter, een requestheader of eerder opgeslagen gegevens in de database zijn. Het gaat erom hoe de applicatie die waarde gebruikt, niet waar de waarde vandaan komt.
Een eenvoudig experiment op Linux
Het onderstaande voorbeeld werkt uitsluitend met een tijdelijke database in het procesgeheugen. Het maakt geen verbinding met een website of externe server. Het laat zien hoe meer records worden uitgelezen dan bedoeld, en is geen volledig inlogmechanisme.
Je hebt Python 3 nodig met de module sqlite3. Controleer in de terminal of die beschikbaar is met de opdracht:
python3 -c "import sqlite3; print(sqlite3.sqlite_version)"
Als de opdracht mislukt, raadpleeg dan de documentatie van je distributie voor de installatie van Python 3 of de ontbrekende module. Je hoeft geen aparte databaseserver te starten.
Voer het volledige blok uit in de terminal:
python3 - <<'PY'
import sqlite3
verbinding = sqlite3.connect(":memory:")
polaczenie.execute(
"CREATE TABLE accounts (id INTEGER PRIMARY KEY, login TEXT)"
)
polaczenie.executemany(
"INSERT INTO accounts (login) VALUES (?)",
[("anna",), ("pieter",)]
)
waarde = "' OR 1=1 -- "
query = "SELECT id, login FROM accounts WHERE login = '" + waarde + "'"
print("Query:", query)
print("Resultaat:", polaczenie.execute(query).fetchall())
polaczenie.close()
PY
Het resultaat bevat beide accounts, hoewel geen van beide een gebruikersnaam heeft die overeenkomt met de opgegeven waarde. De apostrof sluit de tekst van de gebruikersnaam af, OR 1=1 voegt een voorwaarde toe die altijd waar is, en -- begint een commentaar. Daardoor heeft de laatste apostrof geen invloed meer op de query.
Deze code is bewust kwetsbaar en bedoeld voor een lokale demonstratie. Test soortgelijke invoer niet op websites van anderen zonder uitdrukkelijke toestemming die ook de omvang van de tests dekt.
Zo herstel je dezelfde code
Een geparametriseerde query bevat plaatshouders voor gegevens. De waarden zelf worden afzonderlijk doorgegeven aan de bibliotheek die met de database communiceert. In de module sqlite3 kan ? zo’n plaatshouder zijn. De bibliotheek behandelt de doorgegeven waarde als gegevens, niet als extra SQL-syntaxis.
Vervang in het vorige voorbeeld het opbouwen en uitvoeren van de query door dit fragment. Laat het vóór polaczenie.close() staan:
query = "SELECT id, login FROM accounts WHERE login = ?"
resultaat = polaczenie.execute(query, (waarde,)).fetchall()
print("Resultaat:", resultaat)
Je krijgt nu een lege lijst: de database zoekt naar een gebruikersnaam die letterlijk de volledige doorgegeven tekst bevat. Als je de waarde van waarde verandert in "anna", geeft de query alleen het account van Anna terug. De komma in (waarde,) maakt er een tuple met één element van, zoals deze manier van parameters doorgeven vereist.
Vervang parameters niet door handmatig apostroffen te verwijderen of tekst te formatteren, bijvoorbeeld met een f-string. Een gebruikersnaam met een apostrof kan geldige invoer zijn; de applicatie moet daar veilig mee omgaan. De syntaxis voor parameters verschilt per bibliotheek. Neem het patroon ? dus niet zonder controle over in elke driver.
Wat de gevolgen van de kwetsbaarheid kunnen zijn
SQL Injection kan het mogelijk maken om gegevens van anderen te lezen, een deel van de toegangscontroles te omzeilen en records te wijzigen of te verwijderen. Het daadwerkelijke gevolg hangt af van de query, de database-engine en de rechten van het account waarmee de applicatie de database benadert. Het vinden van de kwetsbaarheid betekent niet automatisch dat de hele server kan worden overgenomen.
Ook het ontbreken van een zichtbare fout bewijst niet dat een applicatie veilig is. De applicatie kan databasemeldingen verbergen, terwijl de veranderde werking alleen blijkt uit een verschil in de antwoorden. Omgekeerd is één fout na het invoeren van een apostrof aanleiding voor onderzoek, maar op zichzelf geen bewijs van SQL Injection.
Wat je in je eigen applicatie moet controleren
Begin je controle op de plekken waar SQL-query’s worden opgebouwd:
- Vervang het samenvoegen van tekst door parameters voor alle waarden die van buiten de query komen.
- Controleer ook rechtstreeks geschreven query’s als je een ORM gebruikt: een laag die objecten uit de applicatie koppelt aan gegevens in de database.
- Kies kolomnamen en de sorteerrichting uit een vaste lijst met toegestane opties. Parameters voor waarden kunnen zulke onderdelen van de syntaxis doorgaans niet vervangen.
- Beperk de rechten van het databaseaccount tot de bewerkingen die de applicatie nodig heeft.
- Toon gebruikers geen gedetailleerde databasefouten of volledige query’s met vertrouwelijke gegevens.
Kies om te beginnen één zoekopdracht die gebruikersinvoer gebruikt. Voeg een test toe met een gewone waarde en met tekst die een apostrof bevat. Controleer of beide invoerwaarden als gegevens worden behandeld en niet de structuur van de query veranderen.
Zie ook: Wat is een ETF?
