Objetivos
- Conocer el concepto.
- Explicar los Tipos de Inyección:
- Sentencia de Modificación.
- Sentencia de Inyección.
- Conversión de tipo de datos.
- Ejecutar ejemplos prácticos.
- Técnicas contra la Inyección SQL.
El Concepto de Inyección SQL
La Inyección SQL es una vulnerabilidad crítica que permite a un atacante manipular consultas SQL mediante datos maliciosos enviados desde el cliente. Esta técnica puede otorgar acceso no autorizado a la base de datos, exponiendo o alterando información sensible.
En esencia, la inyección SQL aprovecha fallos en la validación de entradas de una aplicación. Al no filtrar correctamente los datos proporcionados por el usuario, el sistema ejecuta instrucciones no previstas, lo que abre la puerta a operaciones ilegítimas como lectura, modificación o eliminación de registros restringidos.
El origen de la vulnerabilidad radica en el manejo inadecuado de las variables utilizadas en un programa que construye o ejecuta sentencias SQL. Cuando no se realiza un correcto chequeo o filtrado de las entradas, se abre la posibilidad de que un atacante inserte código malicioso en la consulta.
Este problema forma parte de una clase más amplia de vulnerabilidades de inyección, que pueden presentarse en cualquier lenguaje de programación o script embebido dentro de otro. En todos los casos, el patrón común es la falta de validación robusta de los datos externos antes de ser procesados por el sistema.
Técnicas de Inyección
Las técnicas de inyección SQL pueden variar en su implementación, pero todas se apoyan en una misma debilidad: la falta de validación adecuada de las cadenas de entrada. Cuando los datos proporcionados por el usuario se concatenan directamente en una sentencia SQL dinámica sin filtros ni controles, el sistema queda expuesto a la ejecución de instrucciones maliciosas.
En otras palabras, el problema no está en la diversidad de métodos, sino en la vulnerabilidad común: el uso inseguro de entradas externas dentro de consultas SQL.
La Sentencia de Modificación
Consiste en alterar deliberadamente una consulta SQL dinámica para que se ejecute de una forma distinta a la prevista por el desarrollador. El objetivo suele ser acceder a información no autorizada o manipular el comportamiento de la aplicación.
Un atacante puede lograrlo modificando la cláusula WHERE de una sentencia SELECT o insertando una cláusula UNION ALL que combine resultados adicionales. El ejemplo clásico es la omisión del proceso de autenticación: se introduce una condición que siempre evalúe como verdadera (WHERE 1=1), permitiendo el acceso sin necesidad de contraseña.
En resumen, esta técnica explota la construcción insegura de consultas dinámicas, donde las entradas del usuario se concatenan sin validación, abriendo la puerta a operaciones no previstas.
Ejemplos
El script a continuación crea dos tablas (TABLA_PRUEBA1 y TABLA_PRUEBA2) y les inserta dos registros a cada usando SQL dinámico.
DECLARE
v_table_name VARCHAR2(30) := 'TABLA_PRUEBA';
BEGIN
FOR i IN 1..2 LOOP
BEGIN
EXECUTE IMMEDIATE
'CREATE TABLE '||v_table_name||i||'
(
id NUMBER(4),
name VARCHAR2(30),
date_ DATE
)
';
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
END LOOP;
FOR i IN 1..2 LOOP
FOR j IN 1..2 LOOP
BEGIN
EXECUTE IMMEDIATE
q'{INSERT INTO }'||v_table_name||i||q'{ (id, name, date_)
VALUES(}'||j||q'{,'name',SYSDATE+}'||j||q'{) }';
END;
END LOOP;
END LOOP;
END;
Consulta las tablas creadas:
SELECT 'TABLA_PRUEBA1' tabla, t1.*
FROM TABLA_PRUEBA1 t1
UNION
SELECT 'TABLA_PRUEBA2', t2.*
FROM TABLA_PRUEBA2 t2;

