← Tilbage til security guides

SQL Injection Prevention

SQL injection er en af de mest kritiske sikkerhedstrusler, hvor angribere kan manipulere database-forespørgsler ved at indsætte ondsindet SQL-kode.

🚨Critical Severity
⚠️

Om Truslen

SQL injection opstår når uvalideret brugerinput inkluderes direkte i SQL-forespørgsler, hvilket giver angribere mulighed for at modificere eller udføre arbitrære database-kommandoer. Ifølge OWASP Top 10 er injection-angreb konsekvent blandt de mest kritiske sikkerhedstrusler. En succesfuld SQL injection kan resultere i komplet database-kompromittering, herunder datatyveri, datamanipulation, adgangskontrol-bypass og i nogle tilfælde remote code execution på databaseserveren. Studier viser at over 65% af webapplikationer er sårbare over for SQL injection i en eller anden form. Konsekvenserne kan være katastrofale for både virksomheder og brugere, med potentielt tab af følsomme data, økonomiske tab og betydelig reputationsskade.

Key Points

  • Brug altid prepared statements med parameteriserede queries
  • Brug aldrig streng-konkatenation til at bygge SQL-queries med brugerinput
  • Implementer whitelist input-validering for alle brugerinput
  • Brug ORM (Object-Relational Mapping) frameworks der håndterer parameterisering automatisk
  • Begræns database-brugerrettigheder til minimum nødvendigt (principle of least privilege)
  • Undgå at vise detaljerede database-fejlmeddelelser til slutbrugere
  • Implementer Web Application Firewall (WAF) til at detektere SQL injection-forsøg
  • Escape special characters hvis prepared statements ikke er mulige (sidste udvej)
  • Udfør regelmæssige sikkerhedsaudits og penetration testing
  • Brug stored procedures som et ekstra lag af beskyttelse (men ikke som primær forsvar)
  • Implementer input-længdebegrænsninger for at reducere attack surface
  • Log og monitorer unormale database-forespørgsler
  • Opdater regelmæssigt database-software og PHP til nyeste sikkerhedspatches
  • Brug separate database-konti for forskellige applikationsniveauer

Sårbar Kode (UNDGÅ)

Brug ALDRIG denne kode i produktion!

<?php
// SÅRBAR KODE - Brug ALDRIG dette i produktion!

// Direkte streng-konkatenation med brugerinput
$username = $_POST['username'];
$password = $_POST['password'];

// Ekstrem sårbarhed: Ingen sanitering eller escaping
$query = "SELECT * FROM users WHERE username = '" . $username . "' AND password = '" . $password . "'";
$result = mysqli_query($conn, $query);

if (mysqli_num_rows($result) > 0) {
    echo "Login successful!";
    // Attacker kan bruge: ' OR '1'='1 som username
    // Resulterende query: SELECT * FROM users WHERE username = '' OR '1'='1' AND password = ''
    // Dette vil altid returnere true og give adgang
} else {
    echo "Login failed!";
}

// Endnu et sårbart eksempel: Søgefunktion
$search = $_GET['search'];
$query = "SELECT * FROM products WHERE name LIKE '%$search%'";
$result = mysqli_query($conn, $query);

while ($row = mysqli_fetch_assoc($result)) {
    echo $row['name'] . "<br>";
}
// Attacker kan bruge: %' UNION SELECT username, password FROM users--
// Dette vil afsløre alle brugernavne og passwords

// Sårbar til data manipulation
$user_id = $_GET['id'];
$query = "DELETE FROM users WHERE id = $user_id";
mysqli_query($conn, $query);
// Attacker kan bruge: 1 OR 1=1
// Dette vil slette ALLE brugere!

// Sårbar til information disclosure
$category = $_GET['category'];
$query = "SELECT * FROM articles WHERE category = '$category'";
$result = mysqli_query($conn, $query);

if (!$result) {
    // Viser detaljerede fejlmeddelelser
    die("Database error: " . mysqli_error($conn));
}
// Fejlmeddelelser kan afsløre database-struktur
?>

Sikker Kode (ANBEFALET)

Best practices for sikker implementation

<?php
// SIKKER KODE - Best practices for SQL injection prevention

// 1. Prepared Statements med MySQLi
$username = $_POST['username'];
$password = $_POST['password'];

// Prepared statement med placeholders
$stmt = $conn->prepare("SELECT id, username, password_hash FROM users WHERE username = ?");
$stmt->bind_param("s", $username); // "s" for string type
$stmt->execute();
$result = $stmt->get_result();

if ($result->num_rows === 1) {
    $user = $result->fetch_assoc();
    // Brug password_verify() til at checke hashed passwords
    if (password_verify($password, $user['password_hash'])) {
        echo "Login successful!";
        $_SESSION['user_id'] = $user['id'];
    } else {
        echo "Invalid credentials";
    }
} else {
    echo "Invalid credentials";
}
$stmt->close();

// 2. Prepared Statements med PDO (anbefalet)
try {
    $pdo = new PDO("mysql:host=localhost;dbname=mydb", "username", "password");
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    
    // Named parameters
    $search = $_GET['search'];
    $stmt = $pdo->prepare("SELECT * FROM products WHERE name LIKE :search");
    $stmt->execute(['search' => "%{$search}%"]);
    
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        echo htmlspecialchars($row['name']) . "<br>";
    }
    
} catch (PDOException $e) {
    // Log fejlen internt, men vis generisk meddelelse
    error_log("Database error: " . $e->getMessage());
    echo "An error occurred. Please try again later.";
}

// 3. Sikker DELETE operation med type validation
$user_id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);

if ($user_id === false || $user_id === null) {
    die("Invalid user ID");
}

$stmt = $pdo->prepare("DELETE FROM users WHERE id = ? AND role != 'admin'");
$stmt->execute([$user_id]);

echo $stmt->rowCount() . " user(s) deleted";

// 4. Whitelist validation for column names og ORDER BY
$allowed_columns = ['name', 'price', 'created_at'];
$sort_column = $_GET['sort'] ?? 'name';

if (!in_array($sort_column, $allowed_columns, true)) {
    $sort_column = 'name';
}

// Column names kan ikke bindes som parameters, så vi bruger whitelist
$stmt = $pdo->prepare("SELECT * FROM products ORDER BY {$sort_column} ASC");
$stmt->execute();
?>

Almindelige Fejl

  • At bruge mysqli_real_escape_string() som primær forsvar i stedet for prepared statements - dette er ikke tilstrækkeligt mod alle angreb
  • At escapee input kun på nogle dele af applikationen, men glemme det i andre (f.eks. admin-paneler)
  • At tro at input-validering alene er nok beskyttelse - du skal BÅDE validere OG bruge prepared statements
  • At bygge dynamiske queries med column names eller table names uden whitelist-validering
  • At vise detaljerede database-fejlmeddelelser til slutbrugere, hvilket afslører database-struktur
  • At bruge emulated prepared statements i stedet for ægte prepared statements (særligt i ældre PHP-versioner)

Forebyggelsesteknikker

  • 🛡️Brug prepared statements med parameteriserede queries for ALLE database-interaktioner
  • 🛡️Implementer strict input validation med whitelists for alle brugerinput
  • 🛡️Brug ORM frameworks som Eloquent eller Doctrine der håndterer SQL-sikkerhed automatisk
  • 🛡️Konfigurer database-brugere med minimum nødvendige rettigheder (read-only hvor muligt)
  • 🛡️Implementer Web Application Firewall (WAF) med SQL injection detection rules
  • 🛡️Aktivér parameteriserede logging og monitorer for SQL injection-mønstre
  • 🛡️Brug static code analysis værktøjer til at identificere potentielle SQL injection-sårbarheder
  • 🛡️Implementér content security policies og input-længdebegrænsninger

Testing Metoder

  • 🔍Manuel testing med klassiske SQL injection payloads (', ", OR 1=1, UNION SELECT, etc.)
  • 🔍Automated scanning med værktøjer som SQLMap, Burp Suite, eller OWASP ZAP
  • 🔍Code review med fokus på alle database-interaktioner og input-håndtering
  • 🔍Penetration testing af erfarne sikkerhedseksperter
  • 🔍Brug af static analysis værktøjer som Psalm, PHPStan med sikkerhedsregler

Quick Info

Severity Level
🚨Critical
Relaterede Trusler
  • NoSQL Injection - Lignende angreb mod NoSQL databaser
  • LDAP Injection - Injection i LDAP queries
  • Command Injection - Injection af OS-kommandoer

⚠️Sikkerhedsadvarsel

Sikkerhedssårbarheder kan have alvorlige konsekvenser. Test altid grundigt i et sikkert miljø og implementer alle anbefalede forebyggelsesteknikker.