SQL-injek­tio­ner udgør en betydelig trussel mod re­la­tions­da­ta­ba­se­mo­del­ler og de følsomme op­lys­nin­ger, de in­de­hol­der. Derfor er det absolut nød­ven­digt med en om­fat­ten­de be­skyt­tel­se mod disse uau­to­ri­se­re­de forsøg på ekstern adgang, som mu­lig­gø­res af sik­ker­heds­svag­he­der.

Hvad er en SQL-injektion?

En SQL-injektion er en type angreb, der udnytter en sik­ker­heds­fejl i re­la­tions­da­ta­ba­se­sy­ste­mer, der bruger SQL-fo­re­spørgsels­spro­get til at behandle bru­ger­in­put. An­gri­be­ren udnytter bru­ger­in­put, der ikke er korrekt eskapet og in­de­hol­der spe­ci­al­tegn såsom dobbelte bin­de­stre­ger, an­før­sels­tegn eller se­mi­ko­lo­ner. Disse tegn har særlige funk­tio­ner for SQL-for­tol­ke­ren og gør det muligt at ma­ni­p­u­le­re de kom­man­do­er, der udføres, eksternt. SQL-injek­tio­ner er ofte forbundet med PHP- og ASP-ap­pli­ka­tio­ner, der er baseret på forældede græn­se­fla­der. I mange af disse tilfælde er inputtet ikke til­stræk­ke­ligt renset, hvilket gør det til et oplagt mål for et angreb.

Ved stra­te­gisk at indsætte funk­tions­tegn kan en uau­to­ri­se­ret bruger indsætte yder­li­ge­re SQL-kom­man­do­er og ma­ni­p­u­le­re da­ta­ba­sepo­ster for at læse, ændre eller slette data. I alvorlige tilfælde kan angribere endda få adgang til systemets kom­man­do­linje, hvilket kan give dem mulighed for at overtage fuld kontrol over da­ta­ba­se­ser­ve­ren.

Eksempel på SQL-injektion, der viser, hvordan et da­ta­ba­se­an­greb fungerer

Da sårbare da­ta­ba­se­ser­ve­re hurtigt kan iden­ti­fi­ce­res, og SQL-injek­tions­an­greb er relativt nemme at gen­nem­fø­re, er denne metode fortsat en af de mest udbredte teknikker blandt cy­ber­kri­mi­nel­le verden over. Angribere anvender for­skel­li­ge stra­te­gi­er, hvor de udnytter både ny­op­da­ge­de og velkendte sik­ker­heds­fejl i de ap­pli­ka­tio­ner, der indgår i da­ta­hånd­te­rings­pro­ces­sen. For bedre at forstå, hvordan SQL-injektion fungerer i praksis, skal vi se på to al­min­de­li­ge an­grebs­me­to­der som eksempler.

Eksempel 1: Adgang via bru­ger­in­put med util­stræk­ke­lig 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 in­de­hol­der felter til bru­ger­navn og ad­gangs­ko­de. Brugerne udfylder for­mu­la­ren, og scriptet kon­trol­le­rer derefter, om der findes matchende poster i databasen. Som standard kan databasen indeholde en tabel ved navn users med ko­lon­ner­ne username og password. I en typisk we­bap­pli­ka­tion kan de relevante script­linjer til da­ta­ba­se­ad­gang (ved hjælp af Python-lignende pseu­do­ko­de) 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)
python

En hacker kan nu ma­ni­p­u­le­re ad­gangs­ko­de­fel­tet ved hjælp af en SQL-injektion, for eksempel ved at indtaste password' OR 1='1, hvilket re­sul­te­rer i følgende SQL-fo­re­spørgsel:

sql = "SELECT id FROM users WHERE username='' AND password='password' OR 1='1'"
python

Dette giver an­gri­be­ren fuld adgang til hele bru­gerta­bel­len i databasen, da be­tin­gel­sen ved­rø­ren­de ad­gangs­ko­den altid vurderes til at være sand (1='1'). Hvis an­gri­be­ren logger ind som ad­mi­ni­stra­tor, kan ved­kom­men­de frit ændre alle poster i databasen. Al­ter­na­tivt kan feltet for bru­ger­navn ma­ni­p­u­le­res på samme måde.

Eksempel 2: Da­ta­ud­træk via ma­ni­pu­la­tion af ID-numre

At hente op­lys­nin­ger 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 op­lys­nin­ger den skal hente fra databasen. Det til­hø­ren­de 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);
    }
?>
php

