Skip to main content

Cómo prevenir ataques de inyección XPath

Escrito por

Marcelo Oliveira

blog feature pypi spoof

10 de mayo de 2023

0 minutos de lectura

Las aplicaciones web son vulnerables a varios tipos de ataques, pero son especialmente susceptibles a los ataques de inyección de código. Uno de ellos, la inyección XPath, aprovecha los sitios web que requieren información proporcionada por el usuario para acceder a datos almacenados en formato XML. Todos los sitios que usan una base de datos en formato XML podrían ser vulnerables a este ataque.

XPath es una sintaxis de consulta que los sitios web pueden usar para buscar en sus almacenes de datos XML. Cuando se ejecutan correctamente, las consultas XPath son una forma legítima de localizar datos contenidos en elementos y atributos. Los sitios web que usan bases de datos con datos en formato XML integran la información ingresada por el usuario (por ejemplo, un nombre de usuario y una contraseña) en las consultas XPath para localizar los datos necesarios.

Sin embargo, al igual que con otros ataques de inyección (como el popular ataque de inyección SQL), un actor malicioso puede aprovechar este proceso mediante información malformada. Un atacante puede averiguar cómo están estructurados los datos XML y acceder a datos a los que normalmente no tendría acceso. Los atacantes que obtienen esta información pueden elevar sus privilegios de acceso (un ataque de «escalada de privilegios»), lo que podría comprometer la aplicación y los datos confidenciales, y usar la información o el acceso para atacar al resto de la organización.

Además de eliminar o corromper datos, la exfiltración de datos puede permitir que los hackers ejecuten comandos de la aplicación, accedan al servidor para lanzar más ataques y derriben a toda la organización con otros ataques. Las consecuencias de estos ataques pueden dañar o destruir la reputación de nuestras aplicaciones y la credibilidad de una empresa ante sus clientes.

Afortunadamente, podemos proteger nuestros sitios contra los ataques de inyección XPath. En este artículo, exploraremos las vulnerabilidades XML y aprenderemos a evitar que comprometan los datos de las aplicaciones.

Cómo identificar y corregir vulnerabilidades de inyección XPath

Para ver cómo prevenir las inyecciones XPath, tendremos que examinar una. Después, aprenderemos cuál es la mejor manera de proteger nuestras aplicaciones y datos contra este ataque.

Exploración de una aplicación de autenticación vulnerable

Veamos una aplicación que requiere que los usuarios inicien sesión con un nombre de usuario y una contraseña.

Crea una carpeta llamada xpath y, dentro de ella, crea un archivo llamado users_info.xml con los siguientes datos XML:

<users>

  <userinfo>
    <user_id>1</user_id>
    <username>jennifer</username>
    <password>4uyFh6v0</password>
    <account_no>757456</account_no>
  </userinfo>

  <userinfo>
    <user_id>2</user_id>
    <username>kimkimani</username>
    <password>6wreD1678</password>
    <account_no>870965</account_no>
  </userinfo>

  <userinfo>
    <user_id>3</user_id>
    <username>michael</username>
    <password>gf5dG63g</password>
    <account_no>345689</account_no>
  </userinfo>

  <userinfo>
    <user_id>4</user_id>
    <username>robert</username>
    <password>yH8wej3h</password>
    <account_no>096417</account_no>
  </userinfo>

</users>

Dentro de la carpeta 0xpath, crea un archivo nuevo llamado main.py y pega el siguiente código en el archivo:

# Import the required modules
from lxml import etree

# Parse the XML data
tree = etree.parse("users_info.xml")

def login(username, password):
	expression = "/users/userinfo[username='" + username + "' and password='" + password + "']"

	results = tree.xpath(expression)

	# if no results, which means login details did not match
	if len(results) < 1:
    		print("Incorrect credentials! Check your username or password.")

	# if login details match
	else:
    	for result in results:
        	print(f'Login successful! username: {result[1].text}, password: {result[2].text}') 
