Il modo più sicuro è sempre utilizzare query parametrizzate o espressioni preparate (prepared statements) e NON costruire query tramite semplice concatenazione di stringhe con valori forniti dall'utente.
Per scenari più complessi (SQL dinamico all'interno di procedure) — applicare meccanismi speciali di escaping e whitelisting (filtrazione rigorosa) dei parametri.
cursor.execute('SELECT * FROM users WHERE login = %s', [login])
EXECUTE format('SELECT * FROM %I WHERE id = $1', tablename) USING id;
Qui %I sostituisce in modo sicuro solo i nomi delle tabelle/colonne.
È sicuro il seguente approccio nell'inserimento di un numero in una stringa?
EXECUTE 'SELECT * FROM users WHERE id = ' || user_input;
NO! Anche se ci si aspetta un numero, un attaccante può inviare codice:
1; DROP TABLE users; — la query verrà eseguita. Pertanto, utilizzare sempre i placeholder e una chiara verifica del tipo dei parametri in ingresso.
Storia 1
Il modulo di ricerca dei dipendenti costruiva le condizioni WHERE tramite concatenazione di stringhe sul lato applicativo. Un attaccante ha inserito: ' OR 1=1 --, che ha restituito i dati di tutti i dipendenti. Successivamente, con un approccio simile, è stata eliminata una sezione di dati. Conclusione: concatenazione di parametri significativi è sempre un rischio.
Storia 2
Nell'interfaccia amministrativa di scelta del report, il nome della tabella veniva prelevato da un campo utente e inserito nella query SQL. L'utente è riuscito a inserire un nome non valido e ad accedere a tabelle di sistema. Dopo questo, sono stati consentiti solo nomi rigidamente definiti (whitelisting).
Storia 3
Nel progetto di e-commerce, durante l'ordinamento dinamico, gli utenti passavano il nome del campo tramite un parametro GET. Un hacker ha inserito price; DELETE FROM orders; --, il che ha portato alla perdita di tutti gli ordini. La filtrazione dei nomi delle colonne consentite e l'uso di query parametrizzate hanno risolto il problema.