¿Qué es SQL Injection y como prevenir este tipo de ataque?

Qué es SQL Injection

SQL Injection 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 permite a un atacante ver datos que normalmente no puede recuperar. Esto puede incluir datos pertenecientes a otros usuarios, o cualquier otro dato al que la propia aplicación pueda acceder. En muchos casos, un atacante puede modificar o eliminar estos datos, causando cambios persistentes en el contenido o comportamiento de la aplicación.

En algunas situaciones, un atacante puede escalar un ataque de inyección de SQL para comprometer el servidor subyacente u otra infraestructura back-end, o realizar un ataque de denegación de servicio.

Recuperación de datos ocultos

Considera una aplicación de compras que muestre productos en diferentes categorías. Cuando el usuario hace clic en la categoría Regalos (Gifts), su navegador solicita la URL:

https://insecure-website.com/products?category=Gifts

Esto hace que la aplicación realice una consulta SQL para recuperar los detalles de los productos relevantes de la base de datos:

SELECT * FROM products WHERE category = 'Gifts' AND released = 1

Esta consulta SQL le pide a la base de datos que regrese:

  • todos los datos (*)
  • de la tabla de products
  • donde la categoría es Gifts
  • y el campo released es 1.

La restricción released = 1 se utiliza para ocultar productos que no están liberados o activos. Para productos no activos, presumiblemente el valor del campop released sería igual a 0.

La aplicación no implementa ninguna defensa contra ataques de inyección SQL, por lo que un atacante puede construir un ataque como:

https://insecure-website.com/products?category=Gifts'--

Esto resulta en una consulta SQL como la siguiente:

SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1

La clave aquí es que la secuencia de doble guión — es un indicador de comentarios en SQL, y significa que el resto de la consulta se interpreta como un comentario. Esto elimina efectivamente el resto de la consulta, por lo que ya no incluye AND released = 1. Esto significa que se muestran todos los productos, incluidos los productos no lanzados.

Yendo más allá, un atacante puede hacer que la aplicación muestre todos los productos en cualquier categoría, incluyendo las categorías que desconocen:

https://insecure-website.com/products?category=Gifts'+OR+1=1--

Esto resulta en una consulta SQL como la siguiente:

SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1

La consulta modificada devolverá todos los elementos en los que la categoría es Gifts, o 1 es igual a 1. Dado que 1=1 es siempre verdadero, la consulta devolverá todos los elementos.

Alterar la lógica de la aplicación

Considere una aplicación que permita a los usuarios iniciar sesión con un nombre de usuario y una contraseña. Si un usuario envía el nombre de usuario wiener y la contraseña bluecheese, la aplicación comprueba las credenciales realizando la siguiente consulta SQL:

SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'

Si la consulta devuelve los datos de un usuario, el inicio de sesión se realiza correctamente. De lo contrario, se rechaza.

Aquí, un atacante puede iniciar sesión como cualquier usuario sin contraseña simplemente usando la secuencia de comentarios SQL para eliminar la comprobación de contraseña de la cláusula WHERE de la consulta. Por ejemplo, al enviar el nombre de usuario administrator’– y una contraseña en blanco, se obtiene la siguiente consulta:

SELECT * FROM users WHERE username = ‘administrator’– AND password = »

Esta consulta devuelve al usuario cuyo nombre de usuario es administrator e inicia sesión con éxito como ese usuario.

Recuperación de datos de otras tablas de base de datos

En los casos en los que los resultados de una consulta SQL se devuelven dentro de las respuestas de la aplicación, un atacante puede aprovechar una vulnerabilidad de inyección SQL para recuperar datos de otras tablas dentro de la base de datos. Esto se hace utilizando la palabra clave UNION, que le permite ejecutar una consulta SELECT adicional y añadir los resultados a la consulta original.

Por ejemplo, si una aplicación ejecuta la siguiente consulta que contiene la entrada de usuario «Gifts»:

SELECT name, description FROM products WHERE category = 'Gifts'

Entonces un atacante puede enviar la siguiente entrada:

