PDO in PHP: Schützt es zu 100% vor SQL-Injection?

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Mythen und Realität von PDO

\n\n

Wenn Sie in PHP programmieren und schon einmal gegoogelt haben, „wie man sich vor SQL-Injection schützt“, haben Sie sicherlich das Mantra gehört: „Verwenden Sie PDO und Prepared Statements – und Sie sind auf der sicheren Seite“. Das ist der Goldstandard, die Grundlage jedes Lehrbuchs zur PHP-Sicherheit. Aber stimmt das wirklich? Reicht es einfach $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?') zu schreiben und das Problem zu vergessen?

\n\n

Die Frage „Sind PDO Prepared Statements ausreichend, um SQL-Injection zu verhindern?“ hat auf Stack Overflow nicht ohne Grund tausende Aufrufe. Die Antwort ist, wie sich herausstellt, nicht so eindeutig. In diesem Artikel tauchen wir unter die Haube von PDO, analysieren, wie Prepared Statements tatsächlich funktionieren, und finden heraus, in welchen Fällen selbst PDO Sie im Stich lassen kann.

\n\n

Dies ist keine langweilige Theorie. Wir betrachten reale Beispiele, Abfrage-Emulation, Nuancen bei Zeichenkodierungen und geben konkrete Action Items, damit Ihr Code wirklich geschützt ist.

\n\n

Wie Prepared Statements funktionieren: Magie oder Mathematik?

\n\n

Erinnern wir uns zunächst daran, was eine SQL-Injection ist. Dabei übermittelt ein Angreifer Daten in einer Abfrage, die als Teil des SQL-Codes interpretiert werden. Klassiker: ' OR 1=1 --.

\n\n

Prepared Statements (vorbereitete Abfragen) lösen dieses Problem grundlegend. Sie trennen die Abfragelogik und die Daten. Das sieht so aus:

\n\n
<?php\n$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');\n$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');\n$stmt->execute([':email' => $_GET['email'], ':status' => 'active']);\n?>\n
\n\n

Unter der Haube passiert Folgendes:

\n\n
    \n
  1. Vorbereitung (Prepare): Der Treiber sendet dem Datenbankserver ein „Skelett“ der Abfrage: SELECT * FROM users WHERE email = ? AND status = ?. Der Server parst dieses Skelett, optimiert es und merkt sich den Ausführungsplan. Der Platz für Daten (Platzhalter) ist vorerst leer.
  2. \n
  3. Ausführung (Execute): Jetzt senden Sie nur die tatsächlichen Werte. Der Server fügt sie in den bereits vorhandenen Plan ein, aber auf eine besondere Weise – er weiß, dass es sich um Daten und nicht um Code handelt. Daher werden alle Versuche, „ein Anführungszeichen zu schließen“ oder einen SQL-Befehl hinzuzufügen, einfach zu einem Teil des String-Literals.
  4. \n
\n\n

Klingt ideal. Aber es gibt einen Haken.

\n\n

Die Fallstricke: Emulation von Prepared Statements

\n\n

Die größte Überraschung, die genau diese Frage auf Stack Overflow ans Licht brachte: PDO verwendet standardmäßig nicht immer „echte“ Prepared Statements.

\n\n

Der Grund ist, dass nicht alle Datenbanken diese Funktion auf Protokollebene unterstützen. Und einige Treiber (z. B. für SQLite oder ältere MySQL-Versionen) arbeiten über eine Emulation. Was bedeutet das?

\n\n

Wenn die Emulation aktiviert ist, setzt PDO die Werte selbst in die SQL-Abfrage auf Client-Seite (in PHP) ein und sendet dann die fertige Abfrage an den Server. Dabei escaped es Sonderzeichen mit PDO::quote().

\n\n

Und hier beginnen die Probleme. Die Emulation ist im Grunde dieselbe String-Ersetzung, nur von der Bibliothek durchgeführt. Und wenn Sie die Verbindungskodierung falsch eingestellt haben, kann dieser Mechanismus geknackt werden.

\n\n

Beispiel: Angriff über unterschiedliche Kodierungen

\n\n

Ein klassischer Angriff unter Verwendung einer falschen Kodierung sieht so aus. Angenommen, Sie haben eine Verbindung zu MySQL mit der Kodierung GBK (chinesische Kodierung) hergestellt, und die Abfrage selbst wird emuliert. Das Zeichen \\ (Backslash) kann in GBK Teil eines Multibyte-Zeichens sein.

\n\n

Der Angreifer übergibt den String: %BF%27. In der Kodierung GBK bildet die Sequenz %BF%5C ein einzelnes Zeichen, und %27 ist einfach ein Anführungszeichen. Der Emulator sieht das Anführungszeichen und escaped es, indem er %27 in %5C%27 (also \\') umwandelt. Aber aufgrund der Tatsache, d

Blogs

Buchempfehlungen