Cómo evitar ataques SQL Injection en Node.js
Seguridad Y Cumplimiento
21 abr 2026
Guía práctica para evitar inyección SQL en Node.js: parametriza consultas, usa ORMs, valida entradas, limita permisos y monitoriza.

La inyección SQL es una de las vulnerabilidades más comunes y peligrosas en aplicaciones web. Si trabajas con Node.js y bases de datos como MySQL o PostgreSQL, proteger tus consultas es esencial para evitar accesos no deseados, pérdida de datos o incluso control total de tu base de datos.
Resumen de medidas clave:
Evita concatenar cadenas en consultas SQL. Esto abre la puerta a entradas maliciosas.
Usa consultas parametrizadas. Son la mejor defensa contra ataques SQL Injection.
En MySQL: Utiliza
mysql2con marcadores como?o:nombre.En PostgreSQL: Usa
pgcon marcadores numerados como$1.
Implementa ORMs o Query Builders. Herramientas como Prisma o Sequelize gestionan automáticamente la seguridad en las consultas.
Valida y sanea las entradas del usuario. Librerías como
express-validatoro Joi ayudan a garantizar que los datos sean seguros antes de procesarlos.Limita los permisos en la base de datos. Aplica el principio de mínimo privilegio para reducir el impacto de posibles ataques.
Monitorea y registra consultas. Herramientas como StackHawk o Snyk pueden detectar vulnerabilidades en tiempo real.
Ejemplo práctico:
En lugar de usar una consulta insegura como esta:
Usa una parametrizada:
Proteger tu aplicación no es opcional. Implementar estas prácticas no solo asegura tus datos, sino que también refuerza la confianza de tus usuarios.

Comparación de métodos de prevención SQL Injection en Node.js
How to Prevent SQL Injection Attacks in Node.js

Prevención de inyección SQL con consultas parametrizadas
Las consultas parametrizadas son una herramienta clave para proteger las bases de datos frente a ataques de inyección SQL. Este enfoque separa el código SQL de los datos proporcionados por los usuarios, empleando marcadores de posición, como ? en MySQL o $1 en PostgreSQL. Esto permite que el controlador de la base de datos maneje la consulta y los valores de manera independiente.
Scott Stroz, MySQL Developer Advocate en Oracle, lo explica de esta manera:
"When we parameterize a database query, we use placeholders in a query instead of constant values... By using parameters, we are telling MySQL the values should be treated as specific data types and not part of a command that should be executed."
Gracias a este método, incluso si un atacante intenta inyectar valores maliciosos, como ' OR '1'='1'; --, dichos valores serán tratados como cadenas literales. Esto neutraliza la capacidad de manipular la lógica de la consulta. A continuación, exploraremos cómo implementar este enfoque en MySQL y PostgreSQL.
Implementación de consultas parametrizadas en MySQL

En MySQL, la biblioteca mysql2 permite trabajar con marcadores de posición utilizando el símbolo ?. El método recomendado es usar .execute() en lugar de .query(), ya que .execute() emplea prepared statements directamente en la base de datos:
Para consultas más complejas, mysql2 admite marcadores de posición con nombre si se habilita la opción namedPlaceholders: true. Esto permite usar nombres descriptivos como :email o :status y pasar los valores como un objeto:
Implementación de consultas parametrizadas en PostgreSQL