# Get the user-provided input
username = input("Enter your username: ")
password = input("Enter your password: ")

login(username, password)

El código carga los datos XML y luego ejecuta una consulta XPath para seleccionar los datos XML que coinciden con las credenciales proporcionadas por el usuario. Si encuentra una coincidencia, autentica al usuario. Si no la encuentra, la aplicación muestra el mensaje: «¡Credenciales no válidas! Verifica tu nombre de usuario o contraseña».

Antes de probar este código, instala el paquete lxml de Python para procesar datos XML. Ejecuta este comando para instalarlo:

pip install lxml

Para probar el código, llama a la función login y pasa tu nombre de usuario y contraseña. Puedes hacerlo agregando este fragmento de código al final de tu archivo main.py:

login(username="kimkimani", password="6wreD1678")

Para ejecutar el archivo, ejecuta el siguiente comando en la terminal.

python3 main.py

Se muestra un mensaje de éxito. También puedes intentar iniciar sesión con credenciales incorrectas, lo que genera un mensaje de error.

Este código es vulnerable a ataques de inyección XPath porque vincula la entrada del usuario con la consulta XPath, lo que modifica la estructura de la consulta e inyecta código malicioso.

Una consulta XPath típica es una cadena de texto que especifica los elementos que quieres seleccionar de los datos XML.

En el código anterior, la consulta "/users/userinfo[username='" + username + "' and password='" + password + "']" selecciona todos los elementos userinfo que coinciden con el nombre de usuario y la contraseña proporcionados.

Sin embargo, si se proporciona un nombre de usuario y una contraseña que contienen caracteres especiales o palabras clave usadas en las consultas XPath, se modifica la estructura de la consulta y un atacante puede evadir el sistema de inicio de sesión. Por ejemplo, si se proporciona "' or 1=1" como nombre de usuario y contraseña, se obtiene la siguiente consulta XPath:

//users/userinfo[username = '' or 1=1' and password = '' or 1=1' ]

Esta consulta selecciona todos los elementos userinfo con un atributo username y un atributo password vacíos o iguales a "1=1". La expresión lógica "1=1" siempre da como resultado true, independientemente de los valores reales de los atributos username y password.

Supongamos que un atacante quiere obtener acceso sin un nombre de usuario o una contraseña válidos. En lugar de credenciales válidas, podría ingresar cualquiera de las siguientes inyecciones XPath en el formulario de inicio de sesión, tanto como nombre de usuario como contraseña:

  • 'or'1'='1

  • ' or 1=1 or 'a'='a'

  • ' or ''=''

  • ' or '1'='1'

  • 'a' or true() or '

  • 'text' or '1' = '1'

Cada una de las instrucciones anteriores evade correctamente la seguridad de la aplicación al aprovechar la lógica y la estructura de las consultas XPath, ya que todas las consultas XPath generadas a partir de esas entradas siempre dan como resultado true. Además, si los atacantes conocen o pueden adivinar un nombre de usuario (por ejemplo, kimkimani), pueden ejecutar la siguiente consulta:

kimkimani' or '1'='1

Cuando ejecutamos esta consulta como username y password, la consulta resultante selecciona el elemento userinfo con el username kimkimani, cualquier elemento userinfo llamado kimkimani o '1'='1', o cualquier elemento userinfo en el que el valor del atributo username sea '1'='1' y el valor del atributo password sea '1'='1'. Como todos los elementos userinfo cumplen estas condiciones, el sistema permite que el atacante inicie sesión.

Además, según la estructura del documento XML, un atacante puede enviar una inyección basada en las posiciones de los nodos, de la siguiente manera:

'or position()=4 or'

Esta consulta permite que los atacantes evadan la autenticación según la posición del usuario en la estructura del documento XML. La función position() es una función XPath integrada que devuelve la posición del nodo actual con respecto a su elemento principal. La expresión anterior da como resultado true para el cuarto nodo del documento XML, lo que permite que el atacante inicie sesión.