Ahora creamos el procedimiento: proc_get_info el cual está adecuado para extraer datos de una de las tablas antes creadas (TABLA_PRUEBA1 y TABLA_PRUEBA2). El procedimiento espera el nombre de una de estas tablas y el código (ID) por el cual se hará el filtro. A continuación mostramos porque dicho SCRIPT dinámico es vulnerable a la Inyección SQL.
CREATE OR REPLACE PROCEDURE proc_get_info(p_table VARCHAR2
, p_id VARCHAR2) IS
TYPE typ_tab IS
TABLE OF TABLA_PRUEBA1%ROWTYPE
INDEX BY BINARY_INTEGER;
v_tab_rec typ_tab;
v_query VARCHAR2(500);
BEGIN
v_query := q'{
SELECT
id,
name,
date_
FROM }'||p_table||q'{
WHERE id = }'||p_id||q'{
}';
EXECUTE IMMEDIATE v_query
BULK COLLECT INTO v_tab_rec;
FOR i IN NVL(v_tab_rec.FIRST,1)..NVL(v_tab_rec.LAST,0) LOOP
DBMS_OUTPUT.PUT_LINE('Id: '||v_tab_rec(i).id||
', Name: '||v_tab_rec(i).name||
', Date: '||v_tab_rec(i).date_);
END LOOP;
END;
A continuación invocamos el procedimiento proc_get_info pasandole parámetros validos para así ver el funcionamiento esperado.
SET SERVEROUTPUT ON
BEGIN
proc_get_info('TABLA_PRUEBA1',1);
END;

El siguiente ejemplo muestra como vulnerar el proceso proc_get_info para que en lugar de mostrar el registro con el código enviado por parámetro muerte todos los registros de la tabla. Esto evidencia cómo el uso de SQL dinámico inseguro permite que un atacante manipule la consulta y obtenga acceso a datos no autorizados.
SET SERVEROUTPUT ON
BEGIN
proc_get_info('TABLA_PRUEBA1','1 or 1=1');
END;

