🎓 Centro de Aprendizaje SQLi

Aprende los conceptos fundamentales de la Inyección SQL y cómo funcionan los retos del laboratorio.

¿Qué es la Inyección SQL?

La Inyección SQL (SQLi) es una vulnerabilidad que permite a un atacante manipular consultas a la base de datos a través de entradas no validadas. Es una de las fallas más críticas y comunes en aplicaciones web.

🚨 Impacto Potencial

  • Acceso no autorizado a datos sensibles
  • Modificación o eliminación de información
  • Escalado de privilegios en el sistema
  • Ejecución de comandos en el servidor

📊 Estadísticas

  • Top 3 en OWASP Top 10 (2021)
  • Presente en ~65% de aplicaciones web
  • Costo promedio: $4.24M por brecha
  • Tiempo de detección: 287 días promedio

💡 ¿Por qué ocurre?

Ocurre cuando la aplicación inserta datos del usuario directamente en una consulta SQL sin validación ni uso de parámetros seguros. Es como permitir que un extraño escriba directamente en tu libro de cuentas.

🧩 Conceptos Clave

🔍 Consulta SQL

Instrucción enviada a la base de datos para obtener o modificar información.

⚙️ Parámetro

Valor que el usuario puede controlar (ej: usuario, búsqueda, etc).

💣 Payload

Entrada maliciosa diseñada para alterar la consulta original.

✅ Validación

Proceso de comprobar que la entrada es segura antes de usarla.

🔝 Escalado

Técnica para obtener más permisos de los que debería tener el usuario.

📤 Exfiltración

Extracción de datos sensibles de la base de datos.

Anatomía de una consulta vulnerable:

SELECT * FROM users WHERE username = '[INPUT_USUARIO]' AND password = '[INPUT_CONTRASEÑA]';

⚠️ Problema: Si el usuario puede controlar [INPUT_USUARIO] o [INPUT_CONTRASEÑA] sin validación, puede inyectar código SQL malicioso.

⚡ Tipos de Inyección SQL

🟢 Clásica / Booleana

Manipula la lógica de la consulta para obtener acceso sin conocer credenciales.

' OR 1=1 --

Permite el acceso sin conocer la contraseña.

🔵 UNION-Based

Une resultados de varias consultas para extraer datos de otras tablas.

' UNION SELECT username, password FROM users --

Permite ver información de otras tablas.

🟣 Basada en Tiempo (Blind)

Usa funciones de retraso para detectar vulnerabilidad cuando no hay mensajes de error.

' AND SLEEP(3) --

Útil cuando no hay mensajes de error ni datos visibles.

🔴 Basada en Errores

Provoca errores para obtener información de la estructura de la base de datos.

' AND (SELECT COUNT(*) FROM information_schema.tables) --

Los mensajes de error pueden revelar estructura interna.

🟡 Stacked Queries

Ejecuta múltiples consultas en una sola petición (si el SGBD lo permite).

'; DROP TABLE users; --

Permite ejecutar comandos destructivos o de configuración.

📝 Ejemplos Prácticos Paso a Paso

🔓 Ejemplo 1: Bypass de Login

Consulta original:

SELECT * FROM users WHERE username = 'admin' AND password = 'mipassword';

Input malicioso en usuario:

admin' --

Consulta resultante:

SELECT * FROM users WHERE username = 'admin' -- AND password = 'cualquiercosa';
✅ Resultado: El comentario -- ignora la validación de contraseña, permitiendo acceso como admin.

📊 Ejemplo 2: Extracción de Datos

Consulta original:

SELECT name, description FROM products WHERE name LIKE '%laptop%';

Input malicioso en búsqueda:

' UNION SELECT username, password FROM users --

Consulta resultante:

SELECT name, description FROM products WHERE name LIKE '%' 
UNION SELECT username, password FROM users --%';
✅ Resultado: Se muestran tanto productos como usuarios y contraseñas de la tabla users.

⏱️ Ejemplo 3: Detección Blind

Escenario:

La aplicación no muestra errores ni datos, pero puedes medir el tiempo de respuesta.