Cómo prevenir las vulnerabilidades de inyección XPath

Aunque hay muchas formas de aprovechar la inseguridad de los datos XML, contamos con varias maneras de mitigar estas vulnerabilidades en nuestros sitios web y aplicaciones. Veamos algunas.

Sanitizar las entradas

Una estrategia para prevenir los ataques de inyección XPath consiste en filtrar los caracteres que pueden ingresar los usuarios. Aunque no es infalible, este método garantiza que bloqueemos las entradas que podrían formar la base de consultas de inyección.

Para implementar este enfoque, crea un archivo llamado regex.py dentro de la carpeta xpath y pega el siguiente código en el archivo.

# Import the required modules
from lxml import etree
import re

# Parse the XML data
tree = etree.parse("users_info.xml")

# Create an XPath evaluator
evaluator = etree.XPathEvaluator(tree, namespaces=None)

# login function
def login(username, password):
    if len(re.findall('[^a-zA-Z0-9]', username)) > 0 or ⏎ len(re.findall('[^a-zA-Z0-9]', password))>0:
        print("Suspected characters detected! Login failed!")
        return False
    else:
        expression = "/users/userinfo[username='" + username + "' and password='" +⏎  password + "']"
        results = tree.xpath(expression)

        # if no results, which means login details did not match
        if len(results) < 1:
            print("Incorrect login details!")

        # if login details match
        else:
            for result in results:
                print("Login successful for user", result[1].text)

login(username="kimkimani", password="6wre1678")

Este código usa una expresión regular (regex) para detectar todos los caracteres que no son numéricos ni alfabéticos. Cuando se detecta alguno de esos caracteres, aparece un mensaje de «error de inicio de sesión» en la terminal y devolvemos inmediatamente False a la función que realiza la llamada para evitar que se siga ejecutando el código. Podemos probar el código con los caracteres usados en la inyección XPath que analizamos en la sección anterior.

Aunque este enfoque mejora la seguridad de nuestros datos, la validación de entradas solo filtra los caracteres que recordamos incluir en las funciones de sanitización. Además, las contraseñas deben tener caracteres especiales, así que no queremos bloquear estas entradas.

Exploremos algunas alternativas para evitar las vulnerabilidades de inyección XPath.

Usar consultas XPath parametrizadas (preparadas)

Para demostrar este enfoque, crea el archivo params.py dentro de la carpeta xpath y pega el siguiente código:

# Import the required modules
from lxml import etree

# Parse the XML data
tree = etree.parse("users_info.xml")

def login(username, password):
    expression = "/users/userinfo[username=$username and password=$password]"

    results = tree.xpath(expression, username=username, password=password)

    # if no results, which means login details did not match
    if len(results) < 1:
        print("Invalid credentials! Check your username or password.")
        return False
    # if login details match
    else:
        for result in results:
            print(f'Login successful! username: {result[1].text}, password:⏎ {result[2].text}')

login(username="kimkimani", password="6wreD1678")

Las consultas XPath parametrizadas nos ayudan a evitar vincular la entrada del usuario con la consulta XPath, ya que la pasan como parámetro. Además de hacer que las consultas sean más seguras, también son más flexibles y reutilizables. Sin embargo, no previenen todas las inyecciones. Veamos otra medida preventiva.

Usar consultas XPath precompiladas

Las consultas XPath precompiladas no se construyen a partir de datos proporcionados por el usuario. Por lo tanto, son la única forma totalmente segura de prevenir ataques de inyección. Para ver cómo ayudan las consultas XPath precompiladas, crea un archivo llamado compiled.py y agrega este código:

import lxml.etree as et

xml = et.parse("users_info.xml")
find = et.xpath("/users/userinfo[username=$username and password=$password]")
results = find(xml, username="'or'1'='1", password="'or'1'='1")
print(results)

