Skip to main content

Como prevenir ataques de injeção XPath

Escrito por

Marcelo Oliveira

blog feature pypi spoof

10 de maio de 2023

0 minutos de leitura

Aplicações web estão vulneráveis a vários tipos de ataques, mas são especialmente suscetíveis a ataques de injeção de código. Um deles, a injeção XPath, explora sites que precisam de informações fornecidas pelo usuário para acessar dados armazenados em formato XML. Todos os sites que usam um banco de dados em formato XML podem estar vulneráveis a esse ataque.

XPath é uma sintaxe de consulta que sites podem usar para pesquisar seus repositórios de dados XML. Quando executadas corretamente, as consultas XPath são uma maneira legítima de localizar dados armazenados em elementos e atributos. Sites que usam bancos de dados com informações formatadas em XML integram dados inseridos pelo usuário (por exemplo, nome de usuário e senha) às consultas XPath para localizar os dados necessários.

No entanto, assim como em outros ataques de injeção (como o conhecido ataque de injeção de SQL), um agente mal-intencionado pode explorar esse processo usando informações malformadas. Um invasor pode descobrir como os dados XML estão estruturados e acessar dados que normalmente não estariam disponíveis para ele. Com essas informações, invasores podem elevar seus privilégios de acesso (um ataque de “elevação de privilégios”), comprometendo potencialmente a aplicação e dados confidenciais, além de usar as informações ou o acesso para atacar o restante da organização.

Além de excluir ou corromper dados, a exfiltração de dados pode permitir que hackers executem comandos da aplicação, abram caminho para novos ataques ao servidor e derrubem toda a organização com outros ataques. As consequências podem prejudicar ou destruir a reputação das nossas aplicações e a credibilidade da empresa perante seus clientes.

Felizmente, podemos proteger nossos sites contra ataques de injeção XPath. Neste artigo, vamos explorar vulnerabilidades em XML e aprender a evitar que elas comprometam os dados de aplicações.

Como identificar e corrigir vulnerabilidades de injeção XPath

Para entender como prevenir injeções XPath, precisamos analisar uma delas. Em seguida, vamos aprender as melhores formas de proteger nossas aplicações e nossos dados contra esse ataque.

Analisando uma aplicação de autenticação vulnerável

Vamos usar como exemplo uma aplicação que exige que os usuários façam login com nome de usuário e senha.

Crie uma pasta chamada xpath e, dentro dela, crie um arquivo chamado users_info.xml com os dados XML abaixo:

<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 da pasta 0xpath, crie um arquivo chamado main.py e cole o código a seguir nele:

# 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)

O código carrega os dados XML e, em seguida, executa uma consulta XPath para selecionar os dados XML que correspondem às credenciais fornecidas pelo usuário. Se encontrar uma correspondência, autentica o usuário. Caso contrário, a aplicação exibe a mensagem: “Credenciais inválidas! Confira seu nome de usuário ou senha.”

Antes de testar este código, instale o pacote Python lxml para processar dados XML. Execute este comando para instalá-lo:

pip install lxml

Para testar o código, chame a função login e informe seu nome de usuário e sua senha. Você pode fazer isso adicionando este trecho de código ao final do arquivo main.py:

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

Para executar o arquivo, rode o comando abaixo no terminal.

python3 main.py

Uma mensagem de sucesso é exibida. Você também pode tentar fazer login com credenciais incorretas, o que resulta em uma mensagem de falha.

Este código está vulnerável a ataques de injeção XPath porque vincula os dados de entrada do usuário à consulta XPath, modificando sua estrutura e permitindo a injeção de código malicioso.

Uma consulta XPath típica é uma string de texto que especifica os elementos que você quer selecionar nos dados XML.

No código acima, a consulta "/users/userinfo[username='" + username + "' and password='" + password + "']" seleciona todos os elementos userinfo que correspondem ao nome de usuário e à senha informados.