Input malicioso:

' AND SLEEP(5) --

Observación:

Si la página tarda 5 segundos extra en cargar, confirmas que hay vulnerabilidad SQLi.

✅ Técnica: Puedes usar esto para extraer datos bit a bit: IF(SUBSTRING(password,1,1)='a', SLEEP(5), 0)

🎯 Retos del Laboratorio

🚀 Tecnología del Laboratorio

Este laboratorio utiliza tecnología avanzada para simular un entorno de base de datos real directamente en tu navegador:

⚡ WebAssembly (WASM)

Ejecuta un motor SQLite completo en el navegador con rendimiento nativo. Esto permite:

  • • Consultas SQL reales sin servidor backend
  • • Respuesta instantánea a tus inyecciones
  • • Aislamiento completo y seguridad
  • • Experiencia idéntica a una DB real
🗄️ Base de Datos en el Navegador

SQLite se ejecuta completamente en memoria del navegador:

  • • Datos reales: usuarios, productos, pedidos
  • • Esquema completo con relaciones
  • • Reseteo automático entre retos
  • • Sin limitaciones de sandbox
💡 Ventaja: Puedes experimentar con payloads reales y ver el impacto inmediato en una base de datos funcional, sin riesgos de seguridad.

📋 Descripción Detallada de Cada Reto:

🔓 1. El Login Roto - Autenticación Bypass

Escenario: Sistema de login vulnerable que concatena directamente las credenciales del usuario.

Consulta vulnerable:

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

🎯 Objetivos de Aprendizaje:

  • • Bypass de autenticación con comentarios SQL
  • • Manipulación de lógica booleana
  • • Técnicas de inyección básica

🛠️ Técnicas a Practicar:

  • admin' -- (comentario)
  • ' OR 1=1 -- (tautología)
  • ' OR 'a'='a (comparación verdadera)

💡 Pista: El objetivo es hacer que la condición WHERE siempre sea verdadera o comentar la verificación de contraseña.

🔍 2. El Buscador Curioso - Union-Based SQLi

Escenario: Buscador de productos que permite extraer datos de otras tablas usando UNION SELECT.

Consulta vulnerable:

SELECT name, description FROM products WHERE name LIKE '%$search%'

🎯 Objetivos de Aprendizaje:

  • • Descubrimiento de estructura de tablas
  • • Técnicas UNION SELECT
  • • Extracción sistemática de datos
  • • Enumeración de esquema de DB

🛠️ Técnicas a Practicar:

  • UNION SELECT 1,2 (determinar columnas)
  • UNION SELECT username,password FROM users
  • UNION SELECT sql,type FROM sqlite_master

💡 Pista: Primero determina cuántas columnas devuelve la consulta original, luego usa UNION para combinar con datos de otras tablas.

⏱️ 3. El Cupón Paciente - Time-Based Blind SQLi

Escenario: Sistema de cupones que no muestra errores ni datos, pero permite detectar vulnerabilidades por tiempo de respuesta.

Consulta vulnerable:

SELECT * FROM coupons WHERE code = '$coupon' AND valid = 1

🎯 Objetivos de Aprendizaje:

  • • Detección de SQLi "a ciegas"
  • • Técnicas basadas en tiempo
  • • Extracción de datos bit a bit
  • • Paciencia en ataques manuales

🛠️ Técnicas a Practicar:

  • ' AND SLEEP(5) -- (retraso condicional)
  • ' AND IF(1=1,SLEEP(3),0) --
  • ' AND LENGTH(database())>5 AND SLEEP(2) --

💡 Pista: Si la página tarda más tiempo en responder tras tu input, has confirmado la vulnerabilidad. Usa esto para extraer información.

💥 4. ¡Provoca un Error! - Error-Based SQLi

Escenario: Aplicación que muestra errores de base de datos detallados que pueden revelar información del sistema.

Consulta vulnerable:

SELECT * FROM orders WHERE id = $id

🎯 Objetivos de Aprendizaje:

  • • Explotación de mensajes de error
  • • Técnicas de revelación de información
  • • Manipulación de tipos de datos
  • • Análisis de respuestas de error

