SQL Injection Verhindern
SQL Injection verhindern: Sichere Datenbank-Abfragen
SQL Injection ist eine der gefährlichsten Sicherheitslücken. Angreifer können damit Daten stehlen, ändern oder löschen. So schützen Sie Ihre Anwendung.
Was ist SQL Injection?
Bei SQL Injection schleust ein Angreifer schädlichen SQL-Code über Benutzereingaben ein:
// GEFÄHRLICH: Benutzereingabe direkt im Query $username = $_POST['username']; $query = "SELECT * FROM users WHERE username = '$username'"; // Angreifer gibt ein: // ' OR '1'='1 // Ergebnis: SELECT * FROM users WHERE username = '' OR '1'='1' // → Gibt ALLE User zurück!
Mögliche Angriffe
- Daten auslesen: UNION-basierte Angriffe
- Authentication Bypass: Login umgehen
- Daten löschen: DROP TABLE, DELETE
- Daten ändern: UPDATE, INSERT
- Kommandos ausführen: xp_cmdshell (MSSQL)
Lösung: Prepared Statements
Prepared Statements trennen SQL-Code von Daten. Der sicherste Schutz!
PHP mit PDO
<?php
// ✅ SICHER: Prepared Statement
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->execute(['username' => $username]);
$user = $stmt->fetch();
// Auch für INSERT, UPDATE, DELETE
$stmt = $pdo->prepare("INSERT INTO users (username, email) VALUES (?, ?)");
$stmt->execute([$username, $email]);
// Mit mehreren Parametern
$stmt = $pdo->prepare("
SELECT * FROM products
WHERE category = :category
AND price < :maxPrice
ORDER BY name
");
$stmt->execute([
'category' => $category,
'maxPrice' => $maxPrice
]);
PHP mit MySQLi
<?php
// Prepared Statement mit MySQLi
$stmt = $mysqli->prepare("SELECT * FROM users WHERE username = ?");
$stmt->bind_param("s", $username); // "s" = string
$stmt->execute();
$result = $stmt->get_result();
// Typ-Spezifikationen:
// s = string
// i = integer
// d = double
// b = blob
$stmt = $mysqli->prepare("INSERT INTO users (name, age, score) VALUES (?, ?, ?)");
$stmt->bind_param("sid", $name, $age, $score);
$stmt->execute();
Node.js mit mysql2
// ✅ SICHER: Parametrisierte Query
const [rows] = await connection.execute(
'SELECT * FROM users WHERE username = ?',
[username]
);
// Mehrere Parameter
const [results] = await connection.execute(
'SELECT * FROM products WHERE category = ? AND price < ?',
[category, maxPrice]
);
// Mit Named Parameters
const [rows] = await connection.execute(
'SELECT * FROM users WHERE username = :username',
{ username: username }
);
Python mit psycopg2 (PostgreSQL)
import psycopg2
# ✅ SICHER: Parametrisierte Query
cursor.execute(
"SELECT * FROM users WHERE username = %s",
(username,)
)
# Mehrere Parameter
cursor.execute(
"""
INSERT INTO users (username, email, age)
VALUES (%s, %s, %s)
""",
(username, email, age)
)
# Mit Named Parameters
cursor.execute(
"SELECT * FROM users WHERE username = %(user)s",
{'user': username}
)
Dynamische Queries sicher bauen
Whitelist für Spaltennamen
<?php
// Spaltennamen können NICHT als Parameter übergeben werden!
// Lösung: Whitelist
$allowedColumns = ['name', 'email', 'created_at'];
$orderBy = in_array($orderBy, $allowedColumns) ? $orderBy : 'name';
$stmt = $pdo->prepare("SELECT * FROM users ORDER BY $orderBy");
$stmt->execute();
Whitelist für Sortierrichtung
<?php
$direction = strtoupper($direction) === 'DESC' ? 'DESC' : 'ASC';
$stmt = $pdo->prepare("SELECT * FROM users ORDER BY name $direction");
IN-Clause mit variabler Anzahl
<?php
$ids = [1, 2, 3, 4, 5];
$placeholders = str_repeat('?,', count($ids) - 1) . '?';
$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN ($placeholders)");
$stmt->execute($ids);
Was NICHT funktioniert
- Escaping allein: mysql_real_escape_string ist unzureichend
- Blacklists: Gefährliche Zeichen filtern (umgehbar)
- addslashes(): Nicht sicher für SQL
- Typ-Casting allein: Nur für Integers hilfreich
// ❌ UNSICHER: Escaping
$username = mysqli_real_escape_string($conn, $username);
$query = "SELECT * FROM users WHERE username = '$username'";
// Kann bei bestimmten Zeichensätzen umgangen werden!
// ❌ UNSICHER: Blacklist
$username = str_replace("'", "", $username);
// Kann durch Encoding umgangen werden!
Zusätzliche Schutzmaßnahmen
1. Minimale Datenbankrechte
-- Eigener User für die Anwendung CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'password'; -- Nur notwendige Rechte GRANT SELECT, INSERT, UPDATE ON myapp.* TO 'app_user'@'localhost'; -- KEIN DROP, DELETE, ALTER etc.
2. Input-Validierung
<?php
// Zusätzlich zu Prepared Statements!
$email = filter_var($email, FILTER_VALIDATE_EMAIL);
if (!$email) {
throw new InvalidArgumentException('Invalid email');
}
$age = filter_var($age, FILTER_VALIDATE_INT, [
'options' => ['min_range' => 0, 'max_range' => 150]
]);
3. Web Application Firewall (WAF)
ModSecurity oder Cloud-WAFs wie Cloudflare erkennen SQLi-Versuche.
Testen auf SQL Injection
- sqlmap: Automatisiertes SQLi-Testing
- Burp Suite: Manuelles Testing
- OWASP ZAP: Kostenloser Scanner
# sqlmap Beispiel sqlmap -u "https://example.com/page?id=1" --dbs