SQL Injection — en typ av attack mot applikationens databas där angriparen injicerar skadlig SQL-kod i frågeparametrar och får obehörig åtkomst till data eller möjlighet att ändra dem. Enligt uppgifter från OWASP (2025) är SQL Injection fortfarande en av de kritiska sårbarheterna som kan leda till fullständig kompromettering av databasen. Injektion av SQL-kod gör det möjligt för angriparen att läsa, ändra och ta bort poster, och i vissa fall — få åtkomst till serverns operativsystem.
Huvudpunkter
SQL Injection är en sårbarhet som uppstår när en applikation skapar en SQL-fråga genom strängkonkatenering med användardata. Angriparen skickar en speciellt konstruerad sträng till frågeparametern som ändrar strukturen på SQL-kommandot. Istället för att vara data blir den inmatade strängen en del av SQL-koden, vilket gör att angriparen kan utföra godtyckliga frågor mot databasen. Enligt Verizon Data Breach Report (2025) finns SQL Injection i 8% av alla undersökta dataläckor.
Trots att sårbarheten är känd (första omnämnanden — slutet av 1990-talet) förekommer SQL Injection i moderna applikationer. Orsaken — mänsklig faktor: utvecklare tillåter strängkonkatenering i koden, äldre kod omfaktoriseras inte och ORM-ramverk används felaktigt (t.ex. råa frågor med substituering av värden via strängar). Enligt uppgifter från Veracode (2026) innehåller cirka 14% av alla skannade applikationer minst en SQLi-sårbarhet.
En lyckad SQL Injection ger angriparen ett brett spektrum av möjligheter: läsning av alla tabeller i databasen, inklusive lösenordshashar och personuppgifter; ändring och borttagning av poster; utförande av administrativa operationer (DROP TABLE, TRUNCATE); i vissa konfigurationer — fjärrexekvering av kommandon via xp_cmdshell (MSSQL) eller INTO OUTFILE (MySQL). Konsekvenserna sträcker sig från läckage av användardata till fullständig förlust av kontroll över systemet.
SQL-injektioner klassificeras efter metoden för att extrahera data från databasen. Valet av metod beror på hur applikationen bearbetar frågeresultat och felmeddelanden. OWASP-klassificering skiljer på tre huvudtyper: In-band (data extraheras via samma kanal), Inferential/Blind (logiska slutsatser) och Out-of-band (data överförs via en annan kanal).
| Typ | Metod för dataextrahering | Komplexitet | Frekvens |
|---|---|---|---|
| In-band (klassisk) | Direkt via frågeresultatet | Låg | Hög |
| Blind SQLi | Logiska slutsatser baserat på serversvar | Hög | Medel |
| Out-of-band | Via extern kanal (DNS, HTTP) | Medel | Låg |
Den vanligaste typen. Angriparen injicerar SQL-kod i frågeparametern och resultatet av injektionen syns direkt i serverns svar. Två undertyper: Error-based (via databasens felmeddelanden) och UNION-based (via UNION SELECT-operatorn). Error-based använder information från felmeddelanden, till exempel ett MySQL-syntaxfel som kan avslöja tabellnamnet eller frågestrukturen. UNION-based gör det möjligt att kombinera resultaten från en legitim fråga med data från andra databastabeller.
Används när applikationen inte visar frågeresultat eller felmeddelanden. Angriparen ställer frågor av typen "ja/nej" genom att skicka frågor med logiska villkor och analysera skillnaden i serverns svar (t.ex. svarstid eller sidinnehåll). Time-based Blind SQLi använder en fördröjningsfunktion (SLEEP, WAITFOR DELAY) för att bekräfta villkor — om sidan laddas längre är villkoret sant. Denna metod är mycket långsam — att extrahera en enda post kan ta timmar.
# Exempel på Blind SQL Injection (time-based)
# Om SQLi är sårbar utförs SLEEP(2) under villkor
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
# Om svaret kom efter >2 sekunder — första bokstaven i lösenordet är 'a'
print(f"Time: {elapsed:.2f}s - First letter is 'a'" if elapsed > 2 else "First letter is not 'a'")
Data överförs inte via HTTP-svaret utan via alternativa kanaler: DNS-frågor, HTTP-förfrågningar till en extern server, SMTP. Används när applikationen inte returnerar frågeresultat och inte visar fel. MySQL stöder funktionen LOAD_FILE(), som kan initiera en DNS-fråga, och MSSQL — xp_dirtree för att skicka data till en fjärr-SMB-server. Out-of-band SQL Injection är effektivt men kräver ytterligare konfiguration på angriparens server och närvaro av specifika databasfunktioner.
Mekanismen för SQL Injection bygger på att SQL använder citattecken för strängliteral. Om applikationen substituerar användarinmatning direkt i SQL-frågan utan escaping, kan angriparen "stänga" strängen och lägga till godtycklig SQL-kod. Till exempel i frågan SELECT * FROM users WHERE name = '$input' omvandlar substitueringen ' OR '1'='1 frågan till SELECT * FROM users WHERE name = '' OR '1'='1', vilket returnerar alla användare.
Betrakta ett inloggningsformulär med frågan SELECT * FROM users WHERE username = '$user' AND password = '$pass'. Om angriparen anger i användarnamnsfältet admin' -- och lämnar lösenordet tomt, blir den resulterande frågan SELECT * FROM users WHERE username = 'admin' -- ' AND password = ''. Tecknen -- kommenterar resten av frågan och villkoret för lösenordskontroll inaktiveras. Servern returnerar posten för användaren admin och angriparen loggar in utan att känna till lösenordet.
# Exempel på SQL Injection — kringgående av autentisering
# SÅRBAR KOD: direkt strängkonkatenering
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' --" upphäver lösenordskontrollen
# SÄKER VERSION: parametriserad fråga
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
Operatorn UNION SELECT gör det möjligt att kombinera resultaten från två SELECT-frågor. Om angriparen hittar en sårbar parameter lägger han till UNION SELECT med en fråga från en annan tabell. Till exempel: ' UNION SELECT username, password FROM admins --. Villkor för framgång — antalet kolumner i båda frågorna måste matcha. Antalet kolumner bestäms via ORDER BY (substituering av ' ORDER BY 1--, sedan 2, 3... tills fel). När angriparen känner till antalet kolumner substituerar han UNION SELECT med samma antal fält.
Mobilapplikationer stöter på SQL Injection både på serversidan (API) och på klientsidan — i lokala databaser (SQLite, Realm). Även om risken för server-SQLi i mobilt API liknar webbapplikationer, skapar lokal databas en extra vektor. Om applikationen lagrar data i SQLite och utför frågor med strängkonkatenering, kan skadlig data som kommit in i den lokala databasen via API orsaka SQLi vid efterföljande bearbetning.
Den lokala SQLite-databasen på enheten är också sårbar för SQL Injection om applikationen skapar frågor genom strängkonkatenering. Content Providers på Android och Core Data på iOS använder parametrisering som standard, men råa frågor kräver utvecklarens uppmärksamhet. SQLite stöder inte flera frågor via semikolon, vilket begränsar angriparens möjligheter men skyddar inte mot läsning av data via WHERE-villkor. Använd alltid selectionArgs i Android och NSPredicate med parametrar i iOS.
API:et som mobilapplikationen ansluter till är lika sårbart som vilken webbserver som helst. Mobilutvecklare tror ofta att SQLi bara är ett backend-problem, men sårbarheten uppstår på API-endpoint-nivå som tar emot parametrar från klienten. Ansvarsfördelning skyddar inte: om backend-utvecklaren glömde att parametrisera frågan blir användarens mobilapplikation en attackvektor. Kräv att backend använder ORM eller prepared statements.
// Exempel på SQL Injection i lokal SQLite på Android
// SÅRBAR KOD: direkt konkatenering
fun getUserVulnerable(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = $userId", null
)
}
// SÄKER VERSION: parametrisering via selectionArgs
fun getUserSecure(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = ?",
arrayOf(userId)
)
}
Skydd mot SQL Injection bygger på en enkel princip: lita aldrig på användarinmatning i SQL-frågor. Den enda pålitliga metoden är parametrisering av frågor (prepared statements), där SQL-kod och data skickas separat. Alla andra metoder — escaping, validering, WAF — är extra skyddslager men ersätter inte parametrisering. Enligt uppgifter från OWASP (2025) förhindrar parametrisering 100% av SQL Injection-attackerna.
När prepared statements används kompileras SQL-frågan först av databasservern utan data, och sedan skickas parametervärdena separat. Databasen behandlar parametrar som data, inte som exekverbar kod. Även om angriparen skickar ' OR '1'='1 tolkar databasen detta som ett strängvärde, inte som SQL-kod. Prepared statements stöds av alla moderna språk och ramverk: PDO i PHP, PreparedStatement i Java, cursor.execute i Python.
Moderna ORM (Hibernate, Entity Framework, SQLAlchemy, Room) använder automatiskt parametrisering när frågor utförs, om inte utvecklaren går över till råa frågor. ORM skyddar dock inte fullständigt: konstruktioner som @Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true) i JPA kräver att parametrar skickas via namngivna parametrar, inte via konkatenering. Query Builders (Knex, jOOQ) parametriserar också frågor som standard om inte råa metoder används.
Escaping av specialtecken (mysql_real_escape_string) — en föråldrad metod som inte skyddar mot alla typer av SQL Injection. Problemet: escaping beror på kodningen och kan kringgås med flerbyte-kodningar (t.ex. GBK i asiatiska system). Använd escaping endast i äldre kod där parametrisering inte är möjlig, och alltid i kombination med strikt validering av inmatningsdatatyp.
| Metod | Effektivitet | Rekommendation |
|---|---|---|
| Prepared Statements | 100% | Obligatoriskt för alla frågor |
| ORM (korrekt användning) | 99% | Rekommenderas |
| Strängescaping | 70% (beror på kodning) | Endast äldre kod |
| Inmatningsvalidering (vitlista) | 50% (endast för siffror) | Extra |
| WAF (Web Application Firewall) | 60% | Extra |
Applikationens konto bör ha minimalt nödvändiga rättigheter: SELECT, INSERT, UPDATE, DELETE — endast på tabeller som applikationen verkligen behöver. Förbjud användning av DROP, TRUNCATE, CREATE för applikationskontot. Detta begränsar skadan även vid en lyckad SQL Injection: angriparen kan inte ta bort tabeller eller utföra administrativa operationer.
Regelbunden testning för SQL Injection bör vara en del av pipelinen för säker utveckling. Kombinationen av statisk analys, dynamisk skanning och manuell penetrationstestning ger bäst resultat. Enligt Synopsys Cybersecurity Report (2025) hittar automatiska skannrar upp till 70% av SQLi-sårbarheterna, men komplexa Blind-attacker kräver manuell testning.
För mobilapplikationer är analys av lokal SQLite också viktigt: kontrollera alla rawQuery-anrop, ContentProvider-frågor och Room-frågor med rawQuery. Verktyg: Android Studio Lint (upptäcker SQLi i SQLite), MobSF (Mobile Security Framework) för automatisk statisk och dynamisk analys av APK/IPA. Det rekommenderas också att testa API-endpoints via sqlmap med proxyavlyssning av mobilapplikationstrafik.
Vanliga frågor
SQL Injection attackerar relationsdatabaser via SQL-frågor. NoSQL Injection påverkar icke-relationella databaser (MongoDB, Couchbase) via deras frågeoperatorer ($gte, $ne, $where). I MongoDB är injektion möjlig om applikationen skapar ett BSON-dokument från en JSON-sträng. Skyddsmekanismerna är likartade: parametrisering och typvalidering.
Hitta alla ställen där SQL-frågor skapas via strängkonkatenering med användardata. Leta efter mönster som "SELECT ... WHERE id = " + userId eller f"UPDATE ... SET name = '{name}'". Varje sådan sträng är en potentiell SQL-injektion. Ersätt alla med parametriserade frågor eller prepared statements.
ORM-ramverk skyddar automatiskt endast om du använder deras Query Builder-metoder och namngivna parametrar. Om du använder råa frågor (nativeQuery i JPA, rawQuery i Room) fungerar inte skyddet — du måste skicka parametrar via förberedda uttryck, inte via strängkonkatenering.
Second-Order SQL Injection är en attack där skadlig data lagras i databasen som säker men sedan används i en annan fråga utan escaping. Till exempel registrerar sig angriparen med användarnamnet ' OR '1'='1. Data lagras som en sträng — vid registrering sker ingen attack. Men om en annan fråga substituerar användarnamnet i SQL utan parametrisering aktiveras injektionen.
NoSQL Injection är potentiellt farligare på grund av lägre medvetenhet hos utvecklare. Utvecklare känner till SQL Injection och de flesta använder ORM, men få känner till NoSQL Injection. I MongoDB kan en felaktigt formulerad fråga returnera alla dokument i samlingen. Skydd — samma prepared statements (BSON-parametrisering) och strikt validering av inmatningsdata.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också