Glosario

Qué es la inyección SQL (SQLi): un fallo que permite que la entrada reescriba los comandos de tu base de datos

La inyección SQL ocurre cuando la entrada del usuario cambia una consulta y permite leer, alterar o borrar datos. Cómo evitarla con sentencias preparadas.

Publicado 2026-06-08 Actualizado 2026-10-06 10 min de lectura
En esta página

La inyección SQL es cuando el texto que escribes en un cuadro de búsqueda se convierte en «parte de un comando» para la base de datos. Aquí tienes cómo funciona y cómo prevenirla de forma fiable (sin pasos de ataque).

Por qué ocurre (el mecanismo)

La causa raíz es construir SQL por concatenación de cadenas. Mezcla la entrada directamente en la sentencia y puede cruzar la «frontera del valor» y actuar como comando.

✗ Concatenación de cadenas (peligroso)

Entrada concatenada en la sentencia → la entrada puede leerse como parte del comando

✓ Marcadores de posición (seguro)

Valores pasados a las ranuras ? / $1 como datos → siempre siguen siendo valores

Concatena la entrada y cruza la frontera del valor, cambiando el comando. Con marcadores de posición, un valor sigue siendo un valor.

El daño es «todo lo que esa conexión a la BD pueda hacer». Si la BD interna es alcanzable, es una causa clásica de fugas masivas, junto a RCE y SSRF.

Defensa

1

Usa marcadores de posición / sentencias preparadas (lo más importante)

No construyas SQL por concatenación. Pasa los valores mediante ? / parámetros con nombre como 'datos'. Esto por sí solo elimina casi toda la clase.

2

Apóyate en tu ORM / constructor de consultas

La mayoría de los ORMs usan marcadores de posición por defecto. Cuanto menos SQL en bruto escribas a mano, más seguro. Si debes escribir SQL en bruto, parametrízalo siempre.

3

Usuario de BD con privilegios mínimos

No concedas al usuario de BD de la aplicación derechos innecesarios (DROP, otras tablas, operaciones de administración). Aunque haya brecha, contén el daño.

4

Validación de entrada como complemento

Las comprobaciones de tipo/longitud/formato ayudan, pero no las conviertas en tu defensa principal; complementan a los marcadores de posición.

La visión de este sitio: deja de construir SQL en bruto a mano para empezar

La SQLi se conoce desde hace mucho tiempo y aun así sigue ocurriendo, porque es «cómodo» mezclar la entrada en la sentencia. La postura de este sitio es clara: nunca crees un lugar donde los valores se concatenen como cadenas dentro del SQL. Haz de los marcadores de posición o de un ORM el valor predeterminado y esta vulnerabilidad queda prevenida por diseño. No dependas del escape manual.

Cuidado: los marcadores de posición solo llevan valores

Los marcadores de posición no lo cubren todo. Solo llevan valores, no la estructura de la consulta, como los nombres de tabla, los nombres de columna o la dirección del ORDER BY (asc/desc).

Cuando necesitas esos elementos dinámicos (por ejemplo, dejar que un usuario elija una «clave de ordenación» o «columna de filtro»), no metas la entrada en el SQL: selecciona el nombre real de una lista de permitidos predefinida. La «columna de ordenación» y la «columna de filtro» son los puntos ciegos clásicos donde la gente asume que los marcadores de posición los cubren.

La entrada no son solo los campos de formulario

Si crees que solo hay que tener cuidado con los cuadros de búsqueda y los formularios de inicio de sesión, se te escaparán casos. Los valores que pueden acabar en el SQL llegan por muchas vías.

  • Cabeceras HTTP (User-Agent, X-Forwarded-For, etc.) y cookies
  • Valores dentro del JSON que recibe una API, o leídos de archivos
  • Valores guardados una vez en la base de datos y que otro proceso mete después en SQL

Lo último se llama inyección SQL de segundo orden. Aunque usaras marcadores de posición al guardar, funciona cuando otro proceso concatena después como cadena el valor guardado. No supongas «viene de mi propia base de datos, así que es seguro»: usa marcadores de posición en todos los sitios donde se construye SQL.

El artículo de este sitio sobre la suplantación de X-Forwarded-For describe un caso en el que llegaron cadenas de sondeo en una cabecera, pero la inyección SQL no funcionó porque el valor se pasaba con un marcador de posición.

En qué se diferencia de términos parecidos

Los fallos con «inyección» en el nombre comparten una cosa: la entrada se lee como comandos de otro lenguaje. La diferencia es dónde acaba la entrada.

TérminoDónde acaba la entradaDaño principalSolución de raíz
Inyección SQLComandos a la base de datos (SQL)Leer, alterar o borrar datosMarcadores de posición
XSSHTML o JavaScript que se ejecuta en el navegador del usuarioAbuso de la sesión del usuario, contenido falso en la páginaEscape en la salida y CSP
Inyección de comandos del SOComandos que ejecuta la shell del servidorEjecutar cualquier comando en el servidor (RCE)Usar APIs que no pasan por la shell y pasan los argumentos por separado

