Une requête construite en collant des chaînes mêle vos instructions aux données de quelqu'un d'autre. La base ne peut pas distinguer les unes des autres : une valeur choisie avec soin cesse alors d'être une valeur et devient une partie de la question.

// wrong, and it is wrong even if you escape
$sql = "SELECT * FROM users WHERE email = '" . $email . "'";

Le remède n'est pas l'échappement

L'échappement tente de rendre inoffensive une donnée dangereuse. Les requêtes préparées font ARRIVER la donnée SÉPARÉMENT, de sorte qu'elle ne puisse jamais être lue comme une instruction. On dit d'abord la question à la base, et les valeurs ensuite.

$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
L'échappement peut échouer à cause d'un jeu de caractères, d'une apostrophe imbriquée, d'une valeur déjà passée par une autre couche. Une requête préparée ne peut pas échouer de ces façons-là, parce que la valeur ne touche jamais le texte SQL.

Ce qu'un paramètre ne peut pas être

Un paramètre tient lieu de VALEUR. Il ne peut pas tenir lieu de nom de table, de nom de colonne ni de sens de tri - cela fait partie de la question. Si ces éléments doivent venir d'une saisie, vérifiez-les contre une liste que vous avez écrite.

$allowed = ['created_at', 'total', 'status'];
$sort = in_array($in, $allowed, true) ? $in : 'created_at';
$sql = "SELECT * FROM orders ORDER BY $sort DESC";

Trouver celles que vous avez

grep -rn "SELECT.*\$_\(GET\|POST\|REQUEST\)" --include="*.php" . | head

Toute requête contenant une superglobale à l'intérieur de la chaîne est le motif. Il y en a d'ordinaire une poignée, et d'ordinaire elles sont anciennes.

Et la seconde ligne de défense

Donnez à l'utilisateur de l'application uniquement ce dont il a besoin : quatre verbes sur une seule base. Une injection réussie via un compte qui ne peut pas supprimer une table fait bien moins de dégâts qu'une injection via un compte qui le peut.

Un ORM paramètre à votre place, jusqu'à ce que vous utilisiez son assistant de requête brute. C'est dans cet assistant que vivent les injections du code moderne.