Uno de los ataques más famosos, peligrosos y antiguos de internet sigue siendo increíblemente efectivo hoy en día.

Se llama:

SQL Injection (SQLi)


Y aunque existen desde hace décadas, todavía miles de aplicaciones vulnerables siguen siendo explotadas diariamente.

La razón es simple:

Muchos desarrolladores aún concatenan datos directamente en consultas SQL sin validar correctamente la entrada del usuario.


En esta guía aprenderás:

  • qué es SQL Injection
  • cómo funciona
  • ejemplos reales
  • por qué es tan peligroso
  • cómo prevenirlo correctamente
  • errores comunes
  • mejores prácticas modernas

Todo explicado paso a paso.



¿Qué es SQL Injection?


SQL Injection es un ataque donde un atacante manipula consultas SQL enviando datos maliciosos a través de formularios, URLs o parámetros.

El objetivo suele ser:

  • acceder a información privada
  • saltar autenticación
  • modificar datos
  • eliminar tablas
  • obtener usuarios y contraseñas
  • tomar control parcial de la aplicación

¿Por qué ocurre?


Porque la aplicación mezcla:

❌ datos del usuario

con

❌ código SQL


Ejemplo vulnerable clásico


Supongamos este login en PHP:

$user = $_POST['user'];
$pass = $_POST['pass'];

$sql = "SELECT * FROM users 
        WHERE username = '$user' 
        AND password = '$pass'";

Parece normal.


Pero aquí está el problema:

Los datos del usuario entran directamente en la consulta.



Cómo lo explota un atacante

En vez de escribir una contraseña normal:

' OR '1'='1

La consulta final queda así:

SELECT * FROM users 
WHERE username = 'admin'
AND password = '' OR '1'='1'


Como '1'='1' siempre es verdadero:

✅ el login puede ser bypassed

✅ entra sin contraseña


Qué puede lograr un atacante con SQLi

Muchísimo más que saltarse un login.



1. Robar bases de datos

Usuarios

emails

teléfonos

direcciones

tarjetas

hashes


2. Obtener credenciales admin

Muchos ataques buscan paneles administrativos.



3. Borrar información

Ejemplo peligroso:

DROP TABLE users;

4. Modificar datos

Cambiar precios

inventario

roles

contraseñas


5. Obtener acceso al servidor


En algunos casos extremos:

  • ejecutar comandos
  • subir archivos
  • obtener shell

Tipos de SQL Injection


SQL Injection clásica

La más conocida.

Manipulación directa de consultas.



Blind SQL Injection

La aplicación no muestra errores.

Entonces el atacante usa:

  • tiempos de respuesta
  • respuestas verdaderas/falsas
  • comportamiento del sistema

para extraer información lentamente.



Time-Based SQLi


Ejemplo:

SLEEP(5)

Si la respuesta tarda 5 segundos:

✅ sabe que la inyección funcionó.


Error-Based SQLi

La aplicación muestra errores SQL directamente.


Ejemplo típico:

You have an error in your SQL syntax

Eso ayuda muchísimo al atacante.



Union-Based SQLi

Usa UNION SELECT para combinar consultas.


Ejemplo:

UNION SELECT username,password FROM users

Ejemplo real en URL


Aplicación vulnerable:

https://sitio.com/product.php?id=10


Atacante prueba:

?id=10 OR 1=1

o:

?id=10 UNION SELECT username,password FROM users

Cómo descubren vulnerabilidades los atacantes

Muchos usan herramientas automáticas.



sqlmap

Una de las más famosas.

Detecta y explota SQL Injection automáticamente.

Ejemplo:

sqlmap -u "https://sitio.com/product.php?id=1"

Puede:

  • enumerar bases
  • extraer tablas
  • sacar usuarios
  • romper autenticación

Por qué SQLi sigue existiendo


Porque muchos sistemas:

❌ usan código viejo

❌ concatenan strings

❌ no validan inputs

❌ no usan prepared statements


Cómo prevenir SQL Injection correctamente


La solución moderna principal:

Prepared Statements


Ejemplo seguro con PDO

