Consulta SQL de login con las variables $name y $pwd resaltadas y una jeringa que representa una inyección SQL.
Concatenar $name y $pwd en la consulta de login abre la puerta a una inyección SQL.

Inyección SQL: cómo funciona, tipos y cómo protegerte

La inyección SQL (SQLi) es una vulnerabilidad que permite a un atacante insertar código SQL malicioso a través de una entrada de una aplicación web para alterar las consultas que esta hace a su base de datos. Con ello puede leer datos confidenciales, saltarse la autenticación, modificar o destruir registros y, en el peor de los casos, tomar el control del servidor.

Las inyecciones SQL son uno de los ataques más estudiados y fáciles de entender contra un sitio o aplicación web. Y sin embargo siguen siendo extrañamente comunes. En el laboratorio las reproducimos contra entornos de prácticas como DVWA para entender cómo piensa el atacante y, sobre todo, para cerrarle la puerta. El truco es viejo, pero sigue funcionando.

¿Buscas los payloads listos para usar? Si ya tienes claro el concepto y vienes a por la parte práctica, salta directo a nuestro recurso práctico: Payloads de inyección SQL — el cheat sheet por objetivo y por motor (MySQL, PostgreSQL, SQL Server, Oracle y SQLite).

¿Sigue siendo relevante la inyección SQL Hoy?

