Back

Guía para Principiantes sobre Inyección SQL (Y Cómo Prevenirla)

Guía para Principiantes sobre Inyección SQL (Y Cómo Prevenirla)

Estás construyendo una función de búsqueda para tu aplicación. Los usuarios escriben el nombre de un producto, tu backend consulta la base de datos y aparecen los resultados. Bastante simple—hasta que alguien escribe '; DROP TABLE products;-- en ese cuadro de búsqueda.

Esto es inyección SQL, y sigue siendo una de las vulnerabilidades más peligrosas en el desarrollo web. El OWASP Top 10 lista “Injection” como un riesgo de seguridad crítico, y la inyección SQL está en el centro de esa categoría. Si escribes código que interactúa con una base de datos—aunque sea ocasionalmente—necesitas entender cómo funciona este ataque y cómo detenerlo.

Puntos Clave

  • La inyección SQL ocurre cuando los atacantes manipulan las consultas de base de datos insertando código malicioso a través de la entrada del usuario
  • Las consultas parametrizadas (prepared statements) son la defensa principal, separando el código SQL de los datos
  • Los ORMs no son automáticamente seguros cuando se utilizan métodos de consulta raw
  • Utiliza listas blancas (allowlists) para identificadores dinámicos como nombres de tablas y columnas, que no pueden ser parametrizados
  • Aplica defensa en profundidad: cuentas con privilegios mínimos, capas de acceso a datos centralizadas y pruebas de seguridad en pipelines de CI

¿Qué es la Inyección SQL?

La inyección SQL ocurre cuando un atacante manipula las consultas de base de datos de tu aplicación insertando código malicioso a través de la entrada del usuario. En lugar de tratar la entrada como datos, la base de datos la ejecuta como comandos.

Considera un formulario de inicio de sesión. Tu backend podría construir una consulta como esta:

SELECT * FROM users WHERE email = 'user@example.com' AND password = 'secret123'

Si construyes esta consulta concatenando cadenas con la entrada del usuario, un atacante puede ingresar ' OR '1'='1 como contraseña. La consulta resultante se convierte en:

SELECT * FROM users WHERE email = 'user@example.com' AND password = '' OR '1'='1'

Como '1'='1' siempre es verdadero, la consulta devuelve todos los usuarios, evitando completamente la autenticación.

Por Qué la Inyección SQL Sigue Siendo Importante

Podrías asumir que los frameworks modernos han resuelto este problema. No lo han hecho—al menos no automáticamente.

Las vulnerabilidades de inyección SQL aparecen en:

  • Endpoints de API que aceptan JSON con parámetros de filtro
  • Formularios de búsqueda con múltiples opciones de consulta
  • Paneles de administración con ordenamiento o filtrado dinámico
  • Generadores de reportes con criterios definidos por el usuario

Cualquier lugar donde la entrada del usuario influya en una consulta de base de datos es una superficie de ataque potencial. Las consecuencias van desde el robo de datos hasta la destrucción completa de la base de datos.

Cómo Ocurre la Construcción Insegura de Consultas

La causa raíz es siempre la misma: tratar la entrada del usuario como código SQL confiable en lugar de como datos.

La concatenación de cadenas es el principal culpable:

// Vulnerable - nunca hagas esto
const query = `SELECT * FROM products WHERE name = '${userInput}'`;

Los template literals y el formateo de cadenas crean el mismo problema:

# Vulnerable - nunca hagas esto
query = f"SELECT * FROM products WHERE category = '{category}'"

Incluso los ORMs no son automáticamente seguros cuando usas métodos de consulta raw:

// Vulnerable - las consultas raw evitan las protecciones del ORM
db.query(`SELECT * FROM users WHERE id = ${req.params.id}`);

Prevención de Inyección SQL con Consultas Parametrizadas

La defensa principal contra la inyección SQL es usar consultas parametrizadas (también llamadas prepared statements). Estas separan el código SQL de los datos, asegurando que la entrada del usuario nunca se ejecute como comandos.

Aquí está el enfoque seguro en JavaScript:

// Seguro - consulta parametrizada
const query = 'SELECT * FROM products WHERE name = ?';
db.query(query, [userInput]);

Y en Python:

# Seguro - consulta parametrizada
cursor.execute("SELECT * FROM products WHERE name = %s", (user_input,))

La base de datos trata el parámetro como un valor literal, no como código SQL. Incluso si alguien ingresa '; DROP TABLE products;--, la base de datos busca un producto con exactamente esa cadena como nombre.

Lo Que las Consultas Parametrizadas No Cubren

Los identificadores (nombres de tablas, nombres de columnas) no pueden ser parametrizados. Si necesitas ordenamiento dinámico:

// Usa una lista blanca para nombres de columnas
const allowedColumns = ['name', 'price', 'created_at'];
const sortColumn = allowedColumns.includes(userInput) ? userInput : 'name';
const query = `SELECT * FROM products ORDER BY ${sortColumn}`;

Los procedimientos almacenados solo son seguros si evitan SQL dinámico internamente. Un procedimiento almacenado que concatena cadenas es igual de vulnerable.

Por Qué el Escape No es Suficiente

El escape manual de cadenas y la sanitización son insuficientes por sí solos. Son propensos a errores, específicos de cada base de datos y fáciles de implementar incorrectamente. Las consultas parametrizadas manejan el escape automática y correctamente.

Defensa en Profundidad

Las consultas parametrizadas son tu defensa principal, pero las capas adicionales ayudan:

  • Cuentas de base de datos con privilegios mínimos: El usuario de base de datos de tu aplicación no debería tener permisos administrativos como DROP, ALTER o CREATE USER en producción
  • Capa de acceso a datos centralizada: Enruta todas las consultas a través de un único módulo que imponga la parametrización
  • Revisión de código y pruebas: Incluye verificaciones de inyección SQL en tu proceso de revisión y pipeline de CI

La guía moderna de OWASP ASVS y la iniciativa “Secure by Design” de CISA enfatiza construir la seguridad en tu proceso de desarrollo en lugar de añadirla después.

Conclusión

La inyección SQL sigue siendo peligrosa porque es fácil de introducir y devastadora cuando se explota. La solución es directa: usa consultas parametrizadas para todas las operaciones de base de datos, valida identificadores con listas blancas y nunca confíes en la entrada del usuario.

Haz de las consultas parametrizadas tu opción predeterminada. Tu yo futuro—y tus usuarios—te lo agradecerán.

Preguntas Frecuentes

Sí. La inyección NoSQL es una vulnerabilidad relacionada que afecta a bases de datos como MongoDB. Los atacantes pueden manipular operadores de consulta a través de la entrada del usuario. La defensa es similar: usa los métodos de consulta integrados del driver de la base de datos en lugar de construir consultas a partir de cadenas, y valida toda la entrada del usuario antes de usarla en consultas.

Los ORMs proporcionan protección cuando usas sus métodos de consulta estándar. Sin embargo, la mayoría de los ORMs ofrecen funciones de consulta raw que evitan estas protecciones. Si usas métodos SQL raw o interpolación de cadenas dentro de consultas ORM, sigues siendo vulnerable. Siempre usa métodos parametrizados incluso dentro del código ORM.

Usa herramientas automatizadas como SQLMap o OWASP ZAP para escanear vulnerabilidades. Incluye pruebas unitarias enfocadas en seguridad que intenten payloads de inyección. Realiza revisiones de código verificando específicamente la concatenación de cadenas en consultas. Considera pruebas de penetración para aplicaciones críticas antes del despliegue en producción.

No. La validación de entrada ayuda a reducir la superficie de ataque pero nunca debería ser tu única defensa. Los atacantes inteligentes a menudo pueden eludir las reglas de validación. Las consultas parametrizadas son la defensa principal porque fundamentalmente separan el código de los datos. Usa la validación como una capa adicional, no como un reemplazo para la construcción adecuada de consultas.

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay