Vad är en SQL-injektion?
SQL-injektioner utgör ett betydande hot mot relationsdatabasmodeller och den känsliga information de innehåller. Därför är ett omfattande skydd mot dessa obehöriga externa åtkomstförsök – som möjliggörs av säkerhetsbrister – absolut nödvändigt.
Vad är en SQL-injektion?
En SQL-injektion är en typ av attack som utnyttjar en säkerhetsbrist i relationsdatabassystem som använder SQL-frågespråket för att bearbeta användarinmatning. Angriparen utnyttjar användarinmatningar som inte är korrekt skyddade och innehåller specialtecken som dubbla bindestreck, citattecken eller semikolon. Dessa tecken har specialfunktioner för SQL-tolken och gör det möjligt att manipulera kommandon som exekveras externt. SQL-injektioner är ofta förknippade med PHP- och ASP-applikationer som använder föråldrade gränssnitt. I många av dessa fall är inmatningen inte tillräckligt ren, vilket gör den till ett primärt mål för en attack.
Genom att strategiskt infoga funktionstecken kan en obehörig användare infoga ytterligare SQL-kommandon och manipulera databasposter för att läsa, ändra eller radera data. I allvarliga fall kan angripare till och med få tillgång till systemets kommandorad, vilket kan göra det möjligt för dem att ta full kontroll över databasservern.
Exempel på SQL-injektion som visar hur en databasattack fungerar
Eftersom sårbara databasserverar kan identifieras snabbt och SQL-injektionsattacker är relativt enkla att utföra, är denna metod fortfarande en av de mest använda teknikerna bland cyberbrottslingar världen över. Angripare använder olika strategier och utnyttjar både nyupptäckta och gamla säkerhetsbrister i de applikationer som används i datahanteringsprocessen. För att bättre förstå hur SQL-injektion fungerar i praktiken, ska vi titta på två vanliga attackmetoder som exempel.
Exempel 1: Åtkomst via dåligt skyddad användarinmatning
För att få åtkomst till en databas måste användarna vanligtvis först autentisera sig. För detta ändamål används ofta skript för att visa ett inloggningsformulär med fält för användarnamn och lösenord. Användarna fyller i formuläret och skriptet kontrollerar sedan om det finns matchande poster i databasen. Som standard kan databasen innehålla en tabell med namnet users med kolumnerna username och password. I en typisk webbapplikation kan de relevanta skriptraderna för databasåtkomst (med Python-liknande pseudokod) se ut så här:
uname = request.POST['username']
passwd = request.POST['password']
sql = "SELECT id FROM users WHERE username='" + uname + "' AND password='" + passwd + "'"
database.execute(sql)pythonEn angripare kan nu manipulera lösenordsfältet med hjälp av en SQL-injektion, till exempel genom att ange password' OR 1='1, vilket resulterar i följande SQL-fråga:
sql = "SELECT id FROM users WHERE username='' AND password='password' OR 1='1'"pythonDetta ger angriparen full åtkomst till hela användartabellen i databasen, eftersom lösenordsvillkoret alltid utvärderas till sant (1='1'). Om angriparen loggar in som administratör kan hen fritt ändra alla databasposter. Alternativt kan användarnamnsfältet manipuleras på samma sätt.
Exempel 2: Datautvinning via ID-manipulation
Att söka information från en databas med hjälp av ID är en praktisk och vanlig metod, men öppnar också en möjlig dörr för SQL-injektion. Till exempel vet en webbserver genom en överförd ID-uppgift i en URL vilken information den ska hämta från databasen. Motsvarande PHP-skript ser ut så här:
<?php
$mysqli = new mysqli("localhost", "username", "password", "database");
$id = intval($_GET['id']);
$result = $mysqli->query("SELECT * FROM table WHERE id=$id");
while ($row = $result->fetch_assoc()) {
echo print_r($row, true);
}
?>phpDen förväntade URL:en följer mönstret .../script.php?id=22. I detta fall skulle tabellposten med ID:t ”22” hämtas. Om en obehörig person har möjlighet att manipulera denna URL och istället skickar en begäran som .../script.php?id=22+OR+1=1, kommer den resulterande frågan att leda till att alla rader i tabellen hämtas:
SELECT * FROM table WHERE id=22 OR 1=1;sqlHur hittar brottslingar sårbara databassystem?
I princip kan alla webbplatser eller webbapplikationer som använder SQL-databaser utan förberedda frågor (förberedda uttalanden) eller andra skyddsåtgärder vara sårbara för SQL-injektioner. Upptäckta sårbarheter förblir inte dolda länge på internet. Det finns faktiskt webbplatser som publicerar uppdaterade listor över kända säkerhetsbrister – och till och med förklarar hur angripare kan använda Google-sökningar för att hitta matchande webbprojekt. Om en webbplats returnerar detaljerade SQL-felmeddelanden kan cyberbrottslingar använda dessa meddelanden för att identifiera potentiella sårbarheter. Till exempel kan det räcka med att lägga till en apostrof i slutet av en URL som innehåller en ID-parameter för att avslöja en svaghet, som visas i följande exempel:
[DomainName].com/news.php?id=5’En sårbar webbplats skickar tillbaka ett felmeddelande i följande form:
Query failed: You have an error in your SQL syntax…
Liknande metoder kan också användas för att extrahera antalet kolumner, tabell- och kolumnnamn, SQL-versionen eller till och med användarnamn och lösenord. Dessutom finns det olika verktyg som kan automatisera både upptäcktsprocessen och utförandet av SQL-injektionsattacker.
Hur du skyddar din databas från SQL-injektion
Det finns olika metoder som du kan använda för att förhindra SQL-injektionsattacker på ditt databassystem. Du bör hantera alla komponenter som är inblandade – servern och enskilda applikationer samt databashanteringssystemet.
Steg 1: Övervaka automatiserade inmatningar från applikationer
När man bearbetar indata från externa eller inbäddade applikationer är det viktigt att validera och filtrera de inlämnade värdena för att förhindra SQL-injektioner.
1. Validera datatyper
Varje inmatning ska matcha den förväntade datatypen. Om till exempel en numerisk inmatning krävs kan en enkel validering i PHP se ut så här:
if (filter_var($input, FILTER_VALIDATE_INT) === false) {
throw new InvalidArgumentException("Invalid input");
}phpLiknande kontroller bör genomföras för strängar, datum eller andra specifika format.
2. Filtrera specialtecken
Specialtecken kan skapa säkerhetsrisker, särskilt i SQL- eller HTML-sammanhang. Ett säkert tillvägagångssätt är att använda htmlspecialchars() för HTML-inmatning och PDO::quote() för SQL-frågor.
3. Undvik att visa felmeddelanden
Direkta felmeddelanden som avslöjar tekniska detaljer om databasen eller systemet bör undvikas. Visa istället ett generellt meddelande, till exempel:
echo "An error occurred. Please try again later.";
error_log("Unexpected error encountered. See system log for details.");php4. Använd förberedda uttalanden
Ett av de mest effektiva sätten att förhindra SQL-injektioner är att använda förberedda satser. I denna metod skickas SQL-kommandon och parametrar separat, så att skadlig kod inte kan köras. Här är ett exempel på implementering i PHP med PDO (PHP Data Objects):
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->bindParam(':id', $user_id, PDO::PARAM_INT);
$stmt->execute();phpDatabashanteringssystemet säkerställer automatiskt att inmatningen behandlas på ett säkert sätt.
Steg 2: Säkerställ omfattande serverskydd
Säkerheten för den server som ditt databashanteringssystem körs på spelar också en avgörande roll för att förhindra SQL-injektion. En viktig åtgärd är att stärka operativsystemet genom att följa dessa bästa praxis:
- Installera eller aktivera endast de program och tjänster som är nödvändiga för att köra databasen.
- Ta bort alla oanvända eller onödiga användarkonton.
- Se till att alla relevanta system- och programuppdateringar installeras omedelbart.
- Tillämpa principen om minsta möjliga behörighet för att säkerställa att användare och tjänster endast beviljas de minsta nödvändiga behörigheterna.
Beroende på säkerhetskraven för ditt webbprojekt bör du överväga ytterligare skyddsåtgärder:
- Intrusion Detection Systems (IDS) och Intrusion Prevention Systems (IPS): Dessa system använder olika detekteringsmetoder för att identifiera attacker tidigt, utfärda varningar och – när IPS används – automatiskt initiera motåtgärder.
- Application Layer Gateway (ALG): En ALG övervakar och filtrerar trafiken mellan applikationer och webbläsare direkt på applikationsnivå.
- Web Application Firewall (WAF): En WAF skyddar specifikt webbapplikationer från SQL-injektion och Cross-Site Scripting (XSS) genom att blockera eller rensa misstänkta förfrågningar.
- Zero Trust-metod: Denna moderna säkerhetsmodell säkerställer att varje åtkomstförsök – oavsett ursprung – verifieras och autentiseras innan åtkomst beviljas.
- Brandväggsregler och nätverkssegmentering: Dessa är viktiga för att minimera attackytan på lång sikt.
- Regelbundna IT-säkerhetsrevisioner och penetrationstester: Dessa hjälper till att upptäcka och åtgärda sårbarheter i ett tidigt skede.
Steg 3: Hårda databasen och använd säker kod
Precis som ditt operativsystem bör din databas rensas från alla onödiga komponenter och hållas uppdaterad. Ta bort alla lagrade procedurer som du inte behöver och inaktivera alla oanvända tjänster och användarkonton. Skapa ett dedikerat databaskonto som endast är avsett för webbåtkomst och tilldela det endast de minsta nödvändiga behörigheterna.
I linje med användningen av förberedda uttalanden rekommenderas det starkt att inte använda mysql PHP-modulen (som togs bort i PHP 7). Välj istället mysqli eller PDO för att säkerställa bättre säkerhet och kompatibilitet. En säker mysqli kan se ut så här:
$mysqli = new mysqli("localhost", "username", "password", "database");
if ($mysqli->connect_error) die("Connection failed");
$stmt = $mysqli->prepare("SELECT password FROM users WHERE username = ?");
$stmt->bind_param("s", $_POST['username']);
$stmt->execute();
$stmt->bind_result($hashedPassword);
if ($stmt->fetch() && password_verify($_POST['password'], $hashedPassword)) {
echo "Login successful";
} else {
echo "Invalid login credentials";
}
$stmt->close();
$mysqli->close();phpLösenord ska aldrig lagras direkt i en databas eller hämtas i klartext. Använd istället en hashmetod som password_hash() i kombination med password_verify() för att skydda inloggningsuppgifterna på ett säkert sätt. En säker implementering kan se ut så här:
$mysqli = new mysqli("localhost", "username", "password", "database");
$stmt = $mysqli->prepare("SELECT password FROM users WHERE username = ?");
$stmt->bind_param("s", $_POST['username']);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
if ($row && password_verify($_POST['password'], $row['password'])) {
echo "Login successful!";
} else {
echo "Incorrect username or password.";
}phpVad har bobby-tabeller med SQL-injektion att göra?
Webbplatsen bobby-tables.com använder en xkcd-webbkomisk för att på ett humoristiskt sätt illustrera farorna med osäker användarinmatning i databaser. I komiken får en mamma ett telefonsamtal från sin sons (kärleksfullt kallad Little Bobby Tables) skola. Den som ringer frågar om hennes son verkligen heter Robert'); DROP TABLE Students;--, vilket hon svarar ja på. Anledningen till samtalet blir snart klar: försöket att lägga in Robert i elevdatabasen ledde till att hela elevtabellen raderades. Mamman är inte särskilt förstående – hon hoppas bara att skolan har lärt sig sin läxa och kommer att rensa databasen framöver.
Serien belyser tydligt de katastrofala konsekvenser som kan uppstå om man inte validerar och sanerar användarinmatningar i databasapplikationer på rätt sätt.