El siguiente ejemplo se le saca major provecho a la vulnerabilidad, aparte de mostrar todos los registros de la tabla especificada, también se muestran los de la otra tabla.
SET SERVEROUTPUT ON
BEGIN
proc_get_info('TABLA_PRUEBA1','1 or 1=1
UNION ALL
SELECT * FROM TABLA_PRUEBA2');
END;

La Sentencia de Inyección
Se refiere a una técnica en la que un usuario malicioso agrega una o más instrucciones SQL dentro de una consulta dinámica ya existente.
- El atacante aprovecha que el código concatena directamente las entradas del usuario en la sentencia SQL.
- Al hacerlo, puede insertar comandos adicionales como
UNION,DROP,UPDATEo incluso otroSELECT, alterando el comportamiento original. - Los bloques PL/SQL anónimos son especialmente vulnerables porque suelen ejecutarse sin controles estrictos de validación, y cualquier parámetro malicioso puede modificar la lógica de la consulta.
Esta técnica convierte una consulta legítima en un vehículo para ejecutar código malicioso, lo que permite acceder, modificar o eliminar datos de manera no autorizada.
Ejemplos
Para mostrar el otro tipo de Inyección SQL modificamos el procedimiento proc_get_info. Ahora en lugar de invocar una consulta dinámica, invoca todo un bloque PL/SQL. En el mismo bloque anónimo se imprime el resultado de la consulta. Nota: Ahora este bloque es más vulnerable aun.
CREATE OR REPLACE PROCEDURE proc_get_info(p_table VARCHAR2, p_id VARCHAR2) IS
v_statement VARCHAR2(2000);
BEGIN
v_statement := q'{
DECLARE
TYPE typ_tab IS
TABLE OF TABLA_PRUEBA1%ROWTYPE
INDEX BY BINARY_INTEGER;
v_tab_rec typ_tab;
BEGIN
SELECT
id,
name,
date_ BULK COLLECT INTO v_tab_rec
FROM }'||p_table||q'{
WHERE id = }'||p_id||q'{;
FOR i IN NVL(v_tab_rec.FIRST,1)..NVL(v_tab_rec.LAST,0) LOOP
DBMS_OUTPUT.PUT_LINE('Id: '||v_tab_rec(i).id||', Name: '||v_tab_rec(i).name||', Date: '||v_tab_rec(i).date_);
END LOOP;
END;
}';
EXECUTE IMMEDIATE v_statement;
END;
El siguiente ejemplo muestra como es posible anexar otra sentencia dentro del bloque anónimo; Al ejecutar dicho SCRIPT se eliminarían todos los registros de TABLA_PRUEBA2.
SET SERVEROUTPUT ON
BEGIN
proc_get_info('TABLA_PRUEBA1'
,'1; DELETE FROM TABLA_PRUEBA2');
END;
En el siguiente ejemplo el daño es aun mayor. Al ejecutar el script la tabla TABLA_PRUEBA1 es eliminada (DROP).
SET SERVEROUTPUT ON
BEGIN
proc_get_info('TABLA_PRUEBA2'
,q'{1; EXECUTE IMMEDIATE 'DROP TABLE TABLA_PRUEBA1'}');
END;
La conversión de tipo de datos
Es una técnica de inyección SQL menos conocida que aprovecha parámetros de sesión NLS (National Language Support) en Oracle.
- Estos parámetros controlan aspectos como el formato de números, fechas y cadenas.
- Un atacante puede manipularlos para que el motor interprete los datos de manera distinta, logrando que una entrada aparentemente inocua se convierta en una sentencia SQL inyectada.
- Por ejemplo, alterando el formato de fecha o número, el valor enviado puede transformarse en código ejecutable dentro de la consulta dinámica.
Esta técnica se basa en explotar la configuración de sesión NLS para modificar cómo se interpretan los datos, convirtiendo conversiones de tipo en un canal de inyección SQL.
Un valor datetime o numérico concatenado en el texto de una sentencia SQL dinámica debe convertirse al tipo de dato VARCHAR2. La conversión puede ser implícita (cuando el valor es un operando del operador de concatenación) o explícita (cuando el valor es el argumento de la función TO_CHAR). Esta conversión de tipo de datos depende de la configuración de NLS de la sesión de base de datos que ejecuta la sentencia SQL dinámica. La conversión de valores de fecha y hora utiliza modelos de formato especificados en los parámetros NLS_DATE_FORMAT, NLS_TIMESTAMP_FORMAT o NLS_TIMESTAMP_TZ_FORMAT, dependiendo del tipo de datos de fecha y hora concreto. La conversión de valores numéricos aplica separadores decimales y de grupo especificados en el parámetro NLS_NUMERIC_CHARACTERS.
Ejemplos:
Supongamos que un procedimiento espera un número (p_id) y construye dinámicamente la sentencia:
SELECT *
FROM tabla
WHERE id = '||p_id;
Si el atacante modifica el parámetro de sesión NLS, por ejemplo:
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = ',.';
Entonces un valor como 1.1 podría interpretarse de forma distinta y permitir que se inserte código adicional.
Otro caso es cambiando el formato de fecha con:
ALTER SESSION SET NLS_DATE_FORMAT = '" ) OR 1=1 --';
Y luego pasando una fecha como parámetro, el motor interpreta la cadena como parte de la sentencia SQL, convirtiendo la conversión de tipo en un canal de inyección SQL.
En resumen: al manipular los parámetros NLS, un atacante puede transformar conversiones de datos en sentencias ejecutables, logrando acceso no autorizado.
Técnicas contra la Inyección SQL
Si utiliza SQL dinámico en PL/SQL, debe validar el texto de entrada para asegurar que el usuario no introduzca valores invalidados o maliciosos. Para evitar inyecciones SQL puede utilizar las siguientes técnicas:
- Utilizar argumentos/variables tipo Bind.
- Realizar comprobaciones que validen la integridad del dato recibido.
- Utilizar Modelos de formato explícito.
Uso de argumentos/variables tipo Bind
La manera más efectiva de hacer que su código PL/SQL sea invulnerable a ataques de inyección SQL es usar argumentos Bind. La base de datos utiliza exclusivamente los valores de los argumentos Bind y no interpreta su contenido. (Los argumentos Bind también mejoran el rendimiento).
Realizar Comprobaciones
Es recomendable que siempre realice comprobaciones de validez en los datos que recibe del usuario y así asegurarse de que son los valores esperados para procesar el requerimiento.
Ejemplos:
1. En una consulta que filtra por un campo tipo NUMBER no debe permitir que el usuario introduzca valores tipo carácter.
2. Si espera el nombre de una tabla de la cual desea eliminar registros debe comprobar que dicha tabla existe, para ello puede consultar el vista de diccionario ALL_TABLES.
En general, puede realizar cualquier tipo de comprobación que crea pertinente para así evitar la introducción de cláusulas o sentencias adicionales.
Uso de Modelos de formato explícito
Si utiliza valores de fecha y hora que están concatenados en el texto de una sentencia SQL o PL/SQL y no puede pasarlos como variables Bind, debe conviertalos en texto utilizando modelos de formato explícito que sean independientes de los valores de los parámetros NLS de la sesión en ejecución. Asegúrese de que los valores convertidos tengan el formato datetime SQLo de literales numéricos. El uso de modelos de formatos independientes de la configuración regional al construir SQL es recomendable no sólo desde una perspectiva de seguridad, sino también para garantizar que la sentencia SQL dinámica se ejecute correctamente en cualquier entorno.
Conclusión
La inyección SQL demuestra cómo una simple concatenación puede comprometer la seguridad completa de una base de datos. Comprender su funcionamiento es esencial para prevenir ataques y garantizar la integridad de la información. El uso de consultas parametrizadas, validación de entradas y bind variables no solo protege el código, sino que también refuerza las buenas prácticas en desarrollo PL/SQL.