🛠️ Técnicas a Practicar:

  • ' AND 1=CAST((SELECT COUNT(*) FROM users) AS INT) --
  • ' AND 1=username -- (error de tipo)
  • ' AND 1=(SELECT password FROM users LIMIT 1) --

💡 Pista: Provoca errores de tipo o de sintaxis que fuercen a la DB a mostrar datos en el mensaje de error.

📊 5. Manipula el Orden - ORDER BY Injection

Escenario: Listado de productos con ordenamiento dinámico vulnerable en la cláusula ORDER BY.

Consulta vulnerable:

SELECT name, price FROM products ORDER BY $sort_column

🎯 Objetivos de Aprendizaje:

  • • Inyección en cláusulas ORDER BY
  • • Limitaciones de prepared statements
  • • Técnicas avanzadas de inyección
  • • Explotación de parámetros dinámicos

🛠️ Técnicas a Practicar:

  • price; DROP TABLE products --
  • (SELECT CASE WHEN 1=1 THEN name ELSE price END)
  • 1 AND SLEEP(3)

💡 Pista: ORDER BY no puede usar parámetros preparados para nombres de columna, creando una superficie de ataque única.

🔐 6. Subconsulta Maliciosa - Subquery Injection

Escenario: Sistema de permisos con subconsultas que puede ser manipulado para acceder a datos restringidos.

Consulta vulnerable:

SELECT * FROM documents WHERE owner_id = (SELECT id FROM users WHERE username = '$user')

🎯 Objetivos de Aprendizaje:

  • • Manipulación de subconsultas
  • • Bypass de controles de acceso
  • • Técnicas de escalada de privilegios
  • • Explotación de lógica compleja

🛠️ Técnicas a Practicar:

  • admin' UNION SELECT 1 --
  • ' OR 1=1) OR ('a'='a
  • ') UNION SELECT id FROM users WHERE role='admin' --

💡 Pista: Manipula la subconsulta para que devuelva el ID de un usuario con más privilegios o múltiples IDs.

🎓 Consejos para Resolver los Retos

📝 Metodología:

  • 1. Lee las pistas y entiende el contexto
  • 2. Analiza la consulta SQL generada
  • 3. Identifica el punto de inyección
  • 4. Prueba payloads básicos primero
  • 5. Observa las respuestas y errores
  • 6. Refina tu técnica según el resultado

🔍 Panel de Análisis:

  • • Consulta SQL real ejecutada
  • • Resultados de la base de datos
  • • Tiempo de ejecución
  • • Mensajes de error (si los hay)
  • • Estado de éxito/fallo del reto

🛡️ Desarrollo Seguro - Prevención de SQLi

Implementa estas prácticas de desarrollo seguro para prevenir vulnerabilidades SQLi desde el diseño:

🛡️ Consultas Preparadas (Prepared Statements)

La defensa más efectiva contra SQLi. Separa la lógica SQL de los datos.

✅ PHP/PDO - SEGURO:

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);
$user = $stmt->fetch();

✅ Node.js/MySQL2 - SEGURO:

const query = 'SELECT * FROM users WHERE username = ? AND password = ?';
connection.execute(query, [username, password], (err, results) => {
    // Manejo seguro de resultados
});

✅ Python/SQLite3 - SEGURO:

cursor.execute("SELECT * FROM users WHERE username = ? AND password = ?", 
               (username, password))
user = cursor.fetchone()
✅ Ventaja: Los parámetros se tratan como datos literales, nunca como código SQL ejecutable.

❌ Código VULNERABLE vs ✅ SEGURO

Comparación directa de implementaciones inseguras y seguras.

❌ VULNERABLE - Concatenación:

// ¡NUNCA hagas esto!
$query = "SELECT * FROM users WHERE id = " . $_GET['id'];
$result = mysqli_query($conn, $query);

✅ SEGURO - Validación + Prepared:

// Validar entrada
if (!is_numeric($_GET['id'])) {
    die("ID inválido");
}
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);