No entanto, informar um nome de usuário e uma senha com caracteres especiais ou palavras-chave usadas em consultas XPath modifica a estrutura da consulta e permite que um invasor burle o sistema de login. Por exemplo, informar "' or 1=1" como nome de usuário e senha resulta na seguinte consulta XPath:

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

Essa consulta seleciona todos os elementos userinfo com atributos username e password que estejam vazios ou sejam iguais a "1=1". A expressão lógica "1=1" sempre é avaliada como true, independentemente dos valores reais dos atributos username e password.

Imagine que um invasor queira obter acesso sem um nome de usuário ou senha válidos. Em vez de credenciais legítimas, ele poderia inserir qualquer uma das seguintes injeções XPath no formulário de login, tanto no campo de nome de usuário quanto no de senha:

  • 'or'1'='1

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

  • ' or ''=''

  • ' or '1'='1'

  • 'a' or true() or '

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

Cada uma das instruções acima burla com sucesso a segurança da aplicação ao explorar a lógica e a estrutura das consultas XPath, pois todas as consultas formadas a partir dessas entradas são sempre avaliadas como verdadeiras. Além disso, se os invasores souberem ou conseguirem adivinhar um nome de usuário (por exemplo, kimkimani), poderão executar a seguinte consulta:

kimkimani' or '1'='1

Quando executamos essa consulta usando username e password, a consulta resultante seleciona o elemento userinfo com o username kimkimani, qualquer elemento userinfo chamado kimkimani ou '1'='1', ou qualquer elemento userinfo em que o valor do atributo username seja '1'='1' e o valor do atributo password seja '1'='1'. Como todos os elementos userinfo atendem a essas condições, o sistema de login permite que o invasor entre.

Além disso, dependendo da estrutura do documento XML, um invasor pode enviar uma injeção baseada na posição dos nós, como a seguir:

'or position()=4 or'

Essa consulta permite que invasores burlem a autenticação com base na posição do usuário na estrutura do documento XML. A função position() é uma função XPath integrada que retorna a posição do nó atual em relação ao nó pai. A expressão acima é avaliada como true para o quarto nó do documento XML, permitindo que o invasor entre.

Como prevenir vulnerabilidades de injeção XPath

Há muitas maneiras de explorar dados XML desprotegidos, mas também existem diversas formas de reduzir essas vulnerabilidades em nossos sites e aplicações. Vamos conhecer algumas delas.

Sanitize os dados de entrada

Uma estratégia para prevenir ataques de injeção XPath é filtrar os caracteres que os usuários podem inserir. Embora não seja infalível, esse método garante o bloqueio de entradas que poderiam servir de base para consultas de injeção.

Para implementar essa abordagem, crie o arquivo regex.py dentro da pasta xpath e cole nele o código a seguir.

# 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 uma expressão regular (regex) para detectar todos os caracteres que não são numéricos ou alfabéticos. Quando algum desses caracteres é detectado, uma mensagem de “falha no login” aparece no terminal e retornamos imediatamente False à função que chamou o código, impedindo sua execução. Podemos testar o código com os caracteres usados na injeção XPath discutida na seção acima.

Embora essa abordagem aumente a segurança dos nossos dados, a validação de entradas só filtra os caracteres que lembramos de incluir nas funções de sanitização. Além disso, senhas devem conter caracteres especiais, então não queremos bloquear essas entradas.

Vamos explorar algumas alternativas para evitar vulnerabilidades de injeção XPath.

Use consultas XPath parametrizadas (preparadas)

Para demonstrar essa abordagem, crie o arquivo params.py dentro da pasta xpath e cole nele o código a seguir:

# 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")

As consultas XPath parametrizadas ajudam a evitar que os dados de entrada do usuário sejam vinculados diretamente à consulta XPath: em vez disso, eles são passados como parâmetros. Além de tornar as consultas mais seguras, a parametrização também as deixa mais flexíveis e reutilizáveis. No entanto, ela não impede todos os tipos de injeção. Então, vamos conhecer outra medida preventiva.

Use consultas XPath pré-compiladas

