Eine aus zusammengeklebten Zeichenketten gebaute Abfrage vermischt Ihre Anweisungen mit fremden Daten. Die Datenbank kann beides nicht auseinanderhalten - ein sorgfältig gewählter Wert hört also auf, ein Wert zu sein, und wird Teil der Frage.
// wrong, and it is wrong even if you escape
$sql = "SELECT * FROM users WHERE email = '" . $email . "'";
Die Abhilfe ist nicht Escaping
Escaping versucht, gefährliche Daten ungefährlich zu machen. Prepared Statements lassen die Daten GETRENNT ANKOMMEN, sodass sie überhaupt nie als Anweisungen gelesen werden können. Der Datenbank wird zuerst die Frage gesagt und danach die Werte.
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
Was ein Platzhalter nicht sein kann
Ein Platzhalter steht für einen WERT. Er kann nicht für einen Tabellennamen, einen Spaltennamen oder eine Sortierrichtung stehen - die sind Teil der Frage. Müssen sie aus Eingaben kommen, prüfen Sie sie gegen eine Liste, die Sie selbst geschrieben haben.
$allowed = ['created_at', 'total', 'status'];
$sort = in_array($in, $allowed, true) ? $in : 'created_at';
$sql = "SELECT * FROM orders ORDER BY $sort DESC";
Finden, welche Sie haben
grep -rn "SELECT.*\$_\(GET\|POST\|REQUEST\)" --include="*.php" . | head
Jede Abfrage mit einer Superglobalen mitten in der Zeichenkette ist das Muster. Meist sind es eine Handvoll, und meist sind sie alt.
Und die zweite Linie
Geben Sie dem Anwendungsbenutzer nur, was er braucht - vier Verben auf einer Datenbank. Eine geglückte Injection über ein Konto, das keine Tabelle löschen kann, richtet weit weniger an als eine über ein Konto, das es kann.