❌ VULNERABLE - ORDER BY dinámico:

// Vulnerable a SQLi
$order = $_GET['sort'];
$query = "SELECT * FROM products ORDER BY $order";

✅ SEGURO - Lista blanca para ORDER BY:

// Lista blanca de columnas permitidas
$allowed_columns = ['name', 'price', 'category'];
$sort = in_array($_GET['sort'], $allowed_columns) ? $_GET['sort'] : 'name';
$query = "SELECT * FROM products ORDER BY $sort";

🔍 Validación y Sanitización

Implementa múltiples capas de validación para entrada del usuario.

Validación de Tipos:

// Validar ID numérico
function validateId($id) {
    if (!is_numeric($id) || $id <= 0) {
        throw new InvalidArgumentException("ID inválido");
    }
    return (int)$id;
}

// Validar email
function validateEmail($email) {
    $email = filter_var($email, FILTER_SANITIZE_EMAIL);
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        throw new InvalidArgumentException("Email inválido");
    }
    return $email;
}
💡 Regla: Valida en el cliente para UX, pero SIEMPRE valida en el servidor para seguridad.

⚙️ Configuración Segura de Base de Datos

Configura tu SGBD siguiendo el principio de menor privilegio.

Usuario de aplicación limitado:

-- Crear usuario específico para la aplicación
CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'strong_password';

-- Solo permisos necesarios
GRANT SELECT, INSERT, UPDATE ON myapp.users TO 'webapp'@'localhost';
GRANT SELECT ON myapp.products TO 'webapp'@'localhost';

-- NO otorgar permisos administrativos
-- REVOKE ALL ON *.* FROM 'webapp'@'localhost';
🔐 Principio: Si tu aplicación es comprometida, el atacante solo tendrá los permisos mínimos necesarios.

🛠️ Framework y ORM Seguros

🔷 Laravel (PHP)

// Eloquent ORM - Automáticamente seguro
User::where('email', $email)->first();

// Query Builder con parámetros
DB::table('users')
  ->where('active', 1)
  ->where('email', $email)
  ->get();

🟢 Express + Sequelize (Node.js)

// Sequelize ORM
const user = await User.findOne({
  where: {
    email: email,
    active: true
  }
});

// Raw query con parámetros
await sequelize.query(
  'SELECT * FROM users WHERE email = ?',
  { replacements: [email] }
);

🐍 Django (Python)

# Django ORM - Automáticamente seguro
user = User.objects.filter(
    email=email,
    is_active=True
).first()

# Raw query con parámetros
users = User.objects.raw(
    'SELECT * FROM users WHERE email = %s',
    [email]
)
📝 Checklist de Desarrollo Seguro:
  • ✅ Usar siempre consultas preparadas o ORM seguro
  • ✅ Validar y sanitizar TODA entrada del usuario
  • ✅ Implementar listas blancas para campos dinámicos (ORDER BY, columnas)
  • ✅ Configurar usuario de DB con permisos mínimos
  • ✅ No mostrar errores de DB al usuario final
  • ✅ Implementar logging y monitoreo de consultas sospechosas
  • ✅ Realizar pruebas de penetración regulares
  • ✅ Mantener frameworks y dependencias actualizadas

🛠️ Herramientas y Referencias

SQLMap

Automatiza la detección y explotación de SQLi. Muy útil para pruebas de caja negra.

Visitar SQLMap

Burp Suite

Proxy para interceptar y modificar peticiones HTTP. Permite manipular parámetros y automatizar ataques.

Descargar Burp Suite

CyberChef

Herramienta web para decodificar, codificar y manipular datos. Útil para analizar respuestas y payloads.

Usar CyberChef

DB Fiddle

Entorno online para probar consultas SQL en diferentes motores.

Probar DB Fiddle

SecLists

Listas de payloads y diccionarios para pruebas de seguridad, incluyendo SQLi.

Ver SecLists

PayloadAllTheThings

Repositorio con ejemplos de payloads para SQLi y otras vulnerabilidades.

Ver PayloadAllTheThings