Den for­ven­te­de URL følger mønsteret .../script.php?id=22. I dette tilfælde vil den tabelpost med ID’et »22« blive hentet. Hvis en uau­to­ri­se­ret person har mulighed for at ma­ni­p­u­le­re denne URL og i stedet sender en anmodning som .../script.php?id=22+OR+1=1, vil den re­sul­te­ren­de fo­re­spørgsel medføre, at alle rækker i tabellen hentes:

SELECT * FROM table WHERE id=22 OR 1=1;
sql

Hvordan finder kri­mi­nel­le sårbare da­ta­ba­se­sy­ste­mer?

I prin­cip­pet kan enhver hjem­mesi­de eller we­bap­pli­ka­tion, der bruger SQL-databaser uden for­be­red­te fo­re­spørgs­ler (prepared sta­te­ments) eller andre be­skyt­tel­ses­for­an­stalt­nin­ger, være sårbar over for SQL-injek­tio­ner. Opdagede sår­bar­he­der forbliver ikke skjulte længe på in­ter­net­tet. Der findes faktisk hjem­mesi­der, der of­fent­lig­gør op­da­te­re­de lister over kendte sik­ker­heds­fejl – og endda forklarer, hvordan angribere kan bruge Google-søgninger til at finde relevante webpro­jek­ter. Hvis et websted re­tur­ne­rer de­tal­je­re­de SQL-fejl­med­del­el­ser, kan cy­ber­kri­mi­nel­le bruge disse med­del­el­ser til at iden­ti­fi­ce­re po­ten­ti­el­le sår­bar­he­der. For eksempel kan til­fø­jel­sen af en apostrof i slut­nin­gen af en URL, der in­de­hol­der en ID-parameter, allerede afsløre en svaghed, som vist i følgende eksempel:

[DomainName].com/news.php?id=5’

Et sårbart websted re­tur­ne­rer en fejl­med­del­el­se 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 ko­lon­ne­nav­ne, SQL-versionen eller endda bru­ger­nav­ne og ad­gangs­ko­der. Der findes desuden for­skel­li­ge værktøjer, der kan au­to­ma­ti­se­re både op­da­gel­ses­pro­ces­sen og ud­fø­rel­sen af SQL-injek­tions­an­greb.

Sådan beskytter du din database mod SQL-injektion

Der findes for­skel­li­ge metoder, du kan anvende for at forhindre SQL-injek­tions­an­greb på dit da­ta­ba­se­sy­stem. Du bør tage højde for alle de in­vol­ve­re­de kom­po­nen­ter – både serveren og de enkelte ap­pli­ka­tio­ner samt da­ta­ba­sesty­rings­sy­ste­met.

Trin 1: Overvåg au­to­ma­ti­ske ind­tast­nin­ger fra ap­pli­ka­tio­ner

Når man behandler ind­tast­nin­ger fra eksterne eller ind­byg­ge­de ap­pli­ka­tio­ner, er det afgørende at validere og filtrere de indsendte værdier for at forhindre SQL-injek­tio­ner.

1. Valider datatyper

Hver ind­tast­ning skal svare til den for­ven­te­de datatype. Hvis der f.eks. kræves en numerisk ind­tast­ning, kan en simpel va­li­de­ring i PHP se sådan ud:

if (filter_var($input, FILTER_VALIDATE_INT) === false) {
    throw new InvalidArgumentException("Invalid input");
}
php

Der bør indføres til­sva­ren­de kon­trol­ler for streng­ty­per, datoer eller andre spe­ci­fik­ke formater.

2. Filtrer spe­ci­al­tegn

Særtegn kan udgøre en sik­ker­heds­ri­si­ko, især i for­bin­del­se med SQL eller HTML. En sikker frem­gangs­må­de er at anvende htmlspecialchars() til HTML-ind­tast­nin­ger og PDO::quote() til SQL-fo­re­spørgs­ler.

3. Undgå at vise fejl­med­del­el­ser

Man bør undgå direkte fejl­med­del­el­ser, der afslører tekniske detaljer om databasen eller systemet. Vis i stedet en generel med­del­el­se, f.eks.:

echo "An error occurred. Please try again later.";
error_log("Unexpected error encountered. See system log for details.");
php

4. Brug for­ud­de­fi­ne­re­de sætninger

En af de mest effektive måder at forhindre SQL-injek­tio­ner på er ved at bruge for­be­red­te sætninger. Ved denne frem­gangs­må­de sendes SQL-kom­man­do­er og parametre hver for sig, så ondsindet kode ikke kan udføres. Her er et eksempel på en im­ple­men­te­ring 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();
php

Da­ta­ba­sesty­rings­sy­ste­met sikrer au­to­ma­tisk, at ind­tast­nin­ger­ne behandles sikkert.

Trin 2: Sørg for om­fat­ten­de ser­ver­be­skyt­tel­se