' UNION SELECT username, password FROM users--

Esto hará que la aplicación devuelva todos los nombres de usuario y contraseñas junto con los nombres y descripciones de los productos.

Examinando la base de datos

Tras la identificación inicial de una vulnerabilidad de inyección SQL, generalmente es útil obtener información sobre la propia base de datos. Esta información a menudo puede allanar el camino para una mayor explotación.

Puede consultar los detalles de la versión de la base de datos. La forma en que esto se hace depende del tipo de base de datos, por lo que puede inferir el tipo de base de datos a partir de cualquier técnica que funcione. Por ejemplo, en Oracle se puede ejecutar:

SELECT * FROM v$version
También puede determinar qué tablas de base de datos existen y qué columnas contienen. Por ejemplo, en la mayoría de las bases de datos se puede ejecutar la siguiente consulta para listar las tablas:
SELECT * FROM information_schema.tables

Vulnerabilidades de Blind SQL injection

Muchos casos de inyección SQL son vulnerabilidades ciegas. Esto significa que la aplicación no devuelve los resultados de la consulta SQL ni los detalles de los errores de la base de datos en sus respuestas. Las vulnerabilidades ciegas todavía pueden ser explotadas para acceder a datos no autorizados, pero las técnicas involucradas son generalmente más complicadas y difíciles de realizar.

Dependiendo de la naturaleza de la vulnerabilidad y de la base de datos implicada, se pueden utilizar las siguientes técnicas para explotar las vulnerabilidades de inyección SQL ciegas:

  • Puede cambiar la lógica de la consulta para activar una diferencia detectable en la respuesta de la aplicación en función de la verdad de una única condición. Esto puede implicar inyectar una nueva condición en alguna lógica booleana o desencadenar condicionalmente un error como un dividir por cero.
  • Puede desencadenar condicionalmente un retraso de tiempo en el procesamiento de la consulta, lo que le permite inferir la verdad de la condición en función del tiempo que tarda la aplicación en responder.
  • Puede desencadenar una interacción de red fuera de banda, utilizando las técnicas OAST. Esta técnica es extremadamente poderosa y funciona en situaciones en las que las otras técnicas no lo hacen. A menudo, puede extraer datos directamente a través del canal out-of-band, por ejemplo, colocando los datos en una búsqueda DNS para un dominio que usted controla.

Detectando vulnerabilidades de SQL Injections

La mayoría de las vulnerabilidades de inyección SQL se pueden encontrar de forma rápida y fiable utilizando el escáner de vulnerabilidades web de Burp Suite.

La inyección SQL se puede detectar manualmente utilizando un conjunto sistemático de pruebas contra cada punto de entrada de la aplicación. Esto típicamente involucra:

  • Enviando el carácter de comillas ‘ y buscando errores u otras anomalías.
  • Envío de una sintaxis específica de SQL que evalúa el valor base (original) del punto de entrada, y un valor diferente, y buscando diferencias sistemáticas en las respuestas de la aplicación resultante.
  • Enviar condiciones booleanas como OR 1=1 y OR 1=2, y buscar diferencias en las respuestas de la aplicación.
  • Envío de cargas útiles diseñadas para desencadenar retrasos de tiempo cuando se ejecutan dentro de una consulta SQL, y buscando diferencias en el tiempo que se tarda en responder.
  • Envío de cargas útiles OAST diseñadas para activar una interacción de red fuera de banda cuando se ejecuta dentro de una consulta SQL, y monitorización de cualquier interacción resultante.

Inyección de SQL en diferentes partes de la consulta

La mayoría de las vulnerabilidades de inyección SQL surgen dentro de la cláusula WHERE de una consulta SELECT. Este tipo de inyección SQL es generalmente bien comprendido por los probadores experimentados.

