Inyección SQL

La inyección SQL (SQL Injection) es una técnica común de ataque en redes. Los atacantes inyectan sentencias SQL maliciosas en campos de entrada o solicitudes para manipular la base de datos y ejecutar operaciones no previstas.

Sus objetivos suelen ser:

  • Robar datos confidenciales
  • Evadir la autenticación
  • Modificar o eliminar contenido de la base de datos
  • Ejecutar comandos del sistema, etc.

Cómo funciona la inyección SQL

  • Validación de entradas insuficiente: Cuando la aplicación web no valida correctamente la entrada del usuario, el atacante puede insertar código SQL en los campos de entrada.

  • Concatenación de sentencias SQL: El backend de la aplicación generalmente concatena la entrada del usuario con la consulta SQL para formar la sentencia completa de consulta a la base de datos.

  • Ejecución de SQL malicioso: Si la aplicación no sanea ni escapa adecuadamente la entrada, el código SQL malicioso será ejecutado por el servidor de base de datos.

  • Fuga o destrucción de datos: El atacante puede usar la inyección SQL para consultar, modificar o eliminar datos en la base de datos, o ejecutar comandos del sistema de gestión de base de datos.

Consulta normal

Cuando un usuario inicia sesión en un sitio web, normalmente ingresa su nombre de usuario y contraseña.

A continuación se muestra un código de consulta SQL normal:

SELECT * FROM users WHERE username = 'user1' AND password = 'password1';

Ataque de inyección

Si el atacante ingresa:

用户名: admin' --
密码: anything

La consulta SQL se convierte en:

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

donde--es el símbolo de comentario en SQL, se ignora la condición de la contraseña y se evita directamente la autenticación.


Tipos comunes de inyección SQL

1. Inyección SQL básica

Se incrusta código SQL malicioso directamente en la entrada del usuario, afectando la lógica de la consulta.

输入用户名:admin' OR '1'='1
输入密码:anything

Consulta SQL ejecutada:

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

Resultado:

OR '1'='1' siempre es verdadero, por lo que puede evadir la verificación.

2. Inyección mediante consulta UNION

Mediante UNION, se combinan los resultados de la consulta construida por el atacante con los de una consulta legítima, obteniendo así datos confidenciales.

Entrada:

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

Consulta SQL ejecutada:

SELECT id, name FROM products WHERE id = '' UNION SELECT null, username, password FROM users --';

Resultado:

Los datos de username y password de la tabla users se devuelven como resultado.

3. Inyección SQL basada en errores

Al provocar intencionalmente errores de la base de datos, se utiliza la información de error para deducir nombres de tablas, columnas o datos.

Entrada:

' AND 1=CONVERT(int, (SELECT @@version)) --

Consulta SQL ejecutada:

SELECT * FROM users WHERE username = '' AND 1=CONVERT(int, (SELECT @@version)) --';

Resultado:

El mensaje de error puede exponer la versión de la base de datos u otra información.

4. Inyección ciega

Cuando no se puede obtener directamente el resultado de la consulta, el atacante deduce los datos paso a paso observando la respuesta de la página (como valores booleanos o retrasos de tiempo).

Inyección ciega booleana, entrada:

' AND (SELECT 1 WHERE SUBSTRING((SELECT database()), 1, 1)='t') --

Consulta SQL ejecutada:

SELECT * FROM users WHERE username = '' AND (SELECT 1 WHERE SUBSTRING((SELECT database()), 1, 1)='t') --';

Resultado:

Según el resultado devuelto, se determina si la primera letra del nombre de la base de datos es 't'.

Inyección ciega basada en tiempo, entrada:

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

Consulta SQL ejecutada:

SELECT * FROM users WHERE username = '' AND IF(1=1, SLEEP(5), 0) --';

Resultado:

Si la condición se cumple, el servidor retrasa la respuesta 5 segundos, lo que filtra información.

5. Inyección de consultas apiladas

Permite ejecutar múltiples sentencias SQL a la vez.

Entrada:

