Hvad er en SQL-injektion?
SQL-injektioner udgør en betydelig trussel mod relationsdatabasemodeller og de følsomme oplysninger, de indeholder. Derfor er det absolut nødvendigt med en omfattende beskyttelse mod disse uautoriserede forsøg på ekstern adgang, som muliggøres af sikkerhedssvagheder.
Hvad er en SQL-injektion?
En SQL-injektion er en type angreb, der udnytter en sikkerhedsfejl i relationsdatabasesystemer, der bruger SQL-forespørgselssproget til at behandle brugerinput. Angriberen udnytter brugerinput, der ikke er korrekt eskapet og indeholder specialtegn såsom dobbelte bindestreger, anførselstegn eller semikoloner. Disse tegn har særlige funktioner for SQL-fortolkeren og gør det muligt at manipulere de kommandoer, der udføres, eksternt. SQL-injektioner er ofte forbundet med PHP- og ASP-applikationer, der er baseret på forældede grænseflader. I mange af disse tilfælde er inputtet ikke tilstrækkeligt renset, hvilket gør det til et oplagt mål for et angreb.
Ved strategisk at indsætte funktionstegn kan en uautoriseret bruger indsætte yderligere SQL-kommandoer og manipulere databaseposter for at læse, ændre eller slette data. I alvorlige tilfælde kan angribere endda få adgang til systemets kommandolinje, hvilket kan give dem mulighed for at overtage fuld kontrol over databaseserveren.
Eksempel på SQL-injektion, der viser, hvordan et databaseangreb fungerer
Da sårbare databaseservere hurtigt kan identificeres, og SQL-injektionsangreb er relativt nemme at gennemføre, er denne metode fortsat en af de mest udbredte teknikker blandt cyberkriminelle verden over. Angribere anvender forskellige strategier, hvor de udnytter både nyopdagede og velkendte sikkerhedsfejl i de applikationer, der indgår i datahåndteringsprocessen. For bedre at forstå, hvordan SQL-injektion fungerer i praksis, skal vi se på to almindelige angrebsmetoder som eksempler.
Eksempel 1: Adgang via brugerinput med utilstrækkelig eskaping
For at få adgang til en database skal brugerne som regel først godkende sig selv. Til dette formål bruges der ofte scripts til at vise en login-formular, der indeholder felter til brugernavn og adgangskode. Brugerne udfylder formularen, og scriptet kontrollerer derefter, om der findes matchende poster i databasen. Som standard kan databasen indeholde en tabel ved navn users med kolonnerne username og password. I en typisk webapplikation kan de relevante scriptlinjer til databaseadgang (ved hjælp af Python-lignende pseudokode) se således ud:
uname = request.POST['username']
passwd = request.POST['password']
sql = "SELECT id FROM users WHERE username='" + uname + "' AND password='" + passwd + "'"
database.execute(sql)pythonEn hacker kan nu manipulere adgangskodefeltet ved hjælp af en SQL-injektion, for eksempel ved at indtaste password' OR 1='1, hvilket resulterer i følgende SQL-forespørgsel:
sql = "SELECT id FROM users WHERE username='' AND password='password' OR 1='1'"pythonDette giver angriberen fuld adgang til hele brugertabellen i databasen, da betingelsen vedrørende adgangskoden altid vurderes til at være sand (1='1'). Hvis angriberen logger ind som administrator, kan vedkommende frit ændre alle poster i databasen. Alternativt kan feltet for brugernavn manipuleres på samme måde.
Eksempel 2: Dataudtræk via manipulation af ID-numre
At hente oplysninger fra en database ved hjælp af et ID er en praktisk og udbredt metode, men åbner også mulighed for SQL-injektion. For eksempel ved en webserver via et ID, der er angivet i en URL, hvilke oplysninger den skal hente fra databasen. Det tilhørende PHP-script ser således ud:
<?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 forventede URL følger mønsteret .../script.php?id=22. I dette tilfælde vil den tabelpost med ID’et »22« blive hentet. Hvis en uautoriseret person har mulighed for at manipulere denne URL og i stedet sender en anmodning som .../script.php?id=22+OR+1=1, vil den resulterende forespørgsel medføre, at alle rækker i tabellen hentes:
SELECT * FROM table WHERE id=22 OR 1=1;sqlHvordan finder kriminelle sårbare databasesystemer?
I princippet kan enhver hjemmeside eller webapplikation, der bruger SQL-databaser uden forberedte forespørgsler (prepared statements) eller andre beskyttelsesforanstaltninger, være sårbar over for SQL-injektioner. Opdagede sårbarheder forbliver ikke skjulte længe på internettet. Der findes faktisk hjemmesider, der offentliggør opdaterede lister over kendte sikkerhedsfejl – og endda forklarer, hvordan angribere kan bruge Google-søgninger til at finde relevante webprojekter. Hvis et websted returnerer detaljerede SQL-fejlmeddelelser, kan cyberkriminelle bruge disse meddelelser til at identificere potentielle sårbarheder. For eksempel kan tilføjelsen af en apostrof i slutningen af en URL, der indeholder en ID-parameter, allerede afsløre en svaghed, som vist i følgende eksempel:
[DomainName].com/news.php?id=5’Et sårbart websted returnerer en fejlmeddelelse i følgende form:
Query failed: You have an error in your SQL syntax…
Lignende metoder kan også bruges til at udtrække antallet af kolonner, tabel- og kolonnenavne, SQL-versionen eller endda brugernavne og adgangskoder. Der findes desuden forskellige værktøjer, der kan automatisere både opdagelsesprocessen og udførelsen af SQL-injektionsangreb.
Sådan beskytter du din database mod SQL-injektion
Der findes forskellige metoder, du kan anvende for at forhindre SQL-injektionsangreb på dit databasesystem. Du bør tage højde for alle de involverede komponenter – både serveren og de enkelte applikationer samt databasestyringssystemet.
Trin 1: Overvåg automatiske indtastninger fra applikationer
Når man behandler indtastninger fra eksterne eller indbyggede applikationer, er det afgørende at validere og filtrere de indsendte værdier for at forhindre SQL-injektioner.
1. Valider datatyper
Hver indtastning skal svare til den forventede datatype. Hvis der f.eks. kræves en numerisk indtastning, kan en simpel validering i PHP se sådan ud:
if (filter_var($input, FILTER_VALIDATE_INT) === false) {
throw new InvalidArgumentException("Invalid input");
}phpDer bør indføres tilsvarende kontroller for strengtyper, datoer eller andre specifikke formater.
2. Filtrer specialtegn
Særtegn kan udgøre en sikkerhedsrisiko, især i forbindelse med SQL eller HTML. En sikker fremgangsmåde er at anvende htmlspecialchars() til HTML-indtastninger og PDO::quote() til SQL-forespørgsler.
3. Undgå at vise fejlmeddelelser
Man bør undgå direkte fejlmeddelelser, der afslører tekniske detaljer om databasen eller systemet. Vis i stedet en generel meddelelse, f.eks.:
echo "An error occurred. Please try again later.";
error_log("Unexpected error encountered. See system log for details.");php4. Brug foruddefinerede sætninger
En af de mest effektive måder at forhindre SQL-injektioner på er ved at bruge forberedte sætninger. Ved denne fremgangsmåde sendes SQL-kommandoer og parametre hver for sig, så ondsindet kode ikke kan udføres. Her er et eksempel på en implementering i PHP ved hjælp af PDO (PHP Data Objects):
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->bindParam(':id', $user_id, PDO::PARAM_INT);
$stmt->execute();phpDatabasestyringssystemet sikrer automatisk, at indtastningerne behandles sikkert.
Trin 2: Sørg for omfattende serverbeskyttelse
Sikkerheden på den server, hvor dit databasestyringssystem kører, spiller også en afgørende rolle i forebyggelsen af SQL-injektion. En vigtig foranstaltning er at styrke operativsystemets sikkerhed ved at følge disse anbefalinger:
- Installer eller aktiver kun de programmer og tjenester, der er nødvendige for at køre databasen.
- Fjern alle ubrugte eller unødvendige brugerkonti.
- Sørg for, at alle relevante system- og softwareopdateringer installeres straks.
- Anvend princippet om mindst mulig adgang for at sikre, at brugere og tjenester kun tildeles de minimalt nødvendige tilladelser.
Afhængigt af sikkerhedskravene til dit webprojekt bør du overveje yderligere sikkerhedsforanstaltninger:
- Intrusion Detection Systems (IDS) og Intrusion Prevention Systems (IPS): Disse systemer anvender forskellige detekteringsmetoder til at identificere angreb på et tidligt tidspunkt, udsende advarsler og – når der anvendes IPS – automatisk iværksætte modforanstaltninger.
- Application Layer Gateway (ALG): En ALG overvåger og filtrerer trafikken mellem applikationer og webbrowsere direkte på applikationsniveau.
- Web Application Firewall (WAF): En WAF beskytter specifikt webapplikationer mod SQL-injektion og Cross-Site Scripting (XSS) ved at blokere eller rense mistænkelige anmodninger.
- Zero Trust-tilgang: Denne moderne sikkerhedsmodel sikrer, at hvert eneste adgangsforsøg – uanset dets oprindelse – verificeres og autentificeres, før der gives adgang.
- Firewall-regler og netværkssegmentering: Disse er afgørende for at minimere angrebsfladen på lang sigt.
- Regelmæssige IT-sikkerhedsrevisioner og penetrationstests: Disse hjælper med at opdage og rette sårbarheder på et tidligt stadium.
Trin 3: Sikkerhedsoptimer databasen og brug sikker kode
Ligesom dit operativsystem bør din database renses for alle unødvendige komponenter og holdes opdateret. Fjern alle gemte procedurer, du ikke har brug for, og deaktiver alle ubrugte tjenester og brugerkonti. Opret en dedikeret databasekonto, der udelukkende er beregnet til webadgang, og tildel den kun de absolut nødvendige rettigheder.
I tråd med brugen af forberedte sætninger anbefales det på det kraftigste ikke at bruge mysql PHP-modulet (som blev fjernet i PHP 7). Vælg i stedet mysqli eller PDO for at sikre bedre sikkerhed og kompatibilitet. En sikker mysqli kan se sådan ud:
$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();phpAdgangskoder bør aldrig gemmes direkte i en database eller hentes i klartekst. Brug i stedet en hashing-metode som password_hash() i kombination med password_verify() for at beskytte brugeroplysningerne sikkert. En sikker implementering kan se sådan ud:
$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.";
}phpHvad har klapborde med SQL-injektion at gøre?
Hjemmesiden bobby-tables.com bruger en xkcd-webtegneserie til på humoristisk vis at illustrere farerne ved usikre brugerindtastninger i databaser. I tegneserien modtager en mor et opkald fra sin søns (kært kaldet Little Bobby Tables) skole. Den, der ringer, spørger, om hendes søn virkelig hedder Robert'); DROP TABLE Students;--, hvilket hun bekræfter. Årsagen til opkaldet bliver snart klar: et forsøg på at indtaste Robert i elevdatabasen medførte, at hele elevtabellen blev slettet. Moderen er ikke særlig forstående – hun håber blot, at skolen har lært sin lektie og fremover vil rense databaseindtastningerne.
Tegneserien illustrerer tydeligt de katastrofale konsekvenser, der kan opstå, hvis man undlader at validere og rense brugerinput korrekt i databaseapplikationer.