Consultas XPath pré-compiladas não são construídas com dados fornecidos pelo usuário. Por isso, são a única forma totalmente segura de prevenir ataques de injeção. Para entender como elas ajudam, crie um arquivo chamado compiled.py e adicione 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)

O código usa a função etree.xpath para compilar uma consulta XPath, que é armazenada na variável find. Podemos executar essa consulta compilada várias vezes usando a função find, sem precisar compilá-la novamente a cada execução. Essa é a opção mais eficaz, pois não precisamos nos preocupar com os caracteres que deveríamos escapar.

Práticas recomendadas para detectar e evitar vulnerabilidades de injeção XPath

Além de adotar as medidas preventivas acima, sendo as consultas pré-compiladas a melhor prática, devemos seguir estas recomendações para detectar e evitar vulnerabilidades de injeção XPath.

Verifique pacotes e códigos em busca de vulnerabilidades

Use uma ferramenta como o Snyk Open Source Advisor para analisar os pacotes de código aberto que usamos e detectar possíveis vulnerabilidades de injeção XPath. Com uma visão abrangente da integridade dos pacotes, o Snyk Advisor ajuda a aumentar nossa confiança na segurança da aplicação web.

Da mesma forma, podemos usar o Snyk Code para analisar o código-fonte e identificar possíveis vulnerabilidades de segurança, inclusive injeções XPath. O Snyk Code identifica possíveis vulnerabilidades no código com rapidez e facilidade e ajuda a tomar medidas para corrigi-las.

A captura de tela abaixo mostra os resultados da análise do código-fonte da pasta do projeto com o Snyk Code.

Saída do terminal de um teste do Snyk Code que mostra uma vulnerabilidade de injeção XPath de alta gravidade em main.py

Evite vincular dados de entrada do usuário a consultas XPath

Não concatene diretamente às consultas XPath os dados fornecidos pelo usuário. Essa é uma das causas mais comuns de vulnerabilidades de injeção XPath. Em vez disso, consultas XPath parametrizadas permitem usar espaços reservados para os dados inseridos pelo usuário. Isso pode deixar o código mais legível e fácil de manter, além de simplificar a sanitização desses dados e ajudar a prevenir ataques de injeção XPath.

Tenha atenção aos caracteres especiais

Se não for possível usar consultas parametrizadas, escape corretamente todos os caracteres especiais dos dados fornecidos pelo usuário antes de usá-los em uma consulta XPath.

Implemente instruções preparadas e variáveis de vinculação

O uso de instruções preparadas e variáveis de vinculação em consultas XPath pode ajudar a impedir que um invasor injete consultas XPath arbitrárias em uma aplicação, pois trata os dados de entrada como valores de variáveis, e não como parte da própria consulta.

Revise o código regularmente

Além de usar ferramentas como Snyk Advisor e Snyk Code, é essencial revisar e testar o código regularmente para encontrar possíveis vulnerabilidades de injeção XPath. Isso ajuda a identificar e corrigir vulnerabilidades antes que invasores as explorem. Podemos usar revisões manuais de código, ferramentas automatizadas de análise, como o Snyk Code, e testes de invasão para identificar e corrigir vulnerabilidades no código.

Conclusão

A injeção XPath pode causar danos graves a sites, dados e à reputação das organizações, além de abrir caminho para futuras violações. No entanto, com um pouco mais de esforço, podemos proteger nossas aplicações usando ferramentas como Snyk Advisor e Snyk Code, seguindo as práticas recomendadas para detectar e evitar vulnerabilidades de injeção XPath no código e corrigindo vulnerabilidades com cuidado.

Podemos proteger nossas aplicações contra ataques de injeção XPath sanitizando corretamente os dados fornecidos pelo usuário, usando consultas XPath parametrizadas e, principalmente, recorrendo a consultas XPath pré-compiladas. Como não são construídas com dados fornecidos pelo usuário, elas são a única forma totalmente segura de prevenir ataques de injeção. Essas medidas preventivas são essenciais e não podem ser ignoradas, considerando as consequências dos ataques de injeção XPath.

Comece a jogar Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.