El código usa la función etree.xpath para compilar una consulta XPath y luego la almacena en la variable find. Podemos ejecutar esta consulta compilada varias veces mediante la función find, sin tener que volver a compilarla cada vez. Esta opción es la más eficaz, ya que no tenemos que preocuparnos por los caracteres que deberíamos haber escapado.

Prácticas recomendadas para detectar y evitar vulnerabilidades de inyección XPath

Además de usar las medidas preventivas anteriores, entre las que las consultas precompiladas son la mejor práctica, debemos aplicar las siguientes prácticas recomendadas para detectar y evitar vulnerabilidades de inyección XPath.

Revisar paquetes y código en busca de vulnerabilidades

Usa una herramienta como Snyk Open Source Advisor para analizar los paquetes de código abierto que usamos y detectar posibles vulnerabilidades de inyección XPath. Snyk Advisor ofrece una visión integral del estado de los paquetes para que tengas más confianza en la seguridad de tu aplicación web.

Del mismo modo, podemos usar Snyk Code para analizar el código fuente e identificar posibles vulnerabilidades de seguridad, incluidas las inyecciones XPath. Snyk Code puede identificar rápida y fácilmente posibles vulnerabilidades en nuestro código y ayudarnos a tomar medidas para corregirlas.

La siguiente captura de pantalla muestra los resultados del análisis del código fuente de la carpeta del proyecto con Snyk Code.

Salida de terminal de las pruebas de Snyk Code que muestra una vulnerabilidad de inyección XPath de gravedad alta en main.py

Evitar vincular las entradas del usuario con las consultas XPath

No concatenes directamente las entradas proporcionadas por el usuario en las consultas XPath. Esta es una de las formas más comunes en que se producen las vulnerabilidades de inyección XPath. En su lugar, usa consultas XPath parametrizadas con marcadores de posición para las entradas proporcionadas por el usuario. Esto puede hacer que el código sea más legible y fácil de mantener, y facilitar la sanitización de las entradas proporcionadas por el usuario y la prevención de ataques de inyección XPath.

Tener cuidado con los caracteres especiales

Si no podemos usar consultas parametrizadas, debemos escapar correctamente los caracteres especiales de las entradas proporcionadas por el usuario antes de usarlas en una consulta XPath.

Implementar instrucciones preparadas y variables de enlace

Usar instrucciones preparadas y variables de enlace en las consultas XPath puede ayudar a evitar que un atacante inyecte consultas XPath arbitrarias en una entrada de la aplicación, ya que trata la entrada como el valor de una variable y no como parte de la consulta.

Revisar el código con regularidad

Además de usar herramientas como Snyk Advisor y Snyk Code, es fundamental revisar y probar periódicamente el código para detectar posibles vulnerabilidades de inyección XPath. Esto puede ayudar a identificar y corregir posibles vulnerabilidades antes de que los atacantes las aprovechen. Podemos usar revisiones manuales de código, herramientas automatizadas de análisis de código como Snyk Code y pruebas de penetración para identificar y corregir vulnerabilidades en el código.

Conclusión

La inyección XPath puede causar graves daños a los sitios web, los datos y la reputación de las organizaciones, además de facilitar futuras brechas de seguridad. Sin embargo, con un poco de esfuerzo adicional, podemos proteger nuestras aplicaciones usando herramientas como Snyk Advisor y Snyk Code, siguiendo las prácticas recomendadas para detectar y evitar vulnerabilidades de inyección XPath en nuestro código, y aplicando parches con cuidado.

Podemos proteger nuestras aplicaciones contra los ataques de inyección XPath si sanitizamos correctamente las entradas proporcionadas por el usuario, usamos consultas XPath parametrizadas y, sobre todo, consultas XPath precompiladas, que no se construyen a partir de datos proporcionados por el usuario y son la única forma totalmente segura de prevenir ataques de inyección. Estas medidas preventivas son esenciales y no deben pasarse por alto, dadas las consecuencias de los ataques de inyección XPath.

Comienza con Capture the Flag

Aprende a resolver desafíos de Capture the Flag con nuestro taller virtual 101 a pedido.