Inyección SQL y consultas parametrizadas
Al finalizar este tema
Podrás explicar cómo funciona la inyección SQL y escribir código que la defienda mediante consultas parametrizadas.
¿Qué es la inyección SQL?
La inyección SQL es un ataque que inserta código SQL en las entradas del usuario para manipular la base de datos. Es uno de los ataques más antiguos, peligrosos y aún más comunes en la seguridad web.
Cómo funciona el ataque
Imagina un formulario de inicio de sesión. Cuando un usuario introduce su correo electrónico, el servidor lo procesa de la siguiente manera:
// ❌ Código peligroso — inserta la entrada directamente en SQL
const email = req.body.email;
const sql = `SELECT * FROM users WHERE email = '${email}'`;
connection.query(sql);Entrada válida: hoon@example.com
SELECT * FROM users WHERE email = 'hoon@example.com'
-- Funcionamiento normalEntrada de ataque: ' OR '1'='1
SELECT * FROM users WHERE email = '' OR '1'='1'
-- '1'='1' es siempre verdadero → ¡se devuelven todos los datos de usuario!Un ataque más peligroso: '; DROP TABLE users; --
SELECT * FROM users WHERE email = ''; DROP TABLE users; --'
-- El punto y coma termina la primera consulta y ejecuta el comando para eliminar la tabla
-- -- comenta el restoPunto clave: El problema radica en que la entrada del usuario altera la estructura misma de la consulta SQL.
Defensa: Consultas parametrizadas
Las consultas parametrizadas (Parameterized Query, Prepared Statement) separan la estructura de la consulta SQL de los datos.
// ✅ Código seguro — usa marcadores de posición
const email = req.body.email;
connection.query(
'SELECT * FROM users WHERE email = ?',
[email]
);Incluso si se recibe una entrada de ataque ' OR '1'='1:
SELECT * FROM users WHERE email = '\' OR \'1\'=\'1'
-- Todo se trata como un único valor de cadena. La estructura SQL no cambia.El motor de la base de datos analiza primero la estructura SQL y, a continuación, inserta de forma segura los valores en la posición ?. Independientemente de lo inusuales que sean los valores, no se interpretan como comandos SQL.
Patrones de parametrización por lenguaje
Node.js (mysql2)
// ? Marcador de posición
connection.query('SELECT * FROM users WHERE id = ?', [userId]);
connection.query(
'INSERT INTO users (name, email) VALUES (?, ?)',
[name, email]
);Python (sqlite3 / psycopg2)
# ? Marcador de posición (sqlite3)cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
# %s Marcador de posición (psycopg2 / MySQL Connector)cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))Java (JDBC)
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM users WHERE id = ?"
);
stmt.setInt(1, userId);
ResultSet rs = stmt.executeQuery();La sintaxis puede variar, pero el principio es el mismo: separar el código SQL de los datos.
Capas de defensa adicionales
Aunque las consultas parametrizadas son fundamentales, existen capas de defensa adicionales:
| Método | Función |
|---|---|
| Consultas parametrizadas | Defensa principal: protege la estructura del código SQL |
| Validación de entrada | Filtrado previo (formato de correo electrónico, rango numérico, etc.) |
| Principio de privilegio mínimo | Conceder solo los permisos necesarios a la cuenta de la base de datos (eliminar DELETE si no es necesario) |
| Uso de ORM | Herramientas como Sequelize y Prisma aplican la parametrización internamente |
Lo más importante: no concatenar directamente la entrada del usuario en una cadena de código SQL. Si se cumple esta única regla, no se producirán inyecciones SQL.