La méthode la plus fiable est toujours d'utiliser des requêtes paramétrées ou des déclarations préparées (prepared statements), et NON de générer des requêtes par simple concaténation de chaînes avec des valeurs utilisateur.
Pour des scénarios plus complexes (SQL dynamique à l'intérieur des procédures) — appliquez des mécanismes spéciaux d'échappement et de whitelisting (filtrage strict) des paramètres.
cursor.execute('SELECT * FROM users WHERE login = %s', [login])
EXECUTE format('SELECT * FROM %I WHERE id = $1', tablename) USING id;
Ici, %I insère uniquement les noms de tables/colonnes de manière sécurisée.
Cette approche est-elle sécurisée lors de l'insertion d'un nombre dans une chaîne ?
EXECUTE 'SELECT * FROM users WHERE id = ' || user_input;
NON ! Même si vous attendez un nombre, un attaquant peut transmettre du code :
1; DROP TABLE users; — la requête sera exécutée. Donc utilisez toujours des place-holders et une vérification explicite du type des paramètres d'entrée.
Histoire 1
Le formulaire de recherche des employés construisait des conditions WHERE par concaténation de chaînes côté application. Un attaquant a entré : ' OR 1=1 --, ce qui a renvoyé les données de tous les employés. Plus tard, une approche similaire a conduit à la suppression d'une section de données. Conclusion : la concaténation de paramètres significatifs — toujours un risque.
Histoire 2
Dans l'interface administrative de sélection de rapport, le nom de la table était tiré d'un champ utilisateur et inséré dans la requête SQL. L'utilisateur a réussi à insérer un nom invalide et a obtenu accès aux tables système. Après cela, seuls des noms définis de manière stricte ont été autorisés (whitelisting).
Histoire 3
Dans un projet e-commerce, lors du tri dynamique les utilisateurs transmettaient le nom du champ par le paramètre GET. Un hacker a inséré price; DELETE FROM orders; --, ce qui a entraîné la perte de toutes les commandes. Le filtrage des noms de colonnes autorisés et l'utilisation de requêtes paramétrées ont résolu le problème.