Einleitung: Die Frage, die jeden PHP-Entwickler quält
\n\nWenn du jemals SQL-Abfragen in PHP geschrieben hast, kennst du die goldene Regel: "Nutze PDO und Prepared Statements – dann bist du sicher". Aber stimmt das wirklich? Auf Stack Overflow hat die Frage "Are PDO prepared statements sufficient to prevent SQL injection?" tausende Aufrufe und hitzige Diskussionen gesammelt. Und das zu Recht.
\n\nIn diesem Artikel geben wir nicht einfach ein kurzes "Ja" oder "Nein". Wir schauen unter die Haube von PDO, analysieren, wie Prepared Statements genau funktionieren, und zeigen, in welchen Fällen selbst PDO versagen kann. Bereit? Los geht's.
\n\nWas ist SQL-Injection und warum ist sie gefährlich?
\n\nBevor wir über Schutz sprechen, erinnern wir uns an den Feind. SQL-Injection ist ein Angriff, bei dem ein Angreifer bösartigen SQL-Code über Eingabedaten einschleust. Der Klassiker:
\n\n// Schlechter Code: Mach das niemals!\n$username = $_POST['username'];\n$query = "SELECT * FROM users WHERE username = '$username'";\n$result = mysqli_query($conn, $query);\n\n\nWenn der Angreifer admin' OR '1'='1 übergibt, wird die Abfrage zu:
SELECT * FROM users WHERE username = 'admin' OR '1'='1'\n\n\nSchon hast du Zugriff auf Daten ohne Passwort. Und bei '; DROP TABLE users; --? Tschüss, Benutzertabelle.
PDO und Prepared Statements: Wie funktioniert das?
\n\nPDO (PHP Data Objects) bietet den Mechanismus der Prepared Statements. Die Idee ist einfach: Du trennst die Struktur der Abfrage von den Daten. Statt Variablen direkt in den String einzufügen, verwendest du Platzhalter:
\n\n// Sicherer Code mit PDO\n$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");\n$stmt->execute([':username' => $_POST['username']]);\n\n\nAber was passiert unter der Haube? Das ist der entscheidende Punkt, den viele übersehen.
\n\nEmulationsmodus vs. native Prepared Statements
\n\nPDO kann in zwei Modi arbeiten:
\n\n- \n
- Emulationsmodus (Standard in älteren PHP-Versionen) – PDO escaped die Daten selbst und setzt sie auf PHP-Seite in die Abfrage ein. Das ist immer noch sicher, aber theoretisch anfällig, wenn das Escaping versagt (z. B. bei ungewöhnlichen Zeichenkodierungen). \n
- Nativer Modus – PDO sendet zwei Nachrichten an den Datenbankserver: zuerst das Abfrage-Template, dann separat die Daten. Der Server behandelt die Daten als Werte, nicht als Code. Das ist absolut sicher. \n
Um den nativen Modus zu aktivieren, deaktiviere die Emulation:
\n\n$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);\n\n\nJetzt wird die Abfrage in zwei Schritten an den Server gesendet. Selbst wenn der Angreifer ' OR '1'='1 übergibt, interpretiert MySQL oder PostgreSQL dies einfach als Zeichenfolge, nicht als SQL-Code. Magie!
Aber ist alles perfekt? Fallstricke von PDO
\n\nZurück zur Stack Overflow-Frage. Die Antwort "Ja, PDO Prepared Statements reichen aus" – aber mit Einschränkungen. Hier sind Situationen, in denen der Schutz versagen kann.
\n\n1. Dynamische Bezeichner (Tabellen- und Spaltennamen)
\n\nPrepared Statements funktionieren nur für Werte (Daten). Wenn du einen Tabellen- oder Spaltennamen einsetzt, helfen Platzhalter nicht:
\n\n// Gefährlich! Tabellennamen können nicht über Platzhalter übergeben werden\n$stmt = $pdo->prepare("SELECT * FROM :table WHERE id = :id");\n$stmt->execute([':table' => $_GET['table'], ':id' => 1]); // Fehler!\n\n\nPDO fügt den Tabellennamen einfach als Zeichenfolge ein, und die Abfrage bricht. Wenn du die Emulation verwendest, könnte ein Angreifer users; DROP TABLE users; -- übergeben. Lösung: Whitelist der erlaubten Werte.
2. LIKE-Abfragen und Platzhalterzeichen
\n\nWenn du eine