El camino más confiable es siempre usar consultas parametrizadas o declaraciones preparadas (prepared statements), y NO formar consultas a través de la simple concatenación de cadenas con valores proporcionados por el usuario.
Para escenarios más complejos (SQL dinámico dentro de procedimientos) — aplique mecanismos especiales de escape y whitelisting (filtrado estricto) de parámetros.
cursor.execute('SELECT * FROM users WHERE login = %s', [login])
EXECUTE format('SELECT * FROM %I WHERE id = $1', tablename) USING id;
Aquí %I solo reemplaza los nombres de tablas/columnas de manera segura.
¿Es seguro el siguiente enfoque al insertar un número en una cadena?
EXECUTE 'SELECT * FROM users WHERE id = ' || user_input;
¡NO! Incluso si espera un número, un atacante podría enviar el código:
1; DROP TABLE users; — la consulta se ejecutará. Por lo tanto, siempre use marcadores de posición y verificación explícita del tipo de los parámetros de entrada.
Historia 1
El formulario de búsqueda de empleados construía las condiciones WHERE mediante la concatenación de cadenas en el lado de la aplicación. Un atacante introdujo: ' OR 1=1 --, lo que devolvió datos de todos los empleados. Más tarde, mediante un enfoque similar, se eliminó una sección de datos. Conclusión: la concatenación de parámetros significativos siempre es un riesgo.
Historia 2
En la interfaz administrativa de selección de informes, el nombre de la tabla se tomaba de un campo proporcionado por el usuario e insertaba en la consulta SQL. El usuario logró insertar un nombre no válido y obtener acceso a las tablas del sistema. Después de eso, solo se permitieron nombres fijos (whitelisting).
Historia 3
En un proyecto de comercio electrónico, al ordenar dinámicamente, los usuarios pasaban el nombre del campo a través de un parámetro GET. Un hacker inserta price; DELETE FROM orders; --, lo que provocó la pérdida de todos los pedidos. Filtrar los nombres permitidos de las columnas y usar consultas parametrizadas resolvió el problema.