$stmt = $pdo->prepare("
    SELECT * FROM users
    WHERE username = :user
    AND password = :pass
");

$stmt->execute([
    ':user' => $user,
    ':pass' => $pass
]);

¿Por qué esto es seguro?


Porque:

✅ SQL y datos van separados

✅ el motor SQL interpreta valores como datos

✅ no como código


Prepared Statements vs concatenación


Malo

$sql = "SELECT * FROM users WHERE id = $id";

Bueno

$stmt = $pdo->prepare("
    SELECT * FROM users WHERE id = ?
");

Validar inputs también importa


Prepared statements ayudan muchísimo.

Pero además debes validar:

  • tipos
  • tamaños
  • formatos
  • rangos

Ejemplo correcto

$id = (int) $_GET['id'];

Nunca confiar en input del usuario


Nunca.

Ni formularios.

Ni APIs.

Ni cookies.

Ni headers.

Ni uploads.


ORM y frameworks modernos


Muchos frameworks ayudan automáticamente:

  • Laravel
  • Symfony
  • Django
  • Ruby on Rails


Pero incluso con frameworks:

❌ puedes programar inseguro



Errores comunes


1. Escapar strings manualmente


Muchos hacen:

mysqli_real_escape_string()

Eso NO reemplaza prepared statements.



2. Mostrar errores SQL


Muy peligroso.

En producción:

display_errors = Off

3. Usar usuario root en MySQL

Nunca hagas esto.

Crea usuarios limitados.



4. No limitar permisos SQL

Tu aplicación NO necesita permisos globales.



Qué permisos mínimos usar


Ejemplo:

GRANT SELECT, INSERT, UPDATE
ON tienda.*
TO 'appuser'@'localhost';

Cómo detectar intentos SQLi

Revisa logs buscando:

UNION SELECT
OR 1=1
SLEEP(
--
'
"

Herramientas que ayudan a bloquear SQLi


WAF

Muchos WAF detectan patrones SQLi automáticamente.

Ejemplos:

  • Cloudflare
  • ModSecurity

Fail2Ban

Puede bloquear IPs que generen patrones sospechosos.



OWASP CRS

Las reglas OWASP para ModSecurity detectan SQLi comunes.



Cómo probar tu propia aplicación

Puedes hacer pruebas controladas.

Ejemplo:

?id='

Si aparecen errores SQL:

🚨 mala señal.


Qué hacer si descubres una vulnerabilidad


Inmediatamente

✅ corregir código

✅ cambiar credenciales

✅ revisar logs

✅ revisar accesos

✅ actualizar dependencias


SQL Injection en APIs

También ocurre muchísimo en:

  • APIs REST
  • filtros dinámicos
  • búsquedas
  • GraphQL mal implementado

SQLi en WordPress

Muchos plugins vulnerables han permitido:

  • robo de usuarios
  • takeover admin
  • malware

Por eso actualizar plugins es crítico.



Cómo reducir superficie de ataque

✅ Prepared statements

✅ validación inputs

✅ WAF

✅ logs

✅ mínimos privilegios

✅ actualizaciones

✅ ocultar errores SQL


Ejemplo completo seguro con PDO

<?php

$stmt = $pdo->prepare("
    SELECT id, name, email
    FROM users
    WHERE email = :email
");

$stmt->execute([
    ':email' => $email
]);

$user = $stmt->fetch();





Conclusión:


SQL Injection es uno de los ataques más famosos de internet porque durante años ha sido extremadamente efectivo.

La buena noticia es que hoy prevenirlo correctamente es mucho más sencillo que antes.


Si haces esto:

✅ prepared statements

✅ validación de inputs

✅ mínimos privilegios SQL

✅ WAF

✅ logs

✅ frameworks modernos

ya estarás muy por delante de muchísimas aplicaciones vulnerables.

La seguridad web no consiste en confiar en que nadie atacará tu sitio.

Consiste en asumir que tarde o temprano alguien lo intentará.

Y prepararte antes de que ocurra.


🔙 Volver al post pilar:


👉 Cómo montar una infraestructura web real desde cero