Sik­ker­he­den på den server, hvor dit da­ta­ba­sesty­rings­sy­stem kører, spiller også en afgørende rolle i fore­byg­gel­sen af SQL-injektion. En vigtig for­an­stalt­ning er at styrke ope­ra­tiv­sy­ste­mets sikkerhed ved at følge disse an­be­fa­lin­ger:

  • Installer eller aktiver kun de pro­gram­mer og tjenester, der er nød­ven­di­ge for at køre databasen.
  • Fjern alle ubrugte eller unød­ven­di­ge bru­ger­kon­ti.
  • Sørg for, at alle relevante system- og softwa­re­op­da­te­rin­ger in­stal­le­res straks.
  • Anvend prin­cip­pet om mindst mulig adgang for at sikre, at brugere og tjenester kun tildeles de minimalt nød­ven­di­ge til­la­del­ser.

Afhængigt af sik­ker­heds­kra­ve­ne til dit webpro­jekt bør du overveje yder­li­ge­re sik­ker­heds­for­an­stalt­nin­ger:

  • Intrusion Detection Systems (IDS) og Intrusion Pre­ven­tion Systems (IPS): Disse systemer anvender for­skel­li­ge de­tek­te­rings­me­to­der til at iden­ti­fi­ce­re angreb på et tidligt tidspunkt, udsende advarsler og – når der anvendes IPS – au­to­ma­tisk iværk­sæt­te mod­for­an­stalt­nin­ger.
  • Ap­pli­ca­tion Layer Gateway (ALG): En ALG overvåger og filtrerer trafikken mellem ap­pli­ka­tio­ner og web­brow­se­re direkte på ap­pli­ka­tions­ni­veau.
  • Web Ap­pli­ca­tion Firewall (WAF): En WAF beskytter specifikt we­bap­pli­ka­tio­ner mod SQL-injektion og Cross-Site Scripting (XSS) ved at blokere eller rense mistæn­ke­li­ge an­mod­nin­ger.
  • Zero Trust-tilgang: Denne moderne sik­ker­heds­mo­del sikrer, at hvert eneste ad­gangs­for­søg – uanset dets op­rin­del­se – ve­ri­fi­ce­res og au­ten­ti­fi­ce­res, før der gives adgang.
  • Firewall-regler og net­værks­seg­men­te­ring: Disse er afgørende for at minimere an­grebs­fla­den på lang sigt.
  • Re­gel­mæs­si­ge IT-sik­ker­heds­re­vi­sio­ner og pe­ne­tra­tions­tests: Disse hjælper med at opdage og rette sår­bar­he­der på et tidligt stadium.

Trin 3: Sik­ker­heds­op­ti­mer databasen og brug sikker kode

Ligesom dit ope­ra­tiv­sy­stem bør din database renses for alle unød­ven­di­ge kom­po­nen­ter og holdes opdateret. Fjern alle gemte pro­ce­du­rer, du ikke har brug for, og deaktiver alle ubrugte tjenester og bru­ger­kon­ti. Opret en dedikeret da­ta­ba­se­kon­to, der ude­luk­ken­de er beregnet til webadgang, og tildel den kun de absolut nød­ven­di­ge ret­tig­he­der.

I tråd med brugen af for­be­red­te sætninger anbefales det på det kraf­tig­ste 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 kom­pa­ti­bi­li­tet. 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();
php

Ad­gangs­ko­der bør aldrig gemmes direkte i en database eller hentes i klartekst. Brug i stedet en hashing-metode som password_hash() i kom­bi­na­tion med password_verify() for at beskytte bru­gero­p­lys­nin­ger­ne sikkert. En sikker im­ple­men­te­ring 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.";
}
php

Hvad har klapborde med SQL-injektion at gøre?

Hjem­mesi­den bobby-tables.com bruger en xkcd-web­teg­ne­se­rie til på hu­mo­ri­stisk vis at il­lu­stre­re farerne ved usikre bru­ger­ind­tast­nin­ger i databaser. I teg­ne­se­ri­en 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 elev­da­ta­ba­sen medførte, at hele elev­ta­bel­len blev slettet. Moderen er ikke særlig for­stå­en­de – hun håber blot, at skolen har lært sin lektie og fremover vil rense da­ta­ba­se­ind­tast­nin­ger­ne.

Teg­ne­se­ri­en il­lu­stre­rer tydeligt de ka­ta­stro­fa­le kon­se­kven­ser, der kan opstå, hvis man undlader at validere og rense bru­ger­in­put korrekt i da­ta­ba­se­ap­pli­ka­tio­ner.

Gå til ho­ved­me­nu­en