La inyección SQL y el XSS suelen mencionarse juntos, pero el daño cae en sitios distintos: la inyección SQL afecta a la base de datos del servidor, el XSS al navegador del usuario.

Cómo se llaman los marcadores de posición en los principales lenguajes

Todos los lenguajes principales ofrecen una forma de pasar los valores por separado. Úsalo como guía de qué función usar en tu stack.

Lenguaje / bibliotecaFunción que usar
Java (JDBC)PreparedStatement con métodos para fijar valores como setString
PHP (PDO)Prepara la sentencia con prepare y pasa los valores a execute
Python (DB-API)Pasa los valores como segundo argumento de execute (no los incrustes en la cadena SQL)
Node.js (pg y otros)Símbolos numerados como $1, con los valores pasados en un array aparte
C# (.NET)Añade los valores a los Parameters del comando

En cualquier lenguaje, en el momento en que incrustas valores en el SQL con plantillas de texto o +, esta protección deja de funcionar.

Casos reales: grandes fugas que empezaron con inyección SQL en un producto

La inyección SQL se conoce desde hace mucho tiempo y aun así sigue causando fugas importantes. Los casos recientes no suelen empezar en código escrito por la propia organización, sino en un producto que usaba.

CasoQué ocurrió
MOVEit Transfer (2023)Se explotó una inyección SQL en un producto de transferencia de archivos (CVE-2023-34362) antes de que existiera un parche, y se robaron datos de más de 2.700 organizaciones, muchas de ellas a través de proveedores (→ la brecha masiva de datos de MOVEit)
Metabase (2026)Se explotó una inyección SQL sin autenticación en una herramienta de análisis (CVE-2026-72898) antes del parche, y varias empresas comunicaron fugas de datos de clientes (→ explotación de Metabase y fugas, en inglés)
Núcleo de WordPress (2026)Combinada con otro fallo, una inyección SQL sin autenticación podía llevar a RCE (→ CVE-2026-63030 explicada, en inglés)

Tras MOVEit, las agencias estadounidenses CISA y FBI emitieron en marzo de 2024 una alerta que instaba a los fabricantes de software a eliminar la inyección SQL, y señalaba las consultas parametrizadas (marcadores de posición) como la solución. En el «CWE Top 25» de MITRE publicado en diciembre de 2025, la inyección SQL (CWE-89) ocupa el segundo lugar.

Qué hacer depende de tu papel

Tu papelLo primero que hay que hacer
Escribes SQL (desarrollador)Deja de concatenar cadenas; usa marcadores de posición o un ORM. Revisa cómo se usan las funciones de SQL en bruto
Gestionas productos o software de código abierto (operador)Comprueba los CVE y el estado KEV de los productos que gestionas y actualiza a versiones corregidas. Reduce las pantallas de administración accesibles desde internet
AmbosDa al usuario de base de datos de la aplicación privilegios mínimos, limitados a las tablas y columnas que necesita

Cómo comprobar tu propio entorno

No necesitas escribir cadenas de ataque para comprobarlo. El código y la configuración te dicen lo que necesitas.

1

Busca SQL construido a partir de cadenas

Busca en tu código líneas donde aparezcan palabras clave de SQL (SELECT, WHERE, etc.) junto con concatenación de cadenas o plantillas de texto. Comprueba también que no se concatene ninguna entrada en funciones de SQL en bruto ($queryRawUnsafe, whereRaw, DB::raw, raw() y similares).

2

Anota lo que puede hacer el usuario de la base de datos

Comprueba a qué tablas puede acceder el usuario de base de datos de la aplicación y qué puede hacer en ellas (SHOW GRANTS en MySQL, information_schema.role_table_grants en PostgreSQL). Quita los permisos de borrado que no necesite y cualquier acceso a tablas de otras aplicaciones.

3

Compara los productos que usas con los datos de vulnerabilidades

Usa la consulta de CVE / KEV para ver las vulnerabilidades conocidas de los productos que gestionas y si se están explotando. Para las dependencias, osv-scanner las revisa todas de una vez.

4

Asegúrate de que los errores SQL no se muestran en pantalla

En producción, confirma que los mensajes de error de la base de datos no se muestran tal cual a los usuarios. Guarda los errores detallados solo en los registros.

5

Busca señales en tus registros

Comprueba si los errores de sintaxis de la base de datos han aumentado de repente, y si tu WAF o tus registros de acceso muestran una serie de peticiones desde el mismo origen con cadenas que parecen SQL. Si es así, revisa primero el código de esa ruta.

Fuentes (primarias)

  • OWASP, «SQL Injection Prevention Cheat Sheet» (en inglés) — cheatsheetseries.owasp.org (marcadores de posición, precauciones con los procedimientos almacenados, validación de entrada con lista de permitidos, privilegios mínimos, no depender del escape)
  • CISA y FBI, «Secure by Design Alert: Eliminating SQL Injection Vulnerabilities in Software» (25 de marzo de 2024, en inglés) — cisa.gov
  • MITRE, «2025 CWE Top 25 Most Dangerous Software Weaknesses» (diciembre de 2025, en inglés) — cwe.mitre.org (CWE-89 en segundo lugar)
  • IPA, «How to Secure Your Web Site» — ipa.go.jp (en japonés; soluciones fundamentales frente a mitigaciones)

