Guía completa sobre vulnerabilidades de Inyección SQL (SQLi): Mecanismos y Prevención

Introducción a la Inyección SQL

La Inyeccción SQL (SQLi) es una vulnerabilidad de seguridad web que permite a un atacante interferir con las consultas que una aplicación realiza a su base de datos. Generalmente, ocurre cuando un software no filtra adecuadamente los datos de entrada del usuario, permitiendo que fragmentos de código SQL malicioso sean inteprretados y ejecutados por el motor de la base de datos.

A través de esta técnica, un atacante puede acceder a información sensible, como datos de clientes o secretos corporativos, modificar registros existentes o incluso obtener privilegios de administrador sobre el sistema de gestión de bases de datos (DBMS).

Mecánica de un ataque SQLi

El flujo típico de una vulnerabilidad SQLi se basa en los siguientes puntos críticos:

  • Entrada de datos no saneada: La aplicación recibe datos de fuentes externas (formularios, parámetros URL, cookies) sin la validación adecuada.
  • Construcción dinámica de consultas: El backend utiliza la concatenación de cadenas para insertar las variables del usuario directamente en la sentencia SQL.
  • Alteración de la lógica: El atacante introduce caracteres especiales (como comillas simples, guiones o puntos y coma) para cerrar la consulta original e inyectar comandos adicionales.

Considere el siguiente ejemplo de una implementación vulnerable en un entorno Node.js:


const express = require('express');
const pg = require('pg'); // Cliente de base de datos
const webApp = express();

webApp.get('/buscar-item', (request, response) => {
    const itemNombre = request.query.nombre;
    
    // ERROR: Concatenación directa de parámetros en la cadena SQL
    const instruccionSQL = "SELECT * FROM productos WHERE nombre = '" + itemNombre + "'";
    
    dbClient.query(instruccionSQL, (err, resultado) => {
        if (resultado.rows.length > 0) {
            response.json(resultado.rows);
        } else {
            response.status(404).send('Producto no hallado');
        }
    });
});

Si un usuario legítimo busca laptop, la consulta es segura. Sin embargo, si un atacante envía laptop' OR '1'='1, la consulta final se convierte en:

SELECT * FROM productos WHERE nombre = 'laptop' OR '1'='1'

Esto resultará en la devolución de todos los registros de la tabla, saltándose el filtro de búsqueda.

Tipologías de Inyección SQL

1. SQLi Basada en Uniones (UNION-based)

Utiliza el operador UNION para combinar los resultados de la consulta original con una consulta adicional inyectada. Es extremadamente efectiva para extraer datos de otras tablas.

' UNION SELECT null, esquema_db, nombre_usuario FROM informacion_usuarios --

2. SQLi Basada en Errores

El atacante fuerza a la base de datos a generar un error que contiene información técnica en su mensaje de respuesta. Esto revela detalles sobre la estructura de las tablas, versiones del motor o nombres de columnas.

' AND 1=CAST((SELECT version()) AS int) --

3. SQLi Ciega (Blind SQLi)

Ocurre cuando la aplicación no muestra el resultado de la consulta ni errores detallados. El atacante debe inferir información medinate respuestas binarias (Verdadero/Falso) o retrasos en el tiempo.

  • Booleana: El atacante observa si el contenido de la página cambia al enviar condiciones lógicas.
  • Basada en tiempo: Se utiliza una función que pausa la ejecución si una condición se cumple.
' AND (SELECT 1 FROM usuarios WHERE nombre='admin' AND SUBSTR(clave,1,1)='a') AND SLEEP(5) --

4. Consultas Apiladas (Stacked Queries)

Consiste en finalizar la consulta original mediante un punto y coma (;) para iniciar una instrucción completamente nueva, como borrar registros o actualizar permisos.

'; DROP TABLE registros_logs; --

Estrategias de Mitigación y Defensa

Consultas Parametrizadas y Sentencias Preparadas

Es la defensa más eficaz. En lugar de concatenar valores, se definen marcadores de posición que el motor de la base de datos trata estrictamente como datos, nunca como código ejecutable.

Ejemplo en Java (JDBC):


String sqlFiltro = "SELECT * FROM empleados WHERE departamento = ? AND activo = ?";
PreparedStatement pstmt = dbConexion.prepareStatement(sqlFiltro);
pstmt.setString(1, deptoInput);
pstmt.setBoolean(2, true);
ResultSet rs = pstmt.executeQuery();

Ejemplo en Node.js (mysql2):


const querySegura = "SELECT * FROM clientes WHERE correo = ?";
dbConnection.execute(querySegura, [usuarioEmail], (err, records) => {
    // Procesar resultados de forma segura
});

Implementación de ORM (Object-Relational Mapping)

El uso de herramientas como Hibernate, Sequelize o Entity Framework reduce el riesgo, ya que estos frameworks suelen abstraer la creación de SQL y utilizan parametrización por defecto.

Principio de Mínimo Privilegio

La cuenta de base de datos utilizada por la aplicación web debe tener solo los permisos estrictamente necesarios. Por ejemplo, una cuenta de lectura no debería tener permisos para ejecutar DROP o TRUNCATE, limitando el radio de explosión en caso de una intrusión exitosa.

Validación y Tipado Estricto de Entradas

Implementar listas blancas (allow-lists) para validar que los datos coincidan con el formato esperado (como números de identificación, códigos postales o correos electrónicos) antes de que lleguen a la capa de persistencia.

Etiquetas: sql-injection Web-Security database-security OWASP backend-development

Publicado el 7-20 22:28