Pero las vulnerabilidades de inyección SQL pueden ocurrir en principio en cualquier ubicación dentro de la consulta, y dentro de diferentes tipos de consulta. Las otras ubicaciones más comunes donde surge la inyección SQL son:

  • En las instrucciones UPDATE, dentro de los valores actualizados o de la cláusula WHERE.
  • En las sentencias INSERT, dentro de los valores insertados.
  • En las instrucciones SELECT, dentro del nombre de la tabla o columna.
  • En las instrucciones SELECT, dentro de la cláusula ORDER BY.

Inyección SQL de segundo orden

La inyección SQL de primer orden se produce cuando la aplicación toma la entrada del usuario de una petición HTTP y, en el curso del procesamiento de esa petición, incorpora la entrada en una consulta SQL de forma insegura.

En la inyección SQL de segundo orden (también conocida como inyección SQL almacenada), la aplicación toma la entrada del usuario de una solicitud HTTP y la almacena para su uso futuro. Esto se hace generalmente colocando la entrada en una base de datos, pero no surge ninguna vulnerabilidad en el punto donde se almacenan los datos. Más tarde, cuando se trata de una petición HTTP diferente, la aplicación recupera los datos almacenados y los incorpora a una consulta SQL de forma insegura.

La inyección SQL de segundo orden a menudo surge en situaciones en las que los desarrolladores son conscientes de las vulnerabilidades de la inyección SQL y, por lo tanto, manejan con seguridad la colocación inicial de la entrada en la base de datos. Cuando los datos se procesan posteriormente, se considera que son seguros, ya que anteriormente se colocaban en la base de datos de forma segura. En este punto, los datos se manejan de forma insegura, porque el desarrollador considera erróneamente que son de confianza.

Factores específicos de la base de datos

Algunas de las características principales del lenguaje SQL se implementan de la misma manera en las plataformas de bases de datos más populares, por lo que muchas formas de detectar y explotar las vulnerabilidades de la inyección SQL funcionan de forma idéntica en diferentes tipos de bases de datos.

Sin embargo, también hay muchas diferencias entre las bases de datos comunes. Esto significa que algunas técnicas para detectar y explotar la inyección SQL funcionan de forma diferente en diferentes plataformas. Por ejemplo:

  • Syntax for string concatenation.
  • Comments.
  • Batched (or stacked) queries.
  • Platform-specific APIs.
  • Error messages.

Prevención de SQL Injection

La mayoría de las instancias de inyección SQL se pueden evitar utilizando consultas parametrizadas (también conocidas como sentencias preparadas) en lugar de la concatenación de cadenas dentro de la consulta.

El siguiente código es vulnerable a la inyección SQL porque la entrada del usuario se concatena directamente en la consulta:

String query = "SELECT * FROM products WHERE category = '"+ input + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);

Este código puede ser reescrito fácilmente de manera que se evite que la entrada del usuario interfiera con la estructura de la consulta:

PreparedStatement statement = connection.prepareStatement("SELECT * FROM products WHERE 
category = ?");
statement.setString(1, input);
ResultSet resultSet = statement.executeQuery();

Las consultas parametrizadas se pueden utilizar en cualquier situación en la que la entrada no fiable aparezca como datos dentro de la consulta, incluyendo la cláusula WHERE y los valores de una instrucción INSERT o UPDATE. No se pueden utilizar para gestionar entradas no fiables en otras partes de la consulta, como los nombres de tablas o columnas, o la cláusula ORDER BY. La funcionalidad de la aplicación que coloca datos no confiables en esas partes de la consulta necesitará adoptar un enfoque diferente, como los valores de entrada permitidos en las listas blancas, o utilizar una lógica diferente para proporcionar el comportamiento requerido.

Para que una consulta parametrizada sea eficaz para evitar la inyección SQL, la cadena que se utiliza en la consulta debe ser siempre una constante de código duro y nunca debe contener datos variables de ningún origen. No se sienta tentado a decidir caso por caso si se confía en un elemento de datos, y continúe utilizando la concatenación de cadenas dentro de la consulta para los casos que se consideran seguros. Es demasiado fácil cometer errores sobre el posible origen de los datos, o que los cambios en otro código violen las suposiciones sobre qué datos están contaminados.

Comentarios