Sigue leyendo

Clasificación

CWE clasifica los tipos de debilidad que causan las vulnerabilidades; MITRE ATT&CK clasifica las técnicas de los atacantes. Ambos los mantiene MITRE y se usan ampliamente para etiquetar vulnerabilidades y reglas de detección.

ATT&CK (técnica de ataque)

CWE™: Copyright © 2006–2026, The MITRE Corporation. CWE is a trademark of The MITRE Corporation. The MITRE Corporation hereby grants you a non-exclusive, royalty-free license to use CWE for research, development, and commercial purposes. Any copy you make for such purposes is authorized on the condition that you reproduce MITRE’s copyright designation and this license in any such copy. CWE Terms of Use

© 2026 The MITRE Corporation. This work is reproduced and distributed with the permission of The MITRE Corporation. MITRE ATT&CK and ATT&CK are registered trademarks of The MITRE Corporation. The MITRE Corporation (MITRE) hereby grants you a non-exclusive, royalty-free license to use ATT&CK® for research, development, and commercial purposes. Any copy you make for such purposes is authorized provided that you reproduce MITRE's copyright designation and this license in any such copy. ATT&CK Terms of Use

FAQ

Q¿Qué ocurre con la inyección SQL?
A

Un atacante cambia el significado de una consulta a la base de datos: leer datos que deberían estar ocultos, alterarlos o, en el peor caso, borrarlo todo o eludir la autenticación. Es una causa clásica de fugas masivas de datos.

Q¿Cuál es la defensa más fiable?
A

Pasar los valores mediante marcadores de posición (sentencias preparadas). No construyas SQL con concatenación de cadenas; pasa los valores como 'datos' por un canal separado para que la entrada nunca pueda leerse como comando. La mayoría de los ORMs lo hacen por defecto.

Q¿Basta con escapar la entrada?
A

El escape manual es propenso a errores y se desaconseja. Usa marcadores de posición. Además, da al usuario de la BD privilegios mínimos para limitar el daño si algo se cuela.

QSi uso un ORM, ¿estoy a salvo?
A

En el uso normal, sí: los ORMs pasan los valores como marcadores de posición por defecto. Pero si concatenas la entrada en las funciones de SQL en bruto que ofrecen ($queryRawUnsafe de Prisma, whereRaw y DB::raw de Laravel, raw y extra de Django, etc.), el mismo fallo vuelve aunque uses un ORM. Las partes que los marcadores de posición no pueden llevar, como los nombres de columna de ordenación, también requieren cuidado.

Q¿Puede un WAF detener la inyección SQL?
A

Ayuda a reducir el daño, pero no es la solución de raíz. La guía «How to Secure Your Web Site» del IPA de Japón también separa la solución fundamental (marcadores de posición) de las mitigaciones que solo reducen el daño. Un WAF es una mitigación, y algunas entradas escapan a la detección. Úsalo como medida provisional hasta corregir el código.

Q¿Cómo sé si un producto que uso tiene inyección SQL?
A

Compara el nombre y la versión del producto con las vulnerabilidades publicadas (CVE) y con la lista KEV de CISA de vulnerabilidades con explotación confirmada. La consulta de CVE / KEV de este sitio permite hacerlo. Como mostraron MOVEit Transfer y Metabase, la inyección SQL en un producto que usas ha provocado grandes fugas de datos más de una vez.

Q¿Qué es una sentencia preparada?
A

Un mecanismo en el que primero se envía a la base de datos la forma de la sentencia SQL (dónde van los valores) y después, por separado, los valores. Los huecos para los valores se marcan con símbolos como ? o $1 (marcadores de posición). Como la forma se fija primero, los valores enviados después nunca se leen como comandos, contengan los caracteres que contengan.

Q¿Qué es la inyección SQL ciega?
A

Un tipo de inyección SQL en el que el contenido de la base de datos y los errores no se muestran en pantalla, pero aun así puede leerse información poco a poco a partir de diferencias en las respuestas o en los tiempos de respuesta. Que no se muestre nada en pantalla no hace seguro un sitio. La causa y la solución son las mismas que en la inyección SQL normal: con marcadores de posición no funciona.

Q¿Ocultar los mensajes de error lo evita?
A

No. Ocultar los errores hace el fallo más difícil de encontrar, pero como muestra la inyección SQL ciega, la información puede leerse aunque no aparezca ningún error. No debes mostrar errores en pantalla, pero aparte de eso sigues teniendo que corregir todos los sitios que construyen SQL concatenando cadenas.

Q¿Son seguros los procedimientos almacenados?
A

Bien escritos, son tan eficaces como los marcadores de posición. Pero OWASP advierte de que si un procedimiento construye SQL dentro de sí mismo concatenando la entrada (SQL dinámico), el mismo problema vuelve. Comprueba qué hace el procedimiento por dentro.