Einleitung: Warum SQL-Injection 2025 immer noch relevant ist?
\n\nStellen Sie sich vor: Ihre Website läuft wie geschmiert, Benutzer registrieren sich, hinterlassen Kommentare, geben Bestellungen auf. Und plötzlich – die Datenbank befindet sich in den Händen eines Angreifers. Nicht weil Hacker genial sind, sondern weil jemand vergessen hat, ein Fragezeichen in eine SQL-Abfrage zu setzen.
\n\nSQL-Injection ist keine veraltete Bedrohung aus den 2000ern. Im Jahr 2024 setzt OWASP sie immer noch in die Top 3 der gefährlichsten Webanwendungs-Schwachstellen. Und die Frage "How can I prevent SQL injection in PHP?" auf Stack Overflow hat über 1,5 Millionen Aufrufe – und die Zahl steigt weiter. Das Problem ist also lebendig.
In diesem Artikel zeigen wir Ihnen, wie Sie die Tür für SQL-Injection in PHP im Jahr 2025 dauerhaft verschließen. Ohne Magie, ohne komplexe Frameworks, nur mit sauberem Code und Best Practices.
\n\nWas ist SQL-Injection einfach erklärt?
\n\nSQL-Injection liegt vor, wenn eine Benutzereingabe Teil eines SQL-Befehls wird, anstatt nur Daten zu sein. Ein Beispiel aus der Praxis:
\n\n<?php\n// SCHLECHTER CODE – machen Sie das niemals!\n$username = $_POST['username'];\n$query = "SELECT * FROM users WHERE username = '$username'";\n$result = mysqli_query($conn, $query);\n?>\n\nWenn ein Angreifer in das Feld username die Zeichenkette ' OR '1'='1 eingibt, wird die Abfrage zu:
SELECT * FROM users WHERE username = '' OR '1'='1'\n\nDies gibt alle Benutzer zurück. Und bei Eingabe von '; DROP TABLE users; -- könnte die gesamte Tabelle gelöscht werden. Erschreckend, oder?
Best Practices zum Schutz vor SQL-Injection in PHP 2025
\n\n1. Verwenden Sie PDO mit Prepared Statements (der Standard)
\n\nPDO (PHP Data Objects) ist eine universelle Schnittstelle für die Arbeit mit Datenbanken. Sein Hauptvorteil: Es trennt die SQL-Logik von den Daten. Sie schreiben eine Abfrage mit Platzhaltern, und PDO kümmert sich um das sichere Escaping.
\n\n<?php\n// GUTER CODE – verwenden Sie PDO\n$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';\n$pdo = new PDO($dsn, $user, $pass, [\n PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,\n PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,\n PDO::ATTR_EMULATE_PREPARES => false, // Emulation deaktivieren\n]);\n\n$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');\n$stmt->execute(['email' => $_POST['email']]);\n$user = $stmt->fetch();\n?>\n\nWarum funktioniert das? Die Datenbank erhält die Struktur der Abfrage getrennt von den Daten. Selbst wenn ein Benutzer schädliches SQL eingibt, wird es als normale Zeichenkette und nicht als Teil des Befehls interpretiert.
\n\n2. Mysqli mit Prepared Statements (Alternative)
\n\nWenn Sie nur mit MySQL arbeiten, können Sie mysqli verwenden – aber nur mit Prepared Statements, nicht mit Escaping über mysqli_real_escape_string().
<?php\n$mysqli = new mysqli('localhost', 'user', 'pass', 'testdb');\n$stmt = $mysqli->prepare('INSERT INTO comments (user_id, text) VALUES (?, ?)');\n$stmt->bind_param('is', $userId, $commentText);\n$stmt->execute();\n?>\n\nWichtig: Im Jahr 2025 existiert mysqli_real_escape_string() zwar noch, aber sich darauf zum Schutz zu verlassen, ist keine gute Idee. Es escaped nur Zeichenketten, schützt aber nicht vor Injection in numerischen Feldern oder bei LIKE-Operatoren.
3. Filterung und Validierung – die zweite Verteidigungslinie
\n\nPrepared Statements sind die Basis. Aber fügen Sie eine Filterung nach Datentyp hinzu. Wenn eine Zahl erwartet wird, prüfen Sie, ob es eine Zahl ist. Wenn eine E-Mail erwartet wird, prüfen Sie das Format.
\n\n<?php\n$id = filter_input(INPUT_POST, 'user_id', FILTER_VALIDATE_INT);\nif ($id === false) {\n throw new InvalidArgumentException('ID muss eine ganze Zahl sein');\n}\n\n$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');\n$stmt->execute(['id' => $id]);\n?>\n\nKombini