Sí, y los datos lo respaldan. Durante años la inyección encabezó el OWASP Top 10 (puesto #1 en 2013 y 2017). En la edición de 2021 bajó al puesto #3 (A03:2021) y en la edición vigente, OWASP Top 10:2025, figura como A05:2025, en el puesto #5 — no porque haya dejado de ser peligrosa, sino porque otras categorías ganaron prevalencia (fuente: A05:2025 Injection en OWASP Top 10:2025).

Si miramos el indicador más directo de peligrosidad real, el panorama es aún más claro: en el MITRE CWE Top 25 de 2025, la inyección SQL (CWE-89) es la segunda debilidad de software más peligrosa del año, analizada sobre más de 39.000 CVEs publicadas entre mediados de 2024 y 2025. SQLi sigue siendo una vulnerabilidad de baja frecuencia pero altísimo impacto.

El origen de la vulnerabilidad: el lenguaje SQL

Para entender por qué es posible la inyección SQL hay que entender qué pasa cuando un usuario envía datos a un sitio web. Esos datos entran en la aplicación, que a su vez los utiliza para acceder a la base de datos.

Para consultar datos en bases de datos relacionales se utiliza SQL (structured query language, o lenguaje de consulta estructurado), un lenguaje especial similar al lenguaje natural. Está normalizado y sus comandos básicos son los mismos para los distintos proveedores de SGBD: Microsoft SQL Server, Oracle, MySQL, PostgreSQL y otros. Por ejemplo, esta consulta lee todos los usuarios creados después de una fecha:

SELECT Id, Name FROM Users WHERE CreationDate > '2023-02-01'

Y esta elimina los usuarios con un id superior a 10:

DELETE FROM Users WHERE Id > 10

Así, la aplicación web utiliza los parámetros que introduce el usuario para acceder a sus datos. Si alguien busca portátiles e introduce la palabra “hacking” en la barra de búsqueda, la consulta correspondiente podría tener este aspecto simplificado:

SELECT Id, Name, Category FROM Blog WHERE Name LIKE 'hacking%'

La sentencia LIKE y el carácter comodín (%) se usan para especificar una condición de coincidencia parcial sobre una cadena. En un catálogo de productos, ese mismo patrón puede terminar implementado con una consulta construida por concatenación directa de la entrada del usuario (ejemplo en C#):

var selectProductQuery =
@"SELECT Id, Name, Price FROM Products WHERE Name LIKE '" + productName + "%'";

Ese + productName + es la grieta. El valor introducido por el usuario queda interpolado dentro del texto SQL antes de ejecutarse, de modo que puede dejar de comportarse como dato y pasar a modificar la estructura de la consulta. Y ahí empieza el ataque.

¿Cómo funciona la inyección SQL? Ejemplo clásico

La inyección SQL es una técnica de inyección de código que se usa para modificar o extraer datos de bases de datos SQL. Insertando sentencias SQL especializadas en un campo de entrada, un atacante puede hacerse pasar por un administrador, alterar los datos existentes, modificar transacciones y balances, o recuperar y destruir todos los datos del servidor.

Supongamos que el atacante, en lugar del nombre del producto, introduce este fragmento en el cuadro de búsqueda:

a'; DROP TABLE Products; --

Al concatenarse dentro de la consulta vulnerable, esa entrada cierra la cadena que usaba LIKE, introduce una segunda sentencia y comenta el fragmento restante:

SELECT Id, Name, Price FROM Products WHERE Name LIKE 'a';
DROP TABLE Products;
--%'

¿Qué ocurrió? El texto del formulario dejó de comportarse como un patrón de búsqueda y alteró la estructura de la consulta: primero fuerza el cierre de la condición LIKE, después introduce una sentencia maliciosa que borra la tabla de productos y, por último, neutraliza el resto del SQL original con un comentario.

Esa es la esencia de cualquier inyección SQL: el atacante inyecta código malicioso en un canal de entrada legítimo (un formulario, la barra de direcciones) y fuerza a la aplicación a realizar acciones autodestructivas o a entregar datos no autorizados.

Flujo conceptual de una inyección SQL alterando la estructura de una consulta a la base de datos
Cuando la entrada del usuario se concatena en la consulta, deja de ser un dato y pasa a ser código ejecutable.

Tipos de inyección SQL

Los ataques de SQLi se clasifican según cómo recibe el atacante la información de vuelta. Esta es la taxonomía estándar, la que usamos para diagnosticar:

CategoríaTipoCómo obtiene el dato el atacante
In-band (en banda)Basada en UNIONCombina los resultados de otra consulta en la respuesta visible
In-bandBasada en erroresExtrae datos a partir de los mensajes de error del SGBD
Inferencial / a ciegas (blind)BooleanaDeduce la información por cambios en la respuesta (verdadero/falso)
Inferencial / a ciegas (blind)Basada en tiempoDeduce la información por el retraso de la respuesta
Out-of-band (OOB)Fuera de bandaEl dato sale por un canal externo (p. ej. una petición DNS o HTTP)

A continuación tienes ejemplos de inyección SQL, uno por tipo (o una descripción cuando no aplica un payload). Los ejemplos usan sintaxis de MySQL; las variantes equivalentes para PostgreSQL, SQL Server, Oracle y SQLite están en el cheat sheet práctico de payloads, junto con el inventario completo de payloads, evasión de WAF y técnicas avanzadas.

Taxonomía de los tipos de inyección SQL: in-band, inferencial a ciegas y out-of-band
Los tipos de inyección SQL se agrupan según cómo recibe el dato el atacante.

Basada en UNION (in-band)

La palabra clave UNION combina los resultados de dos o más consultas en uno solo. Haciendo coincidir el número de columnas, un atacante puede sacar casi cualquier dato:

' UNION SELECT NULL, username, password FROM users --

Basada en errores (in-band)

Cuando la aplicación muestra los errores del motor en pantalla, esos mensajes se pueden provocar de forma controlada para que filtren información:

' AND updatexml(null, concat(0x7e, (SELECT @@version)), null) --

Booleana (blind)

Si no hay resultados ni errores visibles —lo que se conoce como inyección SQL ciega—, se infiere la información preguntando condiciones verdadero/falso y observando si la página cambia:

' AND SUBSTRING(user(), 1, 1) = 'a' --

Basada en tiempo (blind)

Cuando ni siquiera cambia la respuesta, se mide el tiempo: si la condición es verdadera, la base de datos “duerme” unos segundos.

' AND IF(SUBSTRING(user(),1,1)='r', SLEEP(5), 0) --

Fuera de banda (OOB)

El caso menos frecuente: se fuerza al servidor de base de datos a emitir una petición (por ejemplo, una resolución DNS) hacia un servidor controlado por el atacante, que de paso lleva incrustado el dato robado. Requiere que la red permita esa salida y, a menudo, privilegios elevados.

El uso de comentarios (--, #, /* */) para “cortar” el resto de la consulta atraviesa casi todos estos tipos. Cada motor tiene sus matices, que se detallan por DBMS en el cheat sheet práctico.

¿Qué tan grave puede ser? Casos reales

La inyección SQL no es un problema de museo. Algunos casos recientes con impacto verificable:

  • MOVEit Transfer (2023). El grupo Cl0p explotó una inyección SQL de día cero (CVE-2023-34362) en la solución de transferencia de archivos MOVEit. Comprometió a más de 960 organizaciones y expuso datos personales de decenas de millones de personas — uno de los mayores incidentes de cadena de suministro de la década (fuente: aviso CISA AA23-158A).
  • Campaña ResumeLooters (2023-2024). Un grupo usó SQLi combinada con XSS contra al menos 65 sitios de empleo y retail, robando más de 2 millones de registros con correos, teléfonos y datos personales (fuente: Group-IB).
  • FortiWeb (2025). La CVE-2025-25257, una inyección SQL en el WAF de Fortinet con CVSS 9.8, fue añadida al catálogo de vulnerabilidades explotadas conocidas de CISA.
  • PostgreSQL (2025). La CVE-2025-1094 demostró que ni los componentes maduros están exentos: un fallo en las funciones de quoting del cliente libpq (PQescapeLiteral, PQescapeString, etc.) permitía inyección SQL mediante secuencias UTF-8 inválidas cuando su salida se pasaba a la utilidad psql. Fue encadenada en ataques reales (fuente: advisory oficial de PostgreSQL).

Si quieres entender cómo se rastrean y clasifican estos fallos, revisa nuestra guía sobre qué es un CVE.

Protección contra la inyección SQL

La buena noticia: prevenir la inyección SQL no requiere expresiones regulares ingeniosas ni trucos de escape. Requiere una garantía estructural —separar el código de los datos— y unas cuantas capas de apoyo. Esto es lo que de verdad funciona:

1. Consultas parametrizadas (la regla de oro)

Los datos enviados por el usuario no deben intervenir en la formación del texto de la consulta. En lugar de concatenar la entrada, se usan parámetros. Frente al ejemplo vulnerable de antes, la forma correcta en C# separa la plantilla SQL del valor recibido:

command.CommandText =
@"SELECT Id, Name, Price FROM Products WHERE Name LIKE @p_productName";
command.Parameters.AddWithValue("p_productName", productName + "%");
var reader = command.ExecuteReader();

Así, escriba lo que escriba el usuario, la aplicación lo trata como un valor asociado al parámetro, no como una ampliación del texto SQL. Si contiene expresiones SQL, estas no cambian la estructura de la consulta: quedan dentro del valor usado por @p_productName. El mismo principio aplica a cualquier lenguaje:

# Python (psycopg2)
cur.execute("SELECT * FROM users WHERE username = %s", (user_input,))

2. Procedimientos almacenados

Cuando se construyen sin SQL dinámico inseguro, cumplen el mismo objetivo defensivo: el código del procedimiento vive en la propia base de datos y la entrada del usuario se entrega como parámetro, no como texto concatenado para formar una sentencia nueva.

3. Lista blanca de validación

Cuando no se puede parametrizar (por ejemplo, el nombre de la tabla de un SELECT no admite parámetro), se limita la lista de valores válidos. Si llega un valor inesperado, probablemente estamos ante un intruso probando la aplicación.

4. Validar la entrada del usuario

El principio rector: el usuario es siempre un atacante potencial. El backend no puede confiar en nada del cliente, aunque la aplicación cliente valide la entrada: un atacante puede enviar peticiones directas a la API (con herramientas como Swagger, un proxy o scripts), saltándose por completo la validación del lado cliente.

5. Privilegios mínimos

La cuenta del sistema que accede a los datos debe tener el menor número posible de privilegios. No debería poder leer ni crear archivos ajenos a la aplicación, ni ejecutar DROP TABLE, GRANT, xp_cmdshell ni acceder al sistema de archivos. Esto limita el radio de impacto si algo falla.

6. Ocultar los errores detallados

Deshabilitar la visualización de errores del motor en pantalla corta el canal más directo de la SQLi basada en errores, porque impide que los mensajes del SGBD lleguen al atacante como respuesta útil.

La referencia canónica de prevención es el Cheat Sheet de Prevención de Inyección SQL de OWASP.

Herramientas para detectar inyección SQL

Siempre es buena idea probar la resistencia de tu aplicación. La utilidad de referencia para encontrar y explotar fallos SQLi es sqlmap. Además, en el blog hemos cubierto otras opciones que vale la pena tener a mano:

  • jSQL Injection: inyección automática de bases de datos SQL con interfaz gráfica.
  • SQLiFinder: escáner de vulnerabilidad de inyección SQL.

Y conviene ubicar la SQLi dentro del panorama más amplio de las vulnerabilidades web.

Preguntas frecuentes sobre la inyección SQL

¿La inyección SQL es ilegal?

Probar inyecciones SQL contra sistemas de terceros sin autorización expresa es un delito. Es legal y legítimo únicamente en entornos propios, laboratorios de prácticas, competiciones CTF y programas de bug bounty autorizados. Todo lo que enseñamos aquí está pensado para aprender a defender.

¿Sigue siendo común la inyección SQL?

Sí. En el MITRE CWE Top 25 de 2025, la inyección SQL (CWE-89) es la segunda debilidad de software más peligrosa del año, y sigue apareciendo en brechas e incidentes recientes como los de MOVEit y FortiWeb.

¿Cuál es la diferencia entre inyección SQL y XSS?

La inyección SQL ataca la base de datos alterando las consultas del backend. El ataque XSS ataca al navegador de otros usuarios inyectando scripts en el contenido de la página. Comparten la causa raíz —datos no confiables tratados como código— pero el objetivo y el impacto son distintos.

¿Qué es una inyección NoSQL?

Es el equivalente de la SQLi en bases de datos no relacionales (MongoDB, etc.). Si tu aplicación consume JSON, conviene sondearla también por inyección NoSQL como vector separado del flujo SQLi.

¿Cómo sé si mi web es vulnerable?

El sondeo más básico es probar una comilla simple (') en un parámetro y observar si la aplicación altera su respuesta normal, por ejemplo mostrando un error distinto al habitual. Ese comportamiento no confirma por sí solo la explotación, pero sí justifica profundizar la prueba. A partir de ahí, herramientas como sqlmap automatizan la detección.

Lo esencial de la inyección SQL

La esencia del ataque es siempre la misma: un atacante intenta inyectar código SQL malicioso a través de un canal de entrada legítimo. La forma más eficaz de protegerse es no usar nunca la entrada del usuario para construir la consulta, sino solo como valor de un parámetro; validar siempre en el backend, partiendo de que el usuario es un intruso; y mantener una inspección regular de tu aplicación con herramientas como sqlmap.

Con estos principios y ejemplos tienes lo esencial del concepto. Cuando quieras bajar al terreno —payloads concretos por objetivo y por motor, evasión de WAF, blind, OOB y RCE— ese es el territorio de nuestro cheat sheet de payloads de inyección SQL.

Mi Carro Close (×)

Tu carrito está vacío
Ver tienda