PDO und die WHERE IN Problematik

Seit die Standard MySQL Extension in PHP als veraltet markiert wurde und entsprechende Meldungen bei der Benutzung dieser Library hervorgerufen werden, wird der Einsatz von moderneren und auch sehr viel sichereren Erweiterungen immer notwendiger. Ich selbst arbeite seit geraumer Zeit mit der PDO Extension von PHP. Wie schon bei den LIKE Statements erwähnt, muss man bei einigen SQL Abfragen ein wenig umdenken. Die WHERE IN Abfrage gehört zu diesen besonderen Fällen.

Das Problem

Aus der alten MySQL Extension war ich es gewohnt eine WHERE IN Abfrage einfach als String Verkettung zu schreiben. Ihr kennt das folgende Beispiel sicherlich noch sehr genau:

1
2
$myIdArray = array(1,2,3);
$sql = "DELETE FROM myTable WHERE id IN (" . implode(",", $myIdArray) . ")";

Das ist simpel, geht verdammt schnell und birgt ein großes Sicherheitsrisiko, welches man SQL Injection nennt. Die Sicherheitsbedenken kann man seit der Einführung von PDO oder der MySQLi Extension mit Prepared Statements aus der Welt schaffen. Ein SQL Query mit Prepared Statements könnte mit PDO wie folgt aussehen:

1
2
3
4
5
6
$pdo = new PDO(...);
$stmt = $pdo->prepare("DELETE FROM myTABLE WHERE id IN (:myIdArray)");
$stmt->bindValue(':myIdArray', implode(",", $myIdArray));
$stmt->execute();
 
// resultiert in: DELETE FROM myTable WHERE id IN ("1,2,3")

Sieht soweit gut aus, wird aber nicht funktionieren. Jeder von Euch, der ein WHERE IN Statement schon mal auf diese Art und Weise versucht hat umzusetzen, wird wissen was ich meine. Es gibt in meiner Datenbanktabelle einfach keine ID, die „1,2,3“ lautet. Aus meiner Sicht ein ganz klarer Nachteil von Prepared Statements. Am Ende muss es aber dennoch sicher sein.

Die Lösung

Mit der MySQL Funktion FIND_IN_SET können wir dieses Problem aber lösen. Diese Funktion sucht einen String in einer durch ein Komma getrennte Liste von Werten. Also genau das, was wir eigentlich wollen. Das ganze sieht dann wie folgt aus:

1
2
3
$stmt = $pdo->prepare("DELETE FROM myTable WHERE FIND_IN_SET(CAST(id AS char), :myIdArray)");
$stmt->bindValue(':myIdArray', implode(",", $myIdArray));
$stmt->execute();

In diesem Beispiel werden die Datensätze mit den IDs 1, 2 und 3 aus der Datenbanktabelle gelöscht. Alles safe ohne jegliche Sicherheitsbedenken.

Wo sind die Nachteile?

Im gezeigten Beispiel wandeln wir die ID, welche im Normalfall ein Integer Wert ist, in einen String um. Leider ist dies für die Nutzung der Funktion FIND_IN_SET notwendig. Dies bedeutet, dass bei großen Datenmengen ein Performance Problem entstehen könnte. Das war ’s dann aber auch schon. Die Sicherheit bleibt Dank Prepared Statements gewahrt und die jeweiligen IDs werden ohne Probleme aus unserer Datenbanktabelle gelöscht. Also bitte hört auf die MySQL Extension von PHP zu nutzen. Sie wird ohnehin auf absehbare Zeit komplett entfernt werden.

Haben wir wieder was gelernt. 😉

About Author: Marcel
Ich bin Senior PHP Developer bei MM Newmedia. Seit 2005 bin ich begeisterter Webentwickler und arbeite als Freelancer für namenhafte Firmen und entwickle jede Menge abgefahrenes Zeug und berichte darüber in meinem Blog.

Kommentar verfassen