'; DROP TABLE users; --

Consulta SQL ejecutada:

SELECT * FROM users WHERE username = ''; DROP TABLE users; --';

Resultado:

La tabla users es eliminada.

Algunas bases de datos (como MySQL) no admiten la ejecución de múltiples sentencias por defecto.

6. Inyección en procedimientos almacenados

Se inyecta SQL malicioso a través de los parámetros de entrada de los procedimientos almacenados.

Entrada:

'; EXEC xp_cmdshell('dir'); --

SQL ejecutado:

EXEC LoginProcedure 'username', ''; EXEC xp_cmdshell('dir'); --'

Resultado:

Ejecuta comandos del sistema (como listar directorios).

7. Inyección mediante cookies

Se realiza la inyección modificando los valores de cookies almacenadas en el navegador.

Cookie: session_id=' OR '1'='1;

El servidor ejecuta SQL malicioso al analizar la cookie.


Peligros de la inyección SQL

  • Fuga de datos:El atacante obtiene información confidencial como nombres de usuario, contraseñas, números de tarjetas bancarias, etc. de la base de datos.
  • Escalada de privilegios:El atacante puede obtener mayores privilegios de acceso mediante comandos inyectados.
  • Alteración de datos:El contenido de la base de datos se modifica o elimina.
  • Interrupción del servicio:El código SQL malicioso puede provocar que la base de datos se bloquee, afectando la disponibilidad del sistema.
  • Ejecución de comandos del sistema:A través de las funciones de extensión de la base de datos, el atacante podría manipular directamente el sistema operativo.

Medidas de prevención

1. Consultas parametrizadas y sentencias preparadas

Use consultas parametrizadas o sentencias preparadas para separar la entrada del usuario de las sentencias SQL, evitando que la entrada se interprete directamente como código SQL.

Código Java:

Ejemplo

String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();

Node.js (módulo MySQL):

Ejemplo

const query = "SELECT * FROM users WHERE username = ? AND password = ?";
connection.query(query, [username, password], (err, results) => {
    if (err) throw err;
    // Handle results
});

2. Uso de frameworks ORM

Idea principal: los ORM (como Hibernate, Sequelize, etc.) generan consultas SQL automáticamente, lo que reduce en gran medida las oportunidades de concatenar SQL manualmente y evita la inyección.

// 使用 Sequelize
const user = await User.findOne({
    where: { username: 'admin', password: 'password123' }
});

3. Validación de entradas

Verifique estrictamente que la entrada del usuario cumpla con lo esperado y rechace las entradas que no cumplan las reglas.

Use expresiones regulares para nombres de usuario, correos electrónicos, etc.; los campos numéricos solo deben permitir dígitos.

Escapar caracteres especiales (por ejemplo, convertir " a \").

const username = req.body.username.replace(/[^a-zA-Z0-9]/g, ''); // 清理特殊字符

4. Limitación de privilegios de la base de datos

Asigne a los usuarios de la base de datos solo los privilegios mínimos necesarios para realizar sus operaciones.

  • Restringir los permisos de escritura:Permitir a los usuarios solo operaciones de inserción y actualización en las tablas correspondientes; no permitir operaciones de alto riesgo como DROP, ALTER, etc.
  • Separar permisos de lectura y escritura:Usar cuentas de solo lectura para acceder a la base de datos.

Crear un usuario de solo lectura:

CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'secure_password';
GRANT SELECT ON database_name.* TO 'readonly_user'@'%';

5. Pruebas de seguridad periódicas

Mediante herramientas de escaneo de seguridad o pruebas manuales, revise periódicamente posibles vulnerabilidades de inyección SQL en el código.

Podemos usar la herramienta de código abierto SQLMap para las pruebas.

SQLMap es una herramienta de pruebas de penetración especializada en la detección y explotación automatizada de inyecciones SQL.

SQLMap se usa ampliamente en evaluaciones de seguridad de redes y pruebas de penetración, ayudando a detectar y corregir vulnerabilidades de inyección SQL.

Otras extensiones