En PostgreSQL, la biblioteca pg utiliza marcadores de posición numerados ($1, $2, etc.) y permite pasar los valores como un array en el segundo argumento de pool.query():
Para mejorar la claridad y facilitar la reutilización de consultas, pg también admite una sintaxis basada en objetos con las propiedades text y values:
Comparativa entre MySQL y PostgreSQL
Característica | MySQL ( | PostgreSQL ( |
|---|---|---|
Sintaxis de marcadores |
|
|
Implementación |
|
|
Paso de valores | Array u objeto (con | Array de valores |
Desactivación de sentencias múltiples
Es fundamental mantener deshabilitada la opción multipleStatements, que permite ejecutar varias consultas en una misma cadena separadas por punto y coma. Si se activa, un atacante podría incluir comandos maliciosos, como DROP TABLE, tras una entrada manipulada:
James Q Quick, Developer Advocate de PlanetScale, subraya la importancia de los marcadores de posición:
"By using placeholders, the malicious SQL will be escaped and treated as a raw string, not as actual SQL code."
Por último, evita usar literales de plantilla ES6 (backticks) o concatenar cadenas con el operador + para insertar datos del usuario en las consultas. Estas prácticas eliminan las protecciones que ofrecen las consultas parametrizadas y dejan la base de datos vulnerable.
Uso de ORMs y Query Builders para mayor seguridad
Los ORMs (Object-Relational Mapping) y los query builders son herramientas que, además de facilitar el manejo de bases de datos, añaden una capa de protección contra la inyección SQL. Esto lo logran al separar la lógica SQL de los datos proporcionados por el usuario, evitando que las entradas maliciosas se ejecuten como código. Plataformas como Prisma, Sequelize o Drizzle implementan consultas parametrizadas automáticamente, eliminando la necesidad de concatenar cadenas manualmente, lo cual es una de las principales causas de las vulnerabilidades de inyección SQL.
Como explica Najbudinalam, desarrollador de software:
"ORM tools automatically escape inputs, protecting against SQL injection attacks without requiring additional effort."
Sin embargo, es importante no confiar ciegamente en estas herramientas. Tal como señala el Node.js Security Blog:
"ORMs are just an added abstraction layer and its own code can still be prone to SQL injection attacks, if unhandled correctly."
El uso de funciones "raw" para consultas complejas puede reintroducir riesgos. Por eso, además de usar ORMs, es esencial validar las entradas y mantener las dependencias actualizadas para evitar problemas de seguridad conocidos.
Uso de Prisma para consultas type-safe

Prisma destaca por generar un cliente tipado que valida los parámetros en tiempo de ejecución. En el caso de consultas complejas, Prisma ofrece $queryRaw, que utiliza tagged template literals para parametrizar automáticamente las variables:
Jon Harrell, Developer Evangelist de Prisma, lo explica así:
"The Prisma SDK will automatically escape the value of untrustedInput to prevent SQL injection attacks [when using $queryRaw]."
Es importante evitar $queryRawUnsafe y funciones similares, ya que no escapan las entradas y son altamente vulnerables cuando se manejan datos no confiables. Durante el desarrollo, se recomienda habilitar el registro de consultas con log: ['query'] para verificar que las entradas estén correctamente parametrizadas.
Característica | Métodos estándar de Prisma |
|
|
|---|---|---|---|
Protección SQLi | Automática / Integrada | Automática (tagged templates) | Ninguna (Vulnerable) |
Manejo de entradas | Parámetros basados en objetos | Interpolación con plantillas etiquetadas | Concatenación de cadenas |
Uso recomendado | Operaciones CRUD generales | Consultas complejas | Solo consultas estáticas internas |
Uso de Sequelize o Drizzle

Sequelize también ofrece una protección robusta mediante el escapado automático de entradas y la abstracción de métodos. Para consultas raw, es posible usar los parámetros replacements o bind en sequelize.query() para mantener la separación entre código y datos:
Desde la versión 4.12, Sequelize deshabilitó los alias de cadena para operadores de consulta, reduciendo el riesgo de ataques como operator injection. Además, activar el registro de consultas con logging: true durante el desarrollo puede ayudar a verificar que las entradas estén correctamente manejadas.
Sea cual sea el ORM que uses, asegúrate de convertir las entradas del usuario a tipos primitivos (como cadenas o números) antes de pasarlas a los filtros. También es recomendable complementar estas medidas con bibliotecas de validación de esquemas como Zod o Joi. Estas prácticas, combinadas con las herramientas que ofrecen los ORMs, refuerzan la seguridad en aplicaciones Node.js.
Validación y sanitización de las entradas del usuario
Además de emplear consultas parametrizadas y ORMs, asegurar que las entradas del usuario sean verificadas y limpiadas es crucial. La validación se encarga de confirmar que los datos cumplen con un formato esperado (como un email válido), mientras que la sanitización elimina o escapa caracteres que podrían ser peligrosos.
Un dato importante: el 70% de los incidentes de seguridad están relacionados con un manejo deficiente de las entradas. Implementar una capa de sanitización puede reducir el riesgo de fallos de seguridad hasta un 85%. Sin embargo, confiar únicamente en validaciones del lado del cliente es un error común. De hecho, casi el 40% de los desarrolladores que no validan en el servidor han sufrido brechas de seguridad graves.
A continuación, exploramos dos herramientas populares para manejar estas tareas.
Validación con express-validator

Express-validator es un middleware diseñado para Express que permite validar y limpiar entradas mediante métodos como check(), body() o param(). Por ejemplo, para validar un nombre de usuario, puedes usar .trim() para eliminar espacios, .escape() para convertir caracteres especiales en entidades HTML, y .isAlphanumeric() para garantizar que solo contenga letras y números:
Cuando la validación falla, responde con un código 400 y mensajes claros. Esto no solo mejora la experiencia del usuario, sino que también puede incrementar las tasas de éxito en formularios hasta un 30%.
Uso de Joi para validación basada en esquemas

Para datos más complejos, Joi es una excelente opción. Este paquete permite definir esquemas que describen cómo deben estructurarse los datos. Además, soporta validación asíncrona, lo que resulta útil para verificar condiciones adicionales, como si un email ya está registrado en la base de datos:
Comparativa entre express-validator y Joi

Característica | express-validator | Joi |
|---|---|---|
Integración | Middleware específico de Express | Independiente (basado en esquemas) |
Sintaxis | Métodos encadenables en rutas | Definición de esquemas de objetos |
Mejor para | Validación rápida a nivel de ruta | Estructuras complejas y reutilizables |
Saneamiento | Integrado (a través de validator.js) | Limitado (enfocado en validación) |
Soporte asíncrono | Mediante validadores personalizados | Nativo con |
Sea cual sea la herramienta que elijas, recuerda siempre validar primero y sanear después. Rechaza los datos incorrectos antes de intentar limpiarlos. Por último, mantén tus herramientas actualizadas (por ejemplo, con npm audit) para detectar y solucionar posibles vulnerabilidades.
Limitación de permisos en la base de datos
Además de implementar consultas parametrizadas y realizar validaciones, es crucial limitar los privilegios de los usuarios en la base de datos para reducir el alcance de posibles ataques. Este enfoque sigue el Principio de Mínimo Privilegio (PoLP), que propone que cada usuario tenga únicamente los permisos indispensables para cumplir con su función.
Si un atacante logra ejecutar una inyección SQL, los permisos limitados pueden minimizar los daños. Por ejemplo, un usuario con privilegios restringidos no podrá ejecutar comandos destructivos como DROP TABLE o TRUNCATE, ni acceder a tablas del sistema, incluso si el atacante logra superar los filtros de entrada. Como lo explica Alan Sastre, ingeniero de software y CEO de CertiDevs:
"El principio de mínimo privilegio es una práctica de seguridad fundamental que consiste en otorgar a los usuarios únicamente los permisos necesarios para realizar sus tareas específicas".
A continuación, se describen los pasos para crear usuarios con permisos específicos en PostgreSQL y MySQL.
Creación de usuarios de base de datos con permisos restringidos
Siempre es preferible usar un usuario dedicado en lugar de cuentas administrativas. En PostgreSQL, puedes hacerlo así:
En MySQL, el proceso es similar:
Estos usuarios tendrán permisos limitados a SELECT, INSERT y UPDATE, sin acceso a comandos como DROP o ALTER. Para tareas de solo lectura, como reportes o consultas, puedes crear un usuario con permiso exclusivo de SELECT, reduciendo aún más el riesgo.
Alan Sastre también sugiere usar el comando REVOKE para eliminar permisos innecesarios de forma proactiva. Además, las credenciales de estos usuarios deben almacenarse en variables de entorno mediante herramientas como dotenv, evitando incluirlas directamente en el código fuente.
Comparativa de roles y permisos de base de datos
Tipo de rol | Permisos recomendados | Permisos prohibidos |
|---|---|---|
Usuario de aplicación |
|
|
Usuario de reportes |
|
|
Administrador de BD |
| N/A (usar con cautela) |
Rol de mantenimiento |
|
|
Revisa regularmente los permisos asignados con comandos como SHOW GRANTS en MySQL o consultando information_schema.table_privileges en PostgreSQL. Esto ayuda a identificar y eliminar accesos innecesarios que puedan haberse otorgado con el tiempo.
Limitar los privilegios de los usuarios es una capa adicional de seguridad que complementa la parametrización y validación, fortaleciendo la protección contra ataques de inyección SQL.
Monitorización y medidas de seguridad adicionales
Es fundamental complementar las estrategias de prevención con monitorización activa, lo que permite detectar intentos de inyección SQL de forma temprana. Matt Tanner, responsable de relaciones con desarrolladores en StackHawk, lo resume perfectamente:
"La seguridad no es una implementación única, sino un proceso continuo que requiere atención y mejora constante a medida que evolucionan las amenazas y se descubren nuevas vulnerabilidades."
La monitorización activa proporciona una visión en tiempo real de las actividades del sistema, ayudando a identificar patrones sospechosos. Herramientas como AppSignal destacan por su capacidad para rastrear accesos no autorizados o tráfico anómalo en la red. Omonigho Kenneth Jimmy recomienda esta herramienta por su integración eficiente y su capacidad para detectar irregularidades. Estas acciones deben combinarse con una estricta validación de entradas y un escape adecuado de datos para maximizar la seguridad.
Validación estricta de entrada y escape de salida
Una validación robusta asegura que los datos cumplen con los requisitos específicos de la aplicación. Esto incluye verificar el tipo, formato, longitud y rango de los datos. Por ejemplo, patrones regex como /^[a-zA-Z\s]+$/ son útiles para bloquear caracteres especiales, comillas o palabras clave SQL que podrían ser utilizadas en ataques.
El escape de salida, por otro lado, transforma los datos en cadenas literales, evitando que se ejecuten como código. Esto no solo protege contra inyecciones SQL, sino también contra ataques XSS. Además, implementar estrategias como el allowlisting, que compara las entradas del usuario con un conjunto predefinido de valores permitidos, añade otra capa de protección eficaz.
Revisiones de código y registro de consultas
Para fortalecer aún más la seguridad, es fundamental combinar la monitorización con revisiones periódicas de código y un registro detallado de consultas. Lucien Chemaly, autor en Snyk, subraya la importancia de estas prácticas:
"Participa en revisiones de código regulares y auditorías de seguridad para detectar vulnerabilidades temprano."
El registro de consultas es clave para identificar patrones sospechosos, como OR 1=1 o sentencias UNION. En bases de datos como PostgreSQL, configurar pgAudit con pgaudit.log = 'read, write' permite un seguimiento exhaustivo. Además, herramientas como StackHawk (desde 5 €/mes) se integran fácilmente en pipelines CI/CD, escaneando endpoints automáticamente en busca de vulnerabilidades de inyección SQL. Por su parte, Snyk ofrece una extensión para Visual Studio Code que proporciona retroalimentación inmediata mientras se escribe el código.
Otras medidas adicionales incluyen implementar rate limiting con middleware como express-rate-limit, que ralentiza los intentos automatizados de escaneo para descubrir puntos débiles. También es recomendable ejecutar regularmente comandos como npm audit o usar herramientas automatizadas para garantizar que las bibliotecas ORM y los drivers de base de datos se mantengan actualizados con los últimos parches de seguridad.
Conclusión: prácticas clave para asegurar aplicaciones Node.js
Proteger una aplicación Node.js contra inyecciones SQL requiere abordar la seguridad desde varios frentes. Las consultas parametrizadas y las sentencias preparadas son fundamentales, ya que garantizan que las entradas se traten como datos y no como código ejecutable. Usar ORMs modernos como Prisma o Drizzle también ayuda a evitar errores derivados de la construcción manual de consultas.
La validación de entradas y la gestión de permisos son otros pilares esenciales. Herramientas como Zod o express-validator funcionan como una barrera inicial, bloqueando datos maliciosos antes de que lleguen a la base de datos. Esto debe complementarse con el principio de mínimo privilegio, configurando usuarios de base de datos con permisos estrictamente necesarios para minimizar daños en caso de que se produzca un ataque.
La importancia de un enfoque integral queda clara en palabras de Matt Tanner de StackHawk:
"La clave para una prevención eficaz de la inyección SQL radica en adoptar un enfoque de defensa en profundidad, utilizando múltiples capas de protección".
Este enfoque incluye integrar herramientas automatizadas en el pipeline de CI/CD, realizar auditorías periódicas con npm audit y emplear extensiones de IDE como Snyk para detectar vulnerabilidades mientras se escribe el código.
La seguridad no es un objetivo estático, sino un proceso continuo que debe adaptarse a las nuevas amenazas. Implementar estas prácticas desde el principio no solo protege los datos, sino que también ahorra tiempo y recursos a largo plazo en producción.
FAQs
¿Las consultas parametrizadas también protegen en ORDER BY o nombres de columna?
Las consultas parametrizadas son una herramienta eficaz para proteger los valores en los parámetros frente a inyecciones SQL. Sin embargo, tienen limitaciones importantes: no suelen proteger nombres de columna ni ciertas cláusulas como ORDER BY.
Esto ocurre porque esos elementos no se tratan como parámetros en la mayoría de las implementaciones. Por lo tanto, no están automáticamente protegidos contra posibles manipulaciones malintencionadas.
Para evitar vulnerabilidades, es fundamental realizar una validación y un control manual de estos casos. Asegúrate de verificar cuidadosamente cualquier entrada que pueda influir en nombres de columna o cláusulas específicas, y aplica medidas adicionales de seguridad según sea necesario. Esto ayudará a mantener la integridad de tu base de datos.
¿Cuándo debería usar SQL “raw” y cómo hacerlo sin riesgo?
Las consultas SQL "raw" son útiles, pero deben emplearse con cuidado y únicamente cuando sea necesario. Por ejemplo, son una buena opción para operaciones complejas que no se pueden resolver fácilmente con un ORM (Object-Relational Mapping) o con consultas parametrizadas estándar.
Sin embargo, hay un riesgo importante: la inyección SQL, que puede comprometer la seguridad de tu aplicación. Para minimizar este riesgo, siempre debes parametrizar las entradas del usuario. Usa marcadores de posición como ? o parámetros nombrados, y asegúrate de pasar los valores por separado. Esto garantiza que los datos proporcionados por el usuario no se interpreten ni se ejecuten como código.
¿Qué permisos mínimos necesita el usuario de base de datos de mi app?
El usuario de base de datos debe contar con permisos concretos y restringidos, ajustados a las tareas que necesita realizar. Por ejemplo:
Si solo necesita consultar información, otórgale permisos SELECT.
Para añadir o actualizar datos, asigna permisos INSERT y UPDATE únicamente en las tablas necesarias.
Evita otorgar permisos generales como ALL o ADMIN, ya que aumentan los riesgos de seguridad. Es fundamental limitar los accesos a las acciones y tablas imprescindibles para